在c#中使用结构体是否有内存或CPU开销?
本文关键字:内存 CPU 开销 是否 结构体 | 更新日期: 2023-09-27 18:10:40
使用struct shim来替换锯齿数组是否有任何开销?
给出一个具体的例子
vertices = new KeyValuePair<uint, EdgeData>[][];
和
private struct Vertex
{
public KeyValuePair<uint, EdgeData>[] Arcs { get; set; }
}
vertices = new KeyValuePair<uint, Vertex>[];
EdgeData是一个类
显然,在结构体示例中意图更清晰,但它需要能够保存大量图形,因此任何内存开销都是显著的
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
(值类型)中包装一个数组(引用类型)并不意味着什么:该数组仍然在堆上分配。
但是,如果您将类EdgeData
更改为结构体,则可以(但可能不会)在数组内内联分配它。因此,如果您的EdgeData
类的大小为16字节,并且您创建并填充包含100个条目的EdgeData[]
,则实际上分配了1个数组实例(具有用于容纳100个对象引用的后备存储大小)和100个EdgeData
类的单独实例。
如果EdgeData
是一个结构体,则分配一个具有后备存储大小的数组来容纳100个EdgeData
实例(在本例中为1600字节,因为我们假设的EdgeData
结构体大小为16字节)
遍历数组的类版本,特别是在数组非常大的情况下,可能会导致分页,因为当您跳过堆以访问单个EdgeData
实例时,可能会丢失引用的局部性。
对struct
版本数组的迭代保留了引用的局部性,因为EdgeData
实例是内联的。
结构数组往往是相当有效的,尽管在您的特定示例中,每一行都有一个额外的单位。同样,避免暴露结构类型的属性,如果一个结构表示一组用胶带绑定在一起的独立值(例如,一个点的坐标),只需将这些项暴露为字段。虽然在许多情况下JIT可以将属性访问转换为字段访问,但也有许多情况下它不能这样做。
如果要比较的效率是:
struct FloatPoint2D {public float X,Y;}
FloatPoint3D[] MyArray;
和
float[] MyXCoords, MyYCoords;
使用上面定义的结构体访问随机序列项的X和Y将比使用一对单独的数组(通常是一个缓存丢失而不是两个)更快,但是如果使用单独的数组访问序列中许多项的X或Y坐标将更快(每条缓存行将获取两倍的有用的坐标)。
在您的特定示例中,您的类型需要封装的确切数据并不清楚;您的结构体和非结构体示例保存不同的数据,因此很难说哪个"更有效"。
用结构体的1D数组替换2D数组不会引起任何问题。这实际上是你如何看待数据的问题。如果将它建模为结构数组更有意义,每个结构数组包含一个弧数组,那么这就是你应该在代码中表达它的方式。
在存储方式上有一些小的不同。特别是,您的1D阵列方法将比2D阵列方法占用更多的内存。基本上,每一行都有一个额外的uint
。
了。它讨论了struct方法和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)))
字节。如果单个数组被限制为2gb(如在。net 4.0及更早版本中,或在。net 4.5中,除非启用非常大的对象),那么使用结构体的1D数组可能会比使用2D数组拥有更多的项。当然,假设您有足够的内存来容纳这么多KeyValuePair<uint, EdgeData>
实例。
所以你的整体内存使用将会增加,但是你的最大单个分配将会小得多。