Which one is faster

Just starting out? Need help? Post your questions and find answers here.
User avatar
jacdelad
Addict
Addict
Posts: 1993
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Which one is faster

Post 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.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
STARGÅTE
Addict
Addict
Posts: 2227
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Which one is faster

Post 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
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: Which one is faster

Post 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)
Egypt my love
User avatar
jacdelad
Addict
Addict
Posts: 1993
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Which one is faster

Post 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?
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
AZJIO
Addict
Addict
Posts: 2143
Joined: Sun May 14, 2017 1:48 am

Re: Which one is faster

Post 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
User avatar
STARGÅTE
Addict
Addict
Posts: 2227
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Which one is faster

Post 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)
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
idle
Always Here
Always Here
Posts: 5836
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Which one is faster

Post 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 
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: Which one is faster

Post 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)
Egypt my love
pjay
Enthusiast
Enthusiast
Posts: 251
Joined: Thu Mar 30, 2006 11:14 am

Re: Which one is faster

Post 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
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Which one is faster

Post 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)
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
jacdelad
Addict
Addict
Posts: 1993
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Which one is faster

Post 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!
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
AZJIO
Addict
Addict
Posts: 2143
Joined: Sun May 14, 2017 1:48 am

Re: Which one is faster

Post 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.
User avatar
jacdelad
Addict
Addict
Posts: 1993
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Which one is faster

Post by jacdelad »

How should this task be done with a macro???
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
pf shadoko
Enthusiast
Enthusiast
Posts: 385
Joined: Thu Jul 09, 2015 9:07 am

Re: Which one is faster

Post 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
pjay
Enthusiast
Enthusiast
Posts: 251
Joined: Thu Mar 30, 2006 11:14 am

Re: Which one is faster

Post by pjay »

You're passing the functions an unchanging value, so the compiler is optimizing it away (when the optimizer is enabled that is).
Post Reply