使用F#语言时,"嵌套if"与"if and"的性能差异

13
以下代码的执行结果为 slow1 = 1323 msslow2 = 1311 msfast = 897 ms。这是怎么可能的?
在这里:嵌套或非嵌套if块?他们提到:

任何现代编译器(我指的是过去20年中构建的任何编译器)都将这些编译为相同的代码。

let s = System.Diagnostics.Stopwatch()
let mutable a = 1
s.Start()

for i in 0 .. 1000000000 do
  if i < 0 then
    if i < 0 then
      a <- 4

printfn "fast = %d" s.ElapsedMilliseconds

s.Restart()

for i in 0 .. 1000000000 do
  if i < 0 && i < 0 then
    a <- 4

printfn "slow1 = %d" s.ElapsedMilliseconds

s.Restart()

for i in 0 .. 1000000000 do
  if i < 0 & i < 0 then
    a <- 4

printfn "slow2 = %d" s.ElapsedMilliseconds

你在发布模式下试过了吗? - AK_
1
是的。还有任何 CPU、X86 和 X64。在调试模式下,两个版本变得同样缓慢(3083 毫秒)。 - Oldrich Svec
我必须说,同样缓慢的结果比不同的结果更有“意义”…… - AK_
1
你能在问题中添加MSIL吗? - AK_
@JohnPalmer:不,那是我第一次尝试的。 - Oldrich Svec
显示剩余4条评论
2个回答

4
我得到了来自ildasm的MSIL代码,我会在这里发布它以供他人详细阐述(时间不够)——现在是社区共享的时候。
快速版(仅包含 i 比较行,因为其余部分都是相同的):
//000030:   if i < 1000 then
  IL_001f:  ldloc.0
  IL_0020:  ldc.i4     0x3e8
  IL_0025:  bge.s      IL_003b
//000031:     if i < 1000 then
  IL_0027:  ldloc.0
  IL_0028:  ldc.i4     0x3e8
  IL_002d:  bge.s      IL_0038

缓慢:

//000039:   if i < 1000 && i < 1000 then
  IL_0084:  ldloc.0
  IL_0085:  ldc.i4     0x3e8
  IL_008a:  bge.s      IL_0097
  IL_008c:  ldloc.0
  IL_008d:  ldc.i4     0x3e8
  IL_0092:  clt
  IL_0094:  nop
  IL_0095:  br.s       IL_0099
  IL_0097:  ldc.i4.0
  IL_0098:  nop
  IL_0099:  brfalse.s  IL_00a4

顺便提一句,C#版本的时间对于两个版本来说是相同的。

在反汇编时,我注意到F#变量是Program.i和Program.a,所以我不确定在F#中是否存在某些对象干扰,而在C#中则不存在。


在优化的构建中,nop 不应该存在。 - usr
这是使用优化开关(VS2012)进行发布构建的输出。没有关于优化的详细信息,只有开或关。 - Phil H

4

来自 Don Syme 的电子邮件:

是的,我们已经注意到了这个线程并记录了一个问题。这不完全是一个错误(代码执行正确),但肯定需要在这里获得相同的性能。


1
能否告诉唐,由于F#开发者/爱好者社区规模相对较小,而且F#开发团队似乎是一群很酷的家伙(从编译器开发者的标准来看),我们希望能得到一些个人关注 :-) - AK_

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接