Page 1 of 2
Which one is faster
Posted: Tue Dec 05, 2023 5:55 am
by jacdelad
Hello,
just out of curiosity, which one is faster (on ASM backend)?
Code: Select all
Procedure Limit(x.l)
If x>255
x=255
ElseIf x<0
x=0
EndIf
ProcedureReturn x
EndProcedure
or
Code: Select all
Procedure Limit(x.l)
If x>255
ProcedureReturn 255
ElseIf x<0
ProcedureReturn 0
Else
ProcedureReturn x
EndIf
EndProcedure
or
Code: Select all
Procedure Limit(x.l)
If x>255
ProcedureReturn 255
ElseIf x<0
ProcedureReturn 0
EndIf
ProcedureReturn x
EndProcedure
?
I would guess the first one is the slowest, but how about the second and third? How does the Else affect speed? And I know, the difference (if any) is almost unmeasureable, I'm just asking out of curiosity.
Re: Which one is faster
Posted: Tue Dec 05, 2023 6:07 am
by STARGÅTE
You have to think different. What is the parameter space of Limit()?
You function Limit() is slowest when
x is between 0 and 255, because then the first and second if is false and you have two additional jumps.
Further, you have to swap x>255 with x<0, when you think that Limit() receives more likely a value below 0 than above 255.
This means, the following function could be faster, when most of the times
x is between 0 and 255, because you have then just one query and no jump.
Code: Select all
Procedure Limit(x.l)
If x & (~$FF) = 0
ProcedureReturn x
ElseIf x < 0
ProcedureReturn 0
Else
ProcedureReturn 255
EndIf
EndProcedure
Re: Which one is faster
Posted: Tue Dec 05, 2023 6:31 am
by RASHAD
Hi jacdelad
Maybe
The speed must be checked
Code: Select all
Procedure Limit(x.l)
While x < 256 And x >= 0
ProcedureReturn x
Wend
EndProcedure
Debug Limit(10)
Re: Which one is faster
Posted: Tue Dec 05, 2023 6:46 am
by jacdelad
Thanks for the answers.
@STARGATE: Yes, I forgot about what scenario might be more likely, values above, below, or withing the range. Let's look at the case, they are within the range, is it faster to use Else or better put the ProcedureReturn outside the If?
@RASHAD: This does not work for values above 255 or below 0. And why use a loop at all?
Re: Which one is faster
Posted: Tue Dec 05, 2023 6:47 am
by AZJIO
Code: Select all
Procedure RangeCheck(value, min, max)
If value < min
value = min
ElseIf value > max
value = max
EndIf
ProcedureReturn value
EndProcedure
s = ReadPreferenceInteger("num", s)
s = RangeCheck(s, 20, 40)
"Limit4" is the best if the number is inside the range
Code: Select all
DisableDebugger
Procedure Limit1(x.l)
If x>255
x=255
ElseIf x<0
x=0
EndIf
ProcedureReturn x
EndProcedure
Procedure Limit2(x.l)
If x>255
ProcedureReturn 255
ElseIf x<0
ProcedureReturn 0
Else
ProcedureReturn x
EndIf
EndProcedure
Procedure Limit3(x.l)
If x>255
ProcedureReturn 255
ElseIf x<0
ProcedureReturn 0
EndIf
ProcedureReturn x
EndProcedure
Procedure Limit4(x.l)
If x & (~$FF) = 0
ProcedureReturn x
ElseIf x < 0
ProcedureReturn 0
Else
ProcedureReturn 255
EndIf
EndProcedure
Procedure Limit5(x.l)
If x < 0
ProcedureReturn 0
ElseIf x > 255
ProcedureReturn 255
EndIf
ProcedureReturn x
EndProcedure
#num = 11
#count = 10000000
StartTime = ElapsedMilliseconds()
For i = 0 To #count
Limit1(#num)
Next
xx = ElapsedMilliseconds() - StartTime
StartTime = ElapsedMilliseconds()
For i = 0 To #count
Limit2(#num)
Next
yy = ElapsedMilliseconds() - StartTime
StartTime = ElapsedMilliseconds()
For i = 0 To #count
Limit3(#num)
Next
zz = ElapsedMilliseconds() - StartTime
StartTime = ElapsedMilliseconds()
For i = 0 To #count
Limit4(#num)
Next
aa = ElapsedMilliseconds() - StartTime
StartTime = ElapsedMilliseconds()
For i = 0 To #count
Limit5(#num)
Next
bb = ElapsedMilliseconds() - StartTime
EnableDebugger
Debug xx
Debug yy
Debug zz
Debug aa
Debug bb
Re: Which one is faster
Posted: Tue Dec 05, 2023 8:00 am
by STARGÅTE
jacdelad wrote: Tue Dec 05, 2023 6:46 am
Let's look at the case, they are within the range, is it faster to use Else or better put the ProcedureReturn outside the If?
There should be no difference, as there is no additional jump command, as long as you use ProcedureReturn in all cases.
To completely avoid jumps you can try to use this ASM code, which would be the fastest variant when using custom limits:
Code: Select all
Procedure.i Clamp(Value.i, Min.i, Max.i)
! MOV rax, [p.v_Value]
! CMP rax, [p.v_Min]
! CMOVL rax, [p.v_Min] ; If Value < Min : Value = Min : EndIf
! CMP rax, [p.v_Max]
! CMOVG rax, [p.v_Max] ; If Value > Max : Value = Max : EndIf
ProcedureReturn
EndProcedure
Debug Clamp(-5, 0, 255)
Debug Clamp(23, 0, 255)
Debug Clamp(300, 0, 255)
Re: Which one is faster
Posted: Tue Dec 05, 2023 8:10 am
by idle
didn't see the posts by stargate clicked without looking
if you want asm macro you can try this
Code: Select all
Macro limit(x)
!mov edx, 255
!mov eax, [v_x]
!cmp eax, edx
!cmovg eax, edx
!test eax, eax
!mov edx, 0
!cmovl eax,edx
!mov [v_x],eax
EndMacro
Define x.l ;<--- needs to be a long
x = -5
limit(x)
Debug x
x = 258
limit(x)
Debug x
x = 126
limit(x)
Debug x
Re: Which one is faster
Posted: Tue Dec 05, 2023 8:25 am
by RASHAD
For x86 - x64
Code: Select all
Procedure.i LimitValues(Value.i, Min.i, Max.i)
!mov eax,dword[p.v_Value]
!xor edx,edx
!cmp eax,dword[p.v_Min]
!cmovl eax,edx
!mov edx,dword[p.v_Max]
!cmp eax,edx
!cmovg eax,edx
ProcedureReturn
EndProcedure
Debug LimitValues(-5, 0, 255)
Debug LimitValues(23, 0, 255)
Debug LimitValues(300, 0, 255)
Re: Which one is faster
Posted: Tue Dec 05, 2023 8:44 am
by pjay
For sure use macros to remove the Procedure call overhead with the ASM compiler. They basically do what the C compiler should do (inline the functions).
I use standard If/Endif for simplicitys sake, as variable type & x86/x64 asm handling then become irrelevant.
Code: Select all
Macro Clamp01M(x) : If x < 0 : x = 0 : ElseIf x > 1 : x = 1 : EndIf : EndMacro
Macro Clamp255M(x) : If x < 0 : x = 0 : ElseIf x > 255 : x = 255 : EndIf : EndMacro
Macro ClampIM(in, min, max) : If in < min : in = min : ElseIf in > max : in = max : EndIf : EndMacro
Macro ClampIOM(out, in, min, max) : If in < min : out = min : ElseIf in > max : out = max : Else : out = in : EndIf : EndMacro
Re: Which one is faster
Posted: Tue Dec 05, 2023 8:50 am
by wilbert
Code: Select all
Procedure Limit(x.l)
; limit between 0 and 255
!movd xmm0, [p.v_x]
!packssdw xmm0, xmm0
!packuswb xmm0, xmm0
!movd eax, xmm0
ProcedureReturn
EndProcedure
Debug Limit(-10)
Debug Limit(1000)
Debug Limit(100)
Re: Which one is faster
Posted: Tue Dec 05, 2023 9:26 am
by jacdelad
I really appreciate the approaches, but it was STARGATE who answered my question. I was really just asking for what is faster, the Else or not the Else.
Anyway, thanks to everyone who put some brain in here!
Re: Which one is faster
Posted: Tue Dec 05, 2023 9:35 am
by AZJIO
pjay wrote: Tue Dec 05, 2023 8:44 am
For sure use macros to remove the Procedure call overhead with the ASM compiler.
The macro increases the code size. The size of the executable file increases and, as I understand it, in memory when executing code, when moving from label to label, the movement will be greater. If you use an empty function (for testing) and call it a million times, the overhead of running it will be negligible. Therefore, you should not complicate the code where speed is not required, but reduces the readability of the code or increases the size of the executable file.
Re: Which one is faster
Posted: Tue Dec 05, 2023 10:08 am
by jacdelad
How should this task be done with a macro???
Re: Which one is faster
Posted: Tue Dec 05, 2023 11:53 am
by pf shadoko
Hi there,
out of curiosity i wanted to compare with the c backend
try with and without debugger
can someone explain?
some modifications in the code to make sure it was really doing the calculations
important: I multiplied by 100 #count
Code: Select all
DisableDebugger
Procedure Limit1(x.l)
If x>255
x=255
ElseIf x<0
x=0
EndIf
ProcedureReturn x
EndProcedure
Procedure Limit2(x.l)
If x>255
ProcedureReturn 255
ElseIf x<0
ProcedureReturn 0
Else
ProcedureReturn x
EndIf
EndProcedure
Procedure Limit3(x.l)
If x>255
ProcedureReturn 255
ElseIf x<0
ProcedureReturn 0
EndIf
ProcedureReturn x
EndProcedure
Procedure Limit4(x.l)
If x & (~$FF) = 0
ProcedureReturn x
ElseIf x < 0
ProcedureReturn 0
Else
ProcedureReturn 255
EndIf
EndProcedure
Procedure Limit5(x.l)
If x < 0
ProcedureReturn 0
ElseIf x > 255
ProcedureReturn 255
EndIf
ProcedureReturn x
EndProcedure
#num = 11
#count = 1000000000
StartTime = ElapsedMilliseconds()
For i = 0 To #count
a+Limit1(#num)
Next
xx = ElapsedMilliseconds() - StartTime
StartTime = ElapsedMilliseconds()
For i = 0 To #count
a+Limit2(#num)
Next
yy = ElapsedMilliseconds() - StartTime
StartTime = ElapsedMilliseconds()
For i = 0 To #count
a+Limit3(#num)
Next
zz = ElapsedMilliseconds() - StartTime
StartTime = ElapsedMilliseconds()
For i = 0 To #count
a+Limit4(#num)
Next
aa = ElapsedMilliseconds() - StartTime
StartTime = ElapsedMilliseconds()
For i = 0 To #count
a+Limit5(#num)
Next
bb = ElapsedMilliseconds() - StartTime
EnableDebugger
MessageRequester("somme : "+a,""+xx+#LF$+yy+#LF$+zz+#LF$+aa+#LF$+bb)
Debug xx
Debug yy
Debug zz
Debug aa
Debug bb
Re: Which one is faster
Posted: Tue Dec 05, 2023 12:09 pm
by pjay
You're passing the functions an unchanging value, so the compiler is optimizing it away (when the optimizer is enabled that is).