gpt4 book ai didi

c# - 迭代 T[] 转换为 IList 的开销

转载 作者:可可西里 更新时间:2023-11-01 08:05:34 24 4
gpt4 key购买 nike

我注意到对已转换为通用接口(interface)集合(IList 或 IEnumberable)的原始集合 (T[]) 进行迭代会影响性能。

例如:

    private static int Sum(int[] array)
{
int sum = 0;

foreach (int i in array)
sum += i;

return sum;
}

上面的代码执行速度明显快于下面的代码,其中参数更改为类型 IList(或 IEnumerable):

    private static int Sum(IList<int> array)
{
int sum = 0;

foreach (int i in array)
sum += i;

return sum;
}

如果传递的对象是原始数组,并且我尝试将循环更改为 for 循环而不是 foreach 循环,性能仍然会受到影响。

我可以通过这样编码来解决性能问题:

    private static int Sum(IList<int> array)
{
int sum = 0;

if( array is int[] )
foreach (int i in (int[])array)
sum += i;
else
foreach (int i in array)
sum += i;

return sum;
}

有没有更优雅的方法来解决这个问题?感谢您的宝贵时间。

编辑:我的基准代码:

    static void Main(string[] args)
{
int[] values = Enumerable.Range(0, 10000000).ToArray<int>();
Stopwatch sw = new Stopwatch();

sw.Start();
Sum(values);
//Sum((IList<int>)values);
sw.Stop();

Console.WriteLine("Elasped: {0} ms", sw.ElapsedMilliseconds);
Console.Read();
}

最佳答案

最好的办法是为 Sum 创建过载与 int[]如果此方法对性能至关重要,则作为参数。 CLR 的 JIT 可以检测数组上的 foreach 式迭代,并且可以跳过范围检查并直接寻址每个元素。循环的每次迭代在 x86 上需要 3-5 条指令,只有一次内存查找。

当使用 IList 时,JIT 不知道底层集合的结构并最终使用 IEnumerator<int> .循环的每次迭代使用两次接口(interface)调用 - 一次用于 Current , 一个用于 MoveNext (2-3 次内存查找和每次调用)。这很可能以大约 20 条非常昂贵的指令结束,您对此无能为力。

编辑:如果您对 JIT 从发布版本没有附加调试器发出的实际机器代码感到好奇,就在这里。

数组版本

            int s = 0;
00000000 push ebp
00000001 mov ebp,esp
00000003 push edi
00000004 push esi
00000005 xor esi,esi
foreach (int i in arg)
00000007 xor edx,edx
00000009 mov edi,dword ptr [ecx+4]
0000000c test edi,edi
0000000e jle 0000001B
00000010 mov eax,dword ptr [ecx+edx*4+8]
s += i;
00000014 add esi,eax
00000016 inc edx
foreach (int i in arg)
00000017 cmp edi,edx
00000019 jg 00000010

IEnumerable 版本

            int s = 0;
00000000 push ebp
00000001 mov ebp,esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp,1Ch
00000009 mov esi,ecx
0000000b lea edi,[ebp-28h]
0000000e mov ecx,6
00000013 xor eax,eax
00000015 rep stos dword ptr es:[edi]
00000017 mov ecx,esi
00000019 xor eax,eax
0000001b mov dword ptr [ebp-18h],eax
0000001e xor edx,edx
00000020 mov dword ptr [ebp-24h],edx
foreach (int i in arg)
00000023 call dword ptr ds:[009E0010h]
00000029 mov dword ptr [ebp-28h],eax
0000002c mov ecx,dword ptr [ebp-28h]
0000002f call dword ptr ds:[009E0090h]
00000035 test eax,eax
00000037 je 00000052
00000039 mov ecx,dword ptr [ebp-28h]
0000003c call dword ptr ds:[009E0110h]
s += i;
00000042 add dword ptr [ebp-24h],eax
foreach (int i in arg)
00000045 mov ecx,dword ptr [ebp-28h]
00000048 call dword ptr ds:[009E0090h]
0000004e test eax,eax
00000050 jne 00000039
00000052 mov dword ptr [ebp-1Ch],0
00000059 mov dword ptr [ebp-18h],0FCh
00000060 push 0F403BCh
00000065 jmp 00000067
00000067 cmp dword ptr [ebp-28h],0
0000006b je 00000076
0000006d mov ecx,dword ptr [ebp-28h]
00000070 call dword ptr ds:[009E0190h]

关于c# - 迭代 T[] 转换为 IList<T> 的开销,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8280377/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com