c# - 为什么 64 位比 32 位更快?

标签 c# performance

我一直在做一些性能测试,主要是为了了解迭代器和简单 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;
}

我有一个简单的测试工具来测试这个

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);
}

当我运行这些时,我得到以下结果:
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 循环一样高效,而且代码更加清晰易读!

最佳答案

x64 处理器包含 64 位通用寄存器,它们可以使用这些寄存器在单个指令中计算 64 位整数上的操作。 32 位处理器没有。这与您的程序特别相关,因为它大量使用 long (64 位整数)变量。

例如,在 x64 汇编中,要添加几个存储在寄存器中的 64 位整数,您可以简单地执行以下操作:

; adds rbx to rax
add rax, rbx

要在 32 位 x86 处理器上执行相同的操作,您必须使用两个寄存器并在第二个操作中手动使用第一个操作的进位:

; adds ecx:ebx to edx:eax
add eax, ebx
adc edx, ecx

更多的指令和更少的寄存器意味着更多的时钟周期、内存获取……最终会导致性能下降。这种差异在数字运算应用程序中非常显着。

对于 .NET 应用程序,64 位 JIT 编译器似乎执行了更积极的优化以提高整体性能。

关于您关于数组迭代的观点,C# 编译器足够聪明,可以识别 foreach在数组上并特殊对待它们。生成的代码与使用 for 相同循环,建议您使用 foreach如果您不需要更改循环中的数组元素。除此之外,运行时识别模式 for (int i = 0; i < a.Length; ++i)并省略循环内对数组访问的绑定(bind)检查。这不会发生在 LongLength 中。大小写并会导致性能下降(对于 32 位和 64 位大小写);因为你将使用 long变量 LongLength , 32 位性能会进一步下降。

关于c# - 为什么 64 位比 32 位更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1941826/

相关文章:

c# - MS Dynamics - QueryExpression with ConditionOperator.In 结果没有结果但与 ConditionOperator.Equal 一起使用

c# - "Go to declaration"自定义 MVC 帮助器扩展 Controller 和操作的功能

C# Lambda 返回语句问题

mysql - 提高仅增长表(不删除)的记录计数性能

c# - 将字符串 [123,234,...] 转换为 List<int> 并返回的更好方法

performance - JedisPool 持有的默认连接

c# - 在 C# 中使用 C++ 会有任何性能问题吗?

.net - 在客户站点上比较.NET性能与VB 6性能的最佳方法是什么?

C++ - 我读取了整个文件(_这是一个由 2 个空格分隔的单词列表_),如何快速分别获取单词?

C# sql INSERT INTO 抛出异常 System.Data.SqlClient.SqlException