整数运算性能 32 与 64 位

Posted

技术标签:

【中文标题】整数运算性能 32 与 64 位【英文标题】:Integer arithmetic performance 32 vs. 64-bit 【发布时间】:2012-01-30 21:28:42 【问题描述】:

我有 2 台机器在 vs 2010 上运行 F# 2.0 Interactive build 4.0.30319.1。我的几个程序在速度更快的机器上运行速度明显变慢。问题似乎在于 32 位 Windows 上的整数运算性能明显低于 64 位 Windows。

在稍慢的 Windows 7 64 位机器上(程序如下所列):

primeCount = 1270607 真实:00:00:07.553,CPU:00:00:07.519,GC gen0:0,gen1:0,gen2:0

在稍快的 Windows XP SP2 机器上:

primeCount = 1270607 真实:00:00:32.800,CPU:00:00:32.796,GC gen0:0,gen1:0,gen2:0

所以 32 位版本的耗时是 64 位版本的 4 倍多。我假设除了支持的字长之外,由于不同的操作系统,没有显着差异。

程序:

let isPrime(n) =
    if n < 2 then false
    elif (n % 2) = 0 then                   // take care of even case
        if n = 2 then true
        else false
    else                                    // n is odd
        let ms = int(sqrt(float(n)))
        let rec isPrimeUtil(m) =
            if m > ms then true
            elif n % m = 0 then false
            else isPrimeUtil(m + 2)
        isPrimeUtil(3)

let nums = [1 .. 20000000]
let pcountref = ref 0                       // # of primes found

let primeCount =
    pcountref := 0
    for x in nums do
        if (isPrime x) then incr pcountref

do primeCount
printfn "primeCount = %d" !pcountref

将程序发送到交互式。 #time;; 然后要测量经过的处理时间,而不是范围 nums 的生成,请选择该行

let pcountref = ref 0

以及所有后续行并发送到交互式。

【问题讨论】:

BigInteger 是从哪里来的?您显示的代码肯定不会使用它... 哎呀!你是对的。问题似乎是 64 与 32 位算术。 @DougT - int 在 32 位和 64 位上是 32 位 FSI 是 32 位的,因此您在两台机器上都运行 32 位版本。在 64 位机器上检查 FSI 中的 System.IntPtr.Size*8。我很确定答案是 32 > System.IntPtr.Size*8;;验证它:int = 32 【参考方案1】:

我认为更可能的解释是 64 位 JIT 执行尾调用优化,而 32 位 JIT 没有。 isPrimeUtil函数可以优化

请注意,给定的示例无论如何都没有使用 BigInteger,还有算法改进的空间 - 筛子会运行得更快

【讨论】:

32 位 JIT 根据blogs.msdn.com/b/davbr/archive/2007/06/20/… 执行尾调用优化 - 可能不满足其中一个条件? @ssg - F# 编译器本身将优化对isPrimeUtil 的递归调用(基本上将其变成一个循环)。但是,它不会为从isPrime 调用isPrimeUtil 生成.tail 前缀,因为即使这是尾调用,静态已知此调用只会添加单个堆栈帧。我相信在这些情况下,64 位 JIT 可能会将其视为尾调用,而 32 位 JIT 不会。但是,我不确定这一次调用是否会导致性能差异。 我将 isPrimeUtil 转换为 while 循环,删除了递归。 64 位 Windows 上的时间为 8.3 秒,而 32 位 Windows 上的时间为 36.7 秒,因此比率保持大致相同。所以我仍然不清楚为什么会有这么大的差异。【参考方案2】:

float 是 64 位的,因此 sqrt(float(n)) 调用可能是您的性能下降器。 (并且会解释为什么 64 位机器处理得更好。)

如果您不需要精度,请尝试 float32。

见:http://msdn.microsoft.com/en-us/library/dd233210.aspx

我没有要测试的 32 位机器,但是在我的 64 位机器上测试 sqrt 代码需要相当长的时间。

let nums = [1 .. 20000000]

let ans = List.map (fun n -> int(sqrt(float(n))) nums

提供 5.120 秒的实时时间 - 这是您执行时间的重要组成部分。

【讨论】:

本机浮点数在 64 位和 32 位机器上都是 64 位 + sqrt(float(n)) 甚至不在内部循环中,所以这几乎肯定是不正确的 它们在两台机器上都是 64 位的事实正是我的观点(即 64 位机器可以比 32 位机器更快地在 64 位浮点数上执行数学运算)。 @John : int(sqrt(float(n)))n 中的isPrime(n) 中被调用一次——这并非无关紧要。 将 float 更改为 float32 不会更改运行时间。 @Massif - 来源? - 浮点寄存器实际上在两台机器上都是 80 位,所有浮点计算都以 80 位精度完成,并且在存储在主存储器中时仅截断为 64 位。除非您使用 SSE 指令(其中一些在 32 位 CPU 上不可用),否则没有理由为什么 64 位应该更快,我怀疑这种情况正在发生。【参考方案3】:

这些结果是有道理的。 BigNum 实现通常使用机器整数,直到它们检测到溢出,然后切换到更复杂的表示。 64 位整数可以容纳比 32 位整数大得多的值。当它在 64 位上运行时,您的测试程序可能会花费更多的时间来执行快速机器算术。

【讨论】:

这里没有使用BigNum,只有int 他在大范围内使用int,而不是在他的实现中使用任何BigInteger 我连代码都没看;当他问及 BigInteger 时,我只是假设他说的是真话! 查看我之前的评论。 BigInteger 不是问题(因为它没有被使用)。所有算术都在 int32 范围内。所以差异似乎是 32 位和 64 位算术,即使所有值都适合 int32。

以上是关于整数运算性能 32 与 64 位的主要内容,如果未能解决你的问题,请参考以下文章

在 x86-64 中访问 32 位整数数组是不是存在性能损失?

第二章习题答案

win10电脑4g内存不足怎么办啊

linux磁盘分区

x86 OS 的 64 位处理器上的 64 位整数运算

2016-6-16 拓展练习