c# - 数组在.NET4.0中共享内存-反射或StructLayout可以实现吗?

标签 c# arrays reflection .net-4.0 structlayout

我快速创建了巨大的瞬态数组。有些保留,有些是GC-d。这会对堆进行碎片整理,而应用程序消耗大约。比实际需要的OutOfMemoryException多2.5倍的内存。

作为解决方案,我希望拥有一个巨大的数组(PointF [])并由我自己进行段的分配和管理。但是我不知道如何使两个(或更多)阵列共享相同的内存空间。

PointF[] giganticList = new PointF[100];
PointF[] segment = ???; 
// I want the segment length to be 20 and starting e.g at position 50 
// within the gigantic list


我在想一个像this SO question获胜者答案的技巧。
那有可能吗?问题在于段数组的长度和数量仅在运行时才知道。

最佳答案

假设您确定可以避免OutOfMemoryException,并且将其全部存储在内存中的方法并不是实际的问题(如果有可用内存,GC可以很好地阻止这种情况的发生)...


这是您的第一个问题。我不确定CLR supports any single object larger than 2 GB


Crucial Edit-gcAllowVeryLargeObjects在64位系统上进行了更改-在推出自己的解决方案之前尝试一下。

其次,您谈论的是“保留了一些GC”。也就是说,一旦完成“子数组”,您希望能够重新分配数组的元素。
第三,我假设您问题中的PointF[] giganticList = new PointF[100];应该更像PointF[] giganticList = new PointF[1000000];


还可以考虑使用MemoryFailPoint,因为这使您可以“需求”内存并检查异常,而不会因OutOfMemoryException崩溃。

编辑也许最重要的是,您现在正在进入一个权衡之地。如果执行此操作,则可能会在循环开始时就失去诸如jitter optimising for循环by doing数组bound checks之类的优点(for (int i= 0; i < myArray.Length; i++) 得到优化,而int length = 5; for (int i= 0; i < length; i++)没有)。如果您具有较高的计算资源代码,则可能会伤害您。您还必须更加努力地处理彼此并行的不同子数组。创建子数组,子数组的一部分甚至其中的项目的副本仍将分配更多的内存,这些内存将由GC分配。

这可以通过包装数组并跟踪哪些节用于哪个子数组来实现。实质上,您是在谈论分配大量内存,然后重用其中的一部分而不会给GC造成麻烦。您可以利用ArraySegment<T>的优势,但这会带来一些潜在的问题,例如将原始数组暴露给所有调用者。

这不会很简单,但是有可能。可能并非每次都删除一个子数组时,您都希望通过移动其他子数组以缩小间隙来对主数组进行碎片整理(或在连续段用完时执行此操作)。

一个简单的示例看起来像下面的(未经测试,如果您的计算机离开家并且猫被炸毁,请不要怪我)伪代码。还有另外两种方法,我最后会提到。

public class ArrayCollection {
  List<int> startIndexes = new List<int>();
  List<int> lengths = new List<int>();
  const int 1beeellion = 100;
  PointF[] giganticList = new PointF[1beeellion];    
  public ArraySegment<PointF> this[int childIndex] {
    get {
    // Care with this method, ArraySegment exposes the original array, which callers could then
    //  do bad things to
    return new ArraySegment<String>(giganticList, startIndexes[childIndex], length[childIndex]);
  }}

  // returns the index of the child array
  public int AddChild(int length) {

    // TODO: needs to take account of lists with no entries yet
    int startIndex = startIndexes.Last() + lengths.Last(); 

    // TODO: check that startIndex + length is not more than giganticIndex
    //   If it is then 
    //     find the smallest unused block which is larger than the length requested
    //     or defrag our unused array sections
    //   otherwise throw out of memory

    startIndexes.Add(startIndex); // will need inserts for defrag operations
    lengths.Add(length); // will need inserts for defrag operations
    return startIndexes.Count - 1; // inserts will need to return inserted index
  }      
  public ArraySegment<PointF> GetChildAsSegment(int childIndex) {
    // Care with this method, ArraySegment exposes the original array, which callers could then
    //  do bad things to
    return new ArraySegment<String>(giganticList, startIndexes[childIndex], length[childIndex]);
  }
  public void SetChildValue(int childIndex, int elementIndex, PointF value) {
    // TODO: needs to take account of lists with no entries yet, or invalid childIndex
    // TODO: check and PREVENT buffer overflow (see warning) here and in other methods
    //    e.g. 
    if (elementIndex >= lengths[childIndex]) throw new YouAreAnEvilCallerException();
    int falseZeroIndex = startIndexes[childIndex];
    giganticList[falseZeroIndex + elementIndex];
  }
  public PointF GetChildValue(int childIndex, int elementIndex) {
    // TODO: needs to take account of lists with no entries yet, bad child index, element index
    int falseZeroIndex = startIndexes[childIndex];
    return giganticList[falseZeroIndex + elementIndex];
  }
  public void RemoveChildArray(int childIndex) {
    startIndexes.RemoveAt(childIndex);
    lengths.RemoveAt(childIndex);

    // TODO: possibly record the unused segment in another pair of start, length lists
    // to allow for defraging in AddChildArray
  }
}


