我一直在做一些性能测试,主要是因为我可以理解迭代器和简单for循环之间的区别.作为其中的一部分,我创建了一组简单的测试,然后对结果感到惊讶.对于某些方法,64位比32位快近10倍.
我正在寻找的是为什么会发生这种情况的一些解释.
[下面的答案说明这是由于32位应用程序中的64位算术.将long更改为int会在32位和64位系统上产生良好的性能.
以下是有问题的3种方法.
private static long ForSumArray(long[] array)
{
var result = 0L;
for (var i = 0L; i < array.LongLength; i++)
{
result += array[i];
}
return result;
}
private static long ForSumArray2(long[] array)
{
var length = array.LongLength;
var result = 0L;
for (var i = 0L; i < length; i++)
{
result += array[i];
}
return result;
}
private static long IterSumArray(long[] array)
{
var result = 0L;
foreach (var entry in array)
{
result += entry;
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
我有一个简单的测试工具来测试这个
var repeat = 10000;
var arrayLength = 100000;
var array = new long[arrayLength];
for (var i = 0; i < arrayLength; i++)
{
array[i] = i;
}
Console.WriteLine("For: {0}", AverageRunTime(repeat, () => ForSumArray(array)));
repeat = 100000;
Console.WriteLine("For2: {0}", AverageRunTime(repeat, () => ForSumArray2(array)));
Console.WriteLine("Iter: {0}", AverageRunTime(repeat, () => IterSumArray(array)));
private static TimeSpan AverageRunTime(int count, Action method)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
for (var i = 0; i < count; i++)
{
method();
}
stopwatch.Stop();
var average = stopwatch.Elapsed.Ticks / count;
return new TimeSpan(average);
}
Run Code Online (Sandbox Code Playgroud)
当我运行这些时,我得到以下结果:
32位:
For: 00:00:00.0006080 For2: 00:00:00.0005694 Iter: 00:00:00.0001717
64位
For: 00:00:00.0007421 For2: 00:00:00.0000814 Iter: 00:00:00.0000818
我从中读到的东西是使用LongLength很慢.如果我使用array.Length,第一个for循环的性能在64位中相当不错,但不是32位.
我从中读到的另一件事是迭代数组和for循环一样高效,代码更清晰,更容易阅读!
Meh*_*ari 51
x64处理器包含64位通用寄存器,通过它们可以在单个指令中计算64位整数的运算.32位处理器没有.这与您的程序特别相关,因为它大量使用long(64位整数)变量.
例如,在x64汇编中,要添加存储在寄存器中的几个64位整数,您可以简单地执行:
; adds rbx to rax
add rax, rbx
Run Code Online (Sandbox Code Playgroud)
要在32位x86处理器上执行相同的操作,您必须使用两个寄存器并在第二个操作中手动使用第一个操作的进位:
; adds ecx:ebx to edx:eax
add eax, ebx
adc edx, ecx
Run Code Online (Sandbox Code Playgroud)
更多的指令和更少的寄存器意味着更多的时钟周期,内存提取,......这最终会导致性能降低.数字运算应用程序中的差异非常显着.
对于.NET应用程序,似乎64位JIT编译器执行更积极的优化,从而提高整体性能.
关于你关于数组迭代的观点,C#编译器足够聪明,可以识别foreach数组并对它们进行特殊处理.生成的代码与使用for循环相同,foreach如果您不需要更改循环中的数组元素,建议您使用它.除此之外,运行时识别模式for (int i = 0; i < a.Length; ++i)并省略循环内数组访问的绑定检查.在这种LongLength情况下不会发生这种情况,并且会导致性能下降(对于32位和64位情况); 并且由于你将使用long变量LongLength,32位性能将进一步降低.