« xDev Magazine and xDe… | Home | Black Friday coming s… »

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

result = if( value > 2, 123, 456

is faster or slower than

if value > 2 then result = "123b" else result = "456b" end if

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:

Sub Test1() LineNumber = &h6603 result = if( value > 2, 123, 456) LineNumber = &h6604 if value > 2 then result = 123 else result = 456 end if LineNumber = &h6605 End Sub

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:

Sub Test2() LineNumber = &h6603 result = if( value > 2, "123a", "456a") LineNumber = &h6604 if value > 2 then result = "123b" else result = "456b" end if LineNumber = &h6605 End Sub

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.

24 11 21 - 09:46