So my question is why is let _ = this faster then this != nil?
Example:
This is:
let this : Bool? = true // let start = DispatchTime.now() for _ in 0...Int.max { guard this != nil else { continue } } let end = DispatchTime.now() let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds let timeInterval = Double(nanoTime) print("Time /(timeInterval)")
Slower than:
let this : Bool? = true // let start = DispatchTime.now() for _ in 0...Int.max { guard _ = this else { continue } } let end = DispatchTime.now() let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds let timeInterval = Double(nanoTime) print("Time /(timeInterval)")
I would check out this post. They both result in the same underlying assembly instructions. My guess is that they both take such a small amount of time to compile that the time difference you’re noticing could be due to other miscellaneous outliers affecting performance.
Following Jonathan’s response I checked the actual disassembled instructions.
Here is the result:
For the code:
let this : Bool? = nil this != nil
we get:
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
and for:
let this : Bool? = nil let _ = this
there is:
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
Also, thank you Code Different for pointing to the Optimisation level.
Changing the value from [-Onone] to [-O -whole-module-optimisation] will cause a change for the generated asm in the following way:
The
let this : Bool? = nil let _ = this
has
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
and the
let this : Bool? = nil this != nil
to
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
So the resulted instructions are actually the same and the time to execute them should be pretty close.