C#结构分配效率
在C#中,我有一个结构数组,需要为每个结构赋值。最有效的方法是什么?我可以分配每个字段,为每个字段的数组编制索引:C#结构分配效率,c#,struct,C#,Struct,在C#中,我有一个结构数组,需要为每个结构赋值。最有效的方法是什么?我可以分配每个字段,为每个字段的数组编制索引: array[i].x = 1; array[i].y = 1; 我可以在堆栈上构造一个新结构并将其复制到数组: array[i] = new Vector2(1, 2); 还有别的办法吗?我可以调用一个方法并通过ref传递结构,但我猜方法调用开销不值得 如果结构大小很重要,所讨论的结构有2-4个float或byte类型的字段 在某些情况下,我需要将相同的值分配给多个数组项,例如
array[i].x = 1;
array[i].y = 1;
我可以在堆栈上构造一个新结构并将其复制到数组:
array[i] = new Vector2(1, 2);
还有别的办法吗?我可以调用一个方法并通过ref传递结构,但我猜方法调用开销不值得
如果结构大小很重要,所讨论的结构有2-4个float或byte类型的字段
在某些情况下,我需要将相同的值分配给多个数组项,例如:
Vector2 value = new Vector2(1, 2);
array[i] = value;
array[i + 1] = value;
array[i + 2] = value;
array[i + 3] = value;
这是否改变了哪种方法更有效
我知道这是相当低的水平,但我做了数百万次,我很好奇
编辑:我创建了一个基准:
this.array = new Vector2[100];
Vector2[] array = this.array;
for (int i = 0; i < 1000; i++){
long startTime, endTime;
startTime = DateTime.Now.Ticks;
for (int x = 0; x < 100000000; x++) {
array[0] = new Vector2(1,2);
array[1] = new Vector2(3,4);
array[2] = new Vector2(5,6);
array[3] = new Vector2(7,8);
array[4] = new Vector2(9,0);
array[5] = new Vector2(1,2);
array[6] = new Vector2(3,4);
array[7] = new Vector2(5,6);
array[8] = new Vector2(7,8);
array[9] = new Vector2(9,0);
}
endTime = DateTime.Now.Ticks;
double ns = ((double)(endTime - startTime)) / ((double)loopCount);
Debug.Log(ns.ToString("F"));
}
this.array=newvector2[100];
Vector2[]数组=this.array;
对于(int i=0;i<1000;i++){
漫长的开始时间,漫长的结束时间;
startTime=DateTime.Now.Ticks;
对于(int x=0;x<100000000;x++){
数组[0]=新向量2(1,2);
数组[1]=新向量2(3,4);
数组[2]=新向量2(5,6);
数组[3]=新向量2(7,8);
数组[4]=新向量2(9,0);
数组[5]=新向量2(1,2);
数组[6]=新向量2(3,4);
数组[7]=新向量2(5,6);
数组[8]=新向量2(7,8);
数组[9]=新向量2(9,0);
}
endTime=DateTime.Now.Ticks;
double ns=((double)(endTime-startTime))/((double)loopCount);
Debug.Log(ns.ToString(“F”));
}
这报告了约0.77ns,另一个对结构字段进行索引和赋值的版本给出了约0.24ns,FWIW。与结构堆栈分配和复制相比,数组索引似乎便宜。在移动设备上查看性能可能会很有趣
Edit2:Dan Bryant下面的答案是为什么我没有开始编写基准测试,太容易出错。我对第一种情况(字段分配与构造函数调用)很好奇,所以我制作了一个发布版本并附加了post JIT以查看反汇编。(x64)代码如下所示:
var array = new Vector2[10];
00000000 mov ecx,191372h
00000005 mov edx,0Ah
0000000a call FFF421C4
0000000f mov edx,eax
array[i].x = 1;
00000011 cmp dword ptr [edx+4],0
00000015 jbe 0000003E
00000017 lea eax,[edx+8]
0000001a fld1
0000001c fstp qword ptr [eax]
array[i].y = 1;
0000001e fld1
00000020 fstp qword ptr [edx+10h]
array[i] = new Vector2(1, 1);
00000023 add edx,8
00000026 mov eax,edx
00000028 fld1
0000002a fld1
0000002c fxch st(1)
0000002e fstp qword ptr [eax]
00000030 fstp qword ptr [eax+8]
值得注意的是,在调试器外部使用发布版本时,“构造函数调用”是内联的,因此,原则上,设置字段和调用构造函数之间应该没有区别。也就是说,抖动在这里做了一些有趣的事情
对于“constructor”版本,它使用了两个浮点堆栈插槽,并将它们同时存储到结构内存(fld1、fld1、fstp、fstp)中。它还有一个fxch(交换),这有点傻,因为两个插槽都包含常量值1,但我认为对于大多数应用程序来说,这并不是一个高优先级的优化目标
对于“单个字段”版本,通过拆分写入(fld1、fstp、fld1、fstp),它只使用FPU堆栈上的一个插槽。我不是x64大师,所以我不知道哪种排序在执行时间方面更有效。不过,任何差异都可能很小,因为主要潜在开销(构造函数方法调用)是内联的。时间效率?为什么不编写一个测试程序并对其进行分析?@O.R.Mapper,因为有无数种方法可以破坏一个微基准。@JonB我想它可以进行基准测试,尽管我个人不熟悉用C#编写微基准测试。这不仅仅是关于优化,出于学术原因,我对效率感到好奇。这里的一个相关问题是,首先使价值类型可变的影响;这在开发风险方面是有代价的,因为价值类型的微妙性和各种可能错误地改变价值副本的方式,而不是你打算改变的价值。在许多情况下,这种工程(时间和金钱)成本可能超过与小的性能损失相关的同等成本。@DanBryant,这是一个很好的观点。我同意immutable几乎总是更好,在这种情况下,唯一的选择是结构构造函数。在我的特殊情况下,结构来自我无法控制的第三方库。@NateS,请注意,这种行为可能并不代表所有构造函数与字段赋值的情况。优化抖动可以对代码做各种各样的事情,试图从CPU管道中获得各种模糊的好处。不能保证内联,可能会发生各种操作的重新排序。特别是,如果读写的顺序不同于用C#写的顺序,那么只要抖动看到它不应该对逻辑造成影响,就可以得到这样的情况。