在Swift中,“let _ = this”为什么比“this != nil”更快?

7
所以我的问题是为什么let _ = thisthis != nil快?
例子:
这是:
let this : Bool? = true //

let start = DispatchTime.now()
for _ in 0...100000000  {
    guard this != nil else { continue }
}
let end = DispatchTime.now()

let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
let timeInterval = Double(nanoTime)
print("Time \(timeInterval)") 

         // Time 5426559135.0
         // Time 5428084767.0
         // Time 5327325459.0

比...慢:
let this : Bool? = true //

let start = DispatchTime.now()
for _ in 0...100000000  {
    guard let _ = this else { continue }
}
let end = DispatchTime.now()

let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
let timeInterval = Double(nanoTime)
print("Time \(timeInterval)")

          // Time 257045414.0
          // Time 261933863.0
          // Time 263465919.0
2个回答

13

根据Jonathan回答,我查看了实际反汇编指令。

以下是结果:

对于这段代码:

let this : Bool? = nil
this != nil

我们得到:

    0x100001290 <+0>:  pushq  %rbp
    0x100001291 <+1>:  movq   %rsp, %rbp
    0x100001294 <+4>:  subq   $0x30, %rsp
    0x100001298 <+8>:  leaq   0x2c7259(%rip), %rdx      ; type metadata for Swift.Bool
    0x10000129f <+15>: leaq   0x2b66ca(%rip), %rcx      ; protocol witness table for Swift.Bool : Swift.Equatable in Swift
    0x1000012a6 <+22>: leaq   -0x18(%rbp), %rax
    0x1000012aa <+26>: leaq   -0x8(%rbp), %r8
    0x1000012ae <+30>: movb   $0x2, 0x2f940b(%rip)
    0x1000012b5 <+37>: movb   0x2f9404(%rip), %r9b      ; test2.this : Swift.Optional<Swift.Bool>
    0x1000012bc <+44>: movb   %r9b, -0x8(%rbp)
    0x1000012c0 <+48>: movb   $0x2, -0x10(%rbp)
    0x1000012c4 <+52>: movb   -0x10(%rbp), %r9b
    0x1000012c8 <+56>: movb   %r9b, -0x18(%rbp)
    0x1000012cc <+60>: movl   %edi, -0x1c(%rbp)
    0x1000012cf <+63>: movq   %r8, %rdi
    0x1000012d2 <+66>: movq   %rsi, -0x28(%rbp)
    0x1000012d6 <+70>: movq   %rax, %rsi
    0x1000012d9 <+73>: callq  0x10004df10               ; Swift.!= infix <A where A: Swift.Equatable> (Swift.Optional<A>, Swift.Optional<A>) -> Swift.Bool
    0x1000012de <+78>: xorl   %r10d, %r10d
    0x1000012e1 <+81>: movb   %al, -0x29(%rbp)
    0x1000012e4 <+84>: movl   %r10d, %eax
    0x1000012e7 <+87>: addq   $0x30, %rsp
    0x1000012eb <+91>: popq   %rbp
    0x1000012ec <+92>: retq

以及:

let this : Bool? = nil
let _ = this

有:

    0x1000012d0 <+0>:  pushq  %rbp
    0x1000012d1 <+1>:  movq   %rsp, %rbp
    0x1000012d4 <+4>:  xorl   %eax, %eax
    0x1000012d6 <+6>:  movb   $0x2, 0x2f93e3(%rip)
    0x1000012dd <+13>: movl   %edi, -0x4(%rbp)
    0x1000012e0 <+16>: movq   %rsi, -0x10(%rbp)
    0x1000012e4 <+20>: popq   %rbp
    0x1000012e5 <+21>: retq   

此外,感谢Code Different指出优化级别的问题。

将值从[-Onone]更改为[-O -whole-module-optimisation]将导致生成的汇编代码发生以下变化:

...

let this : Bool? = nil
let _ = this

    0x100001490 <+0>:  pushq  %rbp
    0x100001491 <+1>:  movq   %rsp, %rbp
    0x100001494 <+4>:  movb   $0x2, 0x3d9595(%rip)      ; gCRAnnotations + 63
    0x10000149b <+11>: xorl   %eax, %eax
    0x10000149d <+13>: popq   %rbp
    0x10000149e <+14>: retq   

并且

let this : Bool? = nil
this != nil
    0x100001490 <+0>:  pushq  %rbp
    0x100001491 <+1>:  movq   %rsp, %rbp
    0x100001494 <+4>:  movb   $0x2, 0x3d9595(%rip)      ; gCRAnnotations + 63
    0x10000149b <+11>: xorl   %eax, %eax
    0x10000149d <+13>: popq   %rbp
    0x10000149e <+14>: retq   

因此,生成的指令实际上是相同的,执行它们的时间应该非常接近。


有趣。这表明编译器错过了一个非常重要的优化机会。我建议向Swift开源项目提交错误报告。 - rickster
1
优化级别是什么? - Code Different
Code Different,我已经更新了回复。 - Durdu

1
我建议您查看这篇文章。它们都会产生相同的底层汇编指令。我的猜测是,它们编译所需的时间非常短,您注意到的时间差可能是由其他杂项异常影响性能引起的。

这是不太可能的,因为我使用多种情况多次重复了测试。 上面的例子只是一些简短的代码示例。 - Durdu
感谢Jonathan提到汇编语言。我不知道我的头脑在哪里... - Durdu

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