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).