整数运算性能 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 位的主要内容,如果未能解决你的问题,请参考以下文章