警告例如,如果您没有在诸如childIndex之类的方法中针对子数组针对length检查针对SetChildValue的请求的unsafe,上述代码将有效地引入buffer overflow漏洞。在生产中尝试执行此操作之前必须先understand this and prevent it,尤其是将这些方法与public PointF this[int index]结合使用时。

现在,它可以扩展为返回用于子数组的伪索引throw OutOfMemory方法,用于子数组的枚举器等,但是正如我所说的,这变得越来越复杂,您需要确定它是否真的可以解决您的问题。您的大部分时间都将花费在重用(第一)碎片整理(第二)扩展(第三)unsafe(最后)逻辑上。

这种方法的优势还在于,如果我对2GB对象限制的评论是正确的,则可以分配许多2GB子数组并将其用作单个数组。

这假定您不想沿gcAllowVeryLargeObjects路线使用指针,但是效果是一样的,您只需要创建一个包装器类即可管理固定内存块中的子数组。

另一种方法是使用哈希集/字典方法。分配整个(大容量2GB阵列)并将其分成大块(例如100个阵列元素)。然后,子数组将分配有多个块,最后一个块中会浪费一些空间。这将对整体上浪费的空间产生影响(取决于您的平均“子长度与块长度”的预测),但优点是可以增加和减小子数组的大小,并以较小的影响删除和插入子数组在你的碎片上。



值得注意的参考:


64位.NET 4中的大型阵列:MemoryFailPoint
OutOfMemoryException-允许您“需求”内存并检查异常,而不是事实发生后不使用unsafe崩溃
Large Arrays, and LOH Fragmentation. What is the accepted convention?
32位上3GB的进程限制,请参见:3_GB_barrierServer Fault /3GB considerationsAWE/PAE
buffer overflow vulnerabilitywhy you can get this in C#


将数组作为不同类型的数组或结构访问的其他示例。这些的实现可能有助于您开发自己的解决方案


BitArray Class
BitVector32 Structure
NGenerics-在该库的某些成员中巧妙地使用了类似数组的概念,尤其是诸如ObjectMatrixBag之类的常规结构
C# array of objects, very large, looking for a better way


阵列优化


Eric Gu - Efficiency of iteration over arrays?-请注意它的年代,但是即使在.NET 4.0中,如何查找JIT优化的方法仍然很重要(例如,请参见Array Bounds Check Elimination in the CLR?
Dave Detlefs - Array Bounds Check Elimination in the CLR
Warning - pdf: Implicit Array Bounds Checking on 64-bit Architectures
LinkedList-允许您按顺序引用多个不同的阵列存储桶(将块存储桶方法中的块捆绑在一起)


并行数组和UnsafeSingle的使用


Parallel Matrix Multiplication With the Task Parallel Library (TPL),尤其是-由单个数组表示的正方形或锯齿状数组与您要解决的问题类别相同。
buffer overflow vulnerabilitywhy you can get this in C#(是的,我已经三遍提到了,这一点很重要)

关于c# - 数组在.NET4.0中共享内存-反射或StructLayout可以实现吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17104480/

相关文章:

c# - 如何在 C#.Net 中创建原型(prototype)方法(如 JavaScript)?

基于多个参数的Java数组排序

java - 为什么不换呢?

c# - 从类型的部分名称获取 System.Type

java - 在具有运行时保留的对象上找不到注释

c# - 是否可以在单个 LINQ 查询中完成所有这些操作?

c# - 创建对现有对象的引用时是否调用处置?

c# - 属性更改后何时调用方法

java - 如何从文本中扫描数组中的特定数字?

调用 super init 的 python 装饰器