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.