If vs If in Xojo
On the Xojo forum was a discussion about the inline If vs the block If syntax in Xojo. Basically whether
is faster or slower than
And the answer is of course: it depends. It depends on what is exactly done and how much compiler optimisation eliminates test code. Like if you do a compare in a loop running one million times, the compiler may detect that the result doesn’t change and only run it once. Also if you assign a value to a variable and then compare that, the compiler may know the result and skip the compare.
So let’s deep dive into the assembly.
Here is our test code. Result and value as properties in the window, so the compiler can’t know the values:
In assembly (for Intel), this looks like the following code:
// load 2 already for compare
0x10012edb1 <+97>: movl $0x2, %eax
0x10012edb6 <+102>: movl %eax, %ecx
// LineNumber = &h6603
0x10012edb8 <+104>: movq -0x30(%rbp), %rdx
0x10012edbc <+108>: movq $0x6603, -0xf0(%rdx) ; imm = 0x6603
// compare value to 2
0x10012edc7 <+119>: cmpq -0xe0(%rdx), %rcx
0x10012edce <+126>: setl %sil
0x10012edd2 <+130>: andb $0x1, %sil
0x10012edd6 <+134>: movb %sil, -0x9(%rbp)
// now check boolean of result
0x10012edda <+138>: cmpb $0x1, %sil
0x10012edde <+142>: je 0x10012edea ; <+154>
// result = 456
0x10012ede0 <+144>: movq $0x1c8, -0x18(%rbp) ; imm = 0x1C8
0x10012ede8 <+152>: jmp 0x10012edf2 ; <+162>
// result = 123
0x10012edea <+154>: movq $0x7b, -0x18(%rbp)
// load 2 again for compare
0x10012edf2 <+162>: movl $0x2, %eax
0x10012edf7 <+167>: movl %eax, %ecx
0x10012edf9 <+169>: movq -0x18(%rbp), %rdx
0x10012edfd <+173>: movq -0x30(%rbp), %rsi
// LineNumber = &h6604
0x10012ee01 <+177>: movq %rdx, -0xe8(%rsi)
0x10012ee08 <+184>: movq $0x6604, -0xf0(%rsi) ; imm = 0x6604
// compare value to 2
0x10012ee13 <+195>: cmpq -0xe0(%rsi), %rcx
0x10012ee1a <+202>: setl %dil
0x10012ee1e <+206>: andb $0x1, %dil
0x10012ee22 <+210>: movb %dil, -0x19(%rbp)
// now check boolean of result
0x10012ee26 <+214>: cmpb $0x0, %dil
0x10012ee2a <+218>: jne 0x10012ee3d ; <+237>
// result = 456
0x10012ee2c <+220>: movq -0x30(%rbp), %rax
0x10012ee30 <+224>: movq $0x1c8, -0xe8(%rax) ; imm = 0x1C8
0x10012ee3b <+235>: jmp 0x10012ee4c ; <+252>
// result = 123
0x10012ee3d <+237>: movq -0x30(%rbp), %rax
0x10012ee41 <+241>: movq $0x7b, -0xe8(%rax)
// LineNumber = &h6605
0x10012ee4c <+252>: movq -0x30(%rbp), %rax
0x10012ee50 <+256>: movq $0x6605, -0xf0(%rax) ; imm = 0x6605
As you see, the code for If() and If Block is about the same.
Now change result to string and put in „123“ and „456“ and make the property a string:
The assembly looks a bit longer with string locking. When locking, the object gets its reference counter increased and later when unlocking the counter is decreased. Once the reference counter reaches zero, the string is freed. So here a longer listing:
// load 2 already for compare
0x10012ec99 <+105>: movl $0x2, %eax
0x10012ec9e <+110>: movl %eax, %ecx
// LineNumber = &h6603
0x10012eca0 <+112>: movq -0x38(%rbp), %rdx
0x10012eca4 <+116>: movq $0x6603, -0xf0(%rdx) ; imm = 0x6603
// compare value to 2
0x10012ecaf <+127>: cmpq -0xe0(%rdx), %rcx
0x10012ecb6 <+134>: setl %sil
0x10012ecba <+138>: andb $0x1, %sil
0x10012ecbe <+142>: movb %sil, -0x9(%rbp)
// now check boolean of result
0x10012ecc2 <+146>: cmpb $0x1, %sil
0x10012ecc6 <+150>: je 0x10012eced ; <+189>
// lock text „456“ and store in variable
0x10012ecc8 <+152>: leaq 0x28659(%rip), %rdi
0x10012eccf <+159>: movq -0x18(%rbp), %rsi
0x10012ecd3 <+163>: callq 0x100135c8a ; symbol stub for: RuntimeLockText
0x10012ecd8 <+168>: leaq 0x28649(%rip), %rsi
0x10012ecdf <+175>: movq $0x0, -0x18(%rbp)
0x10012ece7 <+183>: movq %rsi, -0x18(%rbp)
0x10012eceb <+187>: jmp 0x10012ed10 ; <+224>
// lock text „123“ and store in variable
0x10012eced <+189>: leaq 0x28614(%rip), %rdi
0x10012ecf4 <+196>: xorl %eax, %eax
0x10012ecf6 <+198>: movl %eax, %esi
0x10012ecf8 <+200>: callq 0x100135c8a ; symbol stub for: RuntimeLockText
0x10012ecfd <+205>: leaq 0x28604(%rip), %rsi
0x10012ed04 <+212>: movq $0x0, -0x18(%rbp)
0x10012ed0c <+220>: movq %rsi, -0x18(%rbp)
// free old string in result
0x10012ed10 <+224>: movq -0x20(%rbp), %rdi
0x10012ed14 <+228>: callq 0x100135e7c ; symbol stub for: RuntimeUnlockString
// store nil pointer for string
0x10012ed19 <+233>: movq $0x0, -0x20(%rbp)
// now convert text to string
0x10012ed21 <+241>: movq -0x18(%rbp), %rdi
0x10012ed25 <+245>: movq %rdi, -0x40(%rbp)
0x10012ed29 <+249>: callq 0x100135e2e ; symbol stub for: RuntimeStringFromText
// store the new string
0x10012ed2e <+254>: movq %rax, -0x20(%rbp)
0x10012ed32 <+258>: movq -0x38(%rbp), %rdi
0x10012ed36 <+262>: movq -0xe8(%rdi), %rsi
0x10012ed3d <+269>: movq %rax, %rdi
0x10012ed40 <+272>: movq %rax, -0x48(%rbp)
0x10012ed44 <+276>: callq 0x100135c84 ; symbol stub for: RuntimeLockString
// free the text
0x10012ed49 <+281>: movq -0x38(%rbp), %rax
0x10012ed4d <+285>: movq $0x0, -0xe8(%rax)
0x10012ed58 <+296>: movq -0x48(%rbp), %rsi
0x10012ed5c <+300>: movq %rsi, -0xe8(%rax)
0x10012ed63 <+307>: movq -0x40(%rbp), %rdi
0x10012ed67 <+311>: callq 0x100135e82 ; symbol stub for: RuntimeUnlockText
// unlock new string
0x10012ed6c <+316>: movq $0x0, -0x18(%rbp)
0x10012ed74 <+324>: movq -0x48(%rbp), %rdi
0x10012ed78 <+328>: callq 0x100135e7c ; symbol stub for: RuntimeUnlockString
// load 2 again for compare
0x10012ed7d <+333>: movl $0x2, %ecx
0x10012ed82 <+338>: movl %ecx, %eax
0x10012ed84 <+340>: movq $0x0, -0x20(%rbp)
// LineNumber = &h6604
0x10012ed8c <+348>: movq -0x38(%rbp), %rsi
0x10012ed90 <+352>: movq $0x6604, -0xf0(%rsi) ; imm = 0x6604
// compare value to 2
0x10012ed9b <+363>: cmpq -0xe0(%rsi), %rax
0x10012eda2 <+370>: setl %dl
0x10012eda5 <+373>: andb $0x1, %dl
0x10012eda8 <+376>: movb %dl, -0x21(%rbp)
// now check boolean of result
0x10012edab <+379>: cmpb $0x0, %dl
0x10012edae <+382>: jne 0x10012ede6 ; <+438>
0x10012edb0 <+384>: leaq 0x27fe9(%rip), %rdi ; Window1.Window1.__ctbl + 576
0x10012edb7 <+391>: movq -0x38(%rbp), %rax
0x10012edbb <+395>: movq -0xe8(%rax), %rsi
0x10012edc2 <+402>: callq 0x100135c84 ; symbol stub for: RuntimeLockUnlockStrings
0x10012edc7 <+407>: leaq 0x27fd2(%rip), %rax ; Window1.Window1.__ctbl + 576
0x10012edce <+414>: movq -0x38(%rbp), %rsi
0x10012edd2 <+418>: movq $0x0, -0xe8(%rsi)
0x10012eddd <+429>: movq %rax, -0xe8(%rsi)
0x10012ede4 <+436>: jmp 0x10012ee1a ; <+490>
0x10012ede6 <+438>: leaq 0x27f8b(%rip), %rdi ; Window1.Window1.__ctbl + 536
0x10012eded <+445>: movq -0x38(%rbp), %rax
0x10012edf1 <+449>: movq -0xe8(%rax), %rsi
0x10012edf8 <+456>: callq 0x100135c84 ; symbol stub for: RuntimeLockUnlockStrings
0x10012edfd <+461>: leaq 0x27f74(%rip), %rax ; Window1.Window1.__ctbl + 536
0x10012ee04 <+468>: movq -0x38(%rbp), %rsi
0x10012ee08 <+472>: movq $0x0, -0xe8(%rsi)
0x10012ee13 <+483>: movq %rax, -0xe8(%rsi)
// LineNumber = &h6605
0x10012ee1a <+490>: movq -0x38(%rbp), %rax
0x10012ee1e <+494>: movq $0x6605, -0xf0(%rax) ; imm = 0x6605
// cleanup follows
As you may have guessed already the inline If() was introduced at a time where text data type was current. The implementation seems to take string as text and a conversion from text to string causes here some extra work, which makes the inline If slower in that case.
I made a feature request for the text usage in If(): Feedback #66757.