使用struct shim替换锯齿状数组是否有任何开销?
举一个具体的例子
vertices = new KeyValuePair<uint, EdgeData>[][];
VS
private struct Vertex
{
public KeyValuePair<uint, EdgeData>[] Arcs { get; set; }
}
vertices = new KeyValuePair<uint, Vertex>[];
如果EdgeData有任何不同,则它是一个类 显然,结构示例中的意图更清晰,但它需要能够保存大量图形,因此任何内存开销都很重要
答案 0 :(得分:4)
可以在堆栈上分配或不分配struct
。引用类型可以永远在堆栈上分配;它们总是在堆上分配。
来自标准(ISO 23270),§8.8:
8.8结构 类和结构之间的相似性列表是长结构可以实现的 接口,可以与类具有相同类型的成员。结构不同于 但是,几个重要方面的类:结构是值类型而不是 结构类型和结构不支持继承。结构值 存储在“堆栈”或“在线”。细心的程序员有时可以提升 通过明智地使用结构来表现。
例如,对Point使用结构而不是类可以做大 在运行时执行的内存分配数量的差异。该程序 下面创建并初始化一个包含100个点的数组。
将
Point
作为一个类实现,实例化101个独立的对象 - 一个用于 数组和100个元素各一个。class Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class Test { static void Main() { Point[] points = new Point[100]; for (int i = 0; i < 100; i++) { points[i] = new Point(i, i*i); } }
如果将
Point
实现为结构,如struct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } }
只实例化一个对象 - 数组的对象。 Point实例是 在数组中内联分配。这种优化可能被滥用。使用结构 而不是类也可以使应用程序运行更慢或占用更多的内存, 因为按值传递struct实例会导致创建该结构的副本。
所以答案是“可能”。
对于您的示例,在struct
(值类型)中包装数组(引用类型)并不意味着什么:该数组仍在堆上分配。
但是,如果将类EdgeData
更改为结构,则可以(但可能不是)在数组中内联分配。因此,如果您的EdgeData
类的大小为16个字节,并且您创建并填充了100个条目的EdgeData[]
,则实际上是在分配1个数组实例(后备存储大小为100对象引用,以及EdgeData
类的100个单独实例。
如果EdgeData
是结构,则分配1个数组,其后备存储大小可容纳100 EdgeData
个实例(在本例中为1600字节,因为我们的假设EdgeData
结构为16字节大小。)
迭代数组的类版本,特别是如果数组非常大,可能会导致分页,因为当你跳过整个堆来命中单个EdgeData
实例时,你可能会失去引用的局部性。
对struct
版本的数组进行迭代会保留引用的位置,因为EdgeData
实例是内联的。
答案 1 :(得分:2)
使用1D结构数组替换2D数组不会导致任何问题。这真的是你如何查看数据的问题。如果将它建模为一个结构数组更有意义,每个结构都包含一个弧数组,那么就应该在代码中表达它。
存储它们的方式存在一些细微差别。特别是,与2D阵列方法相比,您的一维阵列方法将占用更多内存。基本上,每行都有一个额外的uint
。
袭来之后。它正在讨论结构方法和2D数组(即[,]
)之间的区别,而不是OP正在使用的锯齿状数组([][]
)。
实际上,使用的总内存不止于此。在2D数组方法中,数组中有 (row * col) KeyValuePair
个结构。该阵列在64位运行时具有大约50个字节的分配开销(如果我记得,在32位运行时中大约40个字节)。在1D数组方法中,您仍然具有(row * col) KeyValuePair
结构,但每个结构都包含一个具有相同50字节分配开销的数组。此外,您拥有vertices
数组,其中包含(row) KeyvaluePair
个结构。
但是,您的2D数组(只是数组)需要(rows * cols * (4 + sizeof(IntPtr)))
个字节。 1D vertices
数组只需要(rows * (4 + sizeof(IntPtr)))
个字节。如果您对单个阵列的限制为2千兆字节(因为您在.NET 4.0及更早版本中,或者在.NET 4.5中,除非您启用了非常大的对象),那么使用1D阵列可能会有更多项目,总计结构比2D数组。当然,假设您有足够的内存来容纳那么多KeyValuePair<uint, EdgeData>
个实例。
因此,您的整体内存使用量将会增加,但您最大的单个分配将会小得多。
答案 2 :(得分:2)
结构数组往往效率相当高,但在您的特定示例中,每行都有一个额外的uint。此外,避免暴露结构类型的属性,如果结构表示与管道胶带绑定在一起的独立值的集合(例如,点的坐标),则只需将这些项作为字段公开。虽然在很多情况下JIT会将属性访问转换为字段访问,但也有很多情况下它不能。
如果要比较效率:
struct FloatPoint2D {public float X,Y;}
FloatPoint3D[] MyArray;
与
float[] MyXCoords, MyYCoords;
使用上面定义的结构以随机顺序访问项目的X和Y将比使用一对单独的数组(通常是一个缓存未命中而不是两个)更快但仅访问X或仅访问许多的Y坐标如果使用单独的数组,则序列中的项将更快(每个缓存行将获取两倍的有用的坐标)。
在您的特定示例中,不清楚您的类型需要封装哪些数据;你的struct和non-struct示例包含不同的数据,所以很难说一个“更高效”。