Hi all.
I've a piece of code executed thousands of times per secs and I'm searching for some execution speed.
Code is mainly pushing inside stack some parameters before calling a function.
My PowerBasic code is the following:
SELECT CASE LONG @lFunction.params(pCount).ParamSubType
CASE %ArrayType_Long
PushParam = @lFunction.params(pCount).DataValue.lLong
! push PushParam
CASE %ArrayType_Single
PushParamSingle = @lFunction.params(pCount).DataValue.lSingle
! push PushParamSingle
CASE %ArrayType_DWord
PushParam = @lFunction.params(pCount).DataValue.lDWord
! push PushParam
Case %ArrayType_Double
PushParam = VarPtr(@lFunction.params(pCount).DataValue.lDouble)
!mov eax,PushParam ;'point EAX TO the 8 BYTE variable
!mov edx,[eax+4] ;'GET latest 4 bytes
!push edx ;'push them
!mov edx,[eax] ;'GET the 1st 4 bytes
!push edx ;'push them
CASE %ArrayType_Quad
PushParam = VARPTR(@lFunction.params(pCount).DataValue.lQuad)
!mov eax,PushParam ;'point EAX TO the 8 BYTE variable
!mov edx,[eax+4] ;'GET latest 4 bytes
!push edx ;'push them
!mov edx,[eax] ;'GET the 1st 4 bytes
!push edx ;'push them
CASE %ArrayType_Ext
PushParam = VARPTR(@lFunction.params(pCount).DataValue.lExt)
!mov eax,PushParam ;'point EAX TO the 8 BYTE variable
!mov edx,[eax+8] ;'GET latest 4 bytes
!push edx ;'push them
!mov edx,[eax+4] ;'GET mid 4 bytes
!push edx ;'push them
!mov edx,[eax] ;'GET the 1st 4 bytes
!push edx ;'push them
CASE %ArrayType_Integer
PushParam = @lFunction.params(pCount).DataValue.lInteger
! push PushParam
Case %ArrayType_Byte
PushParam = @lFunction.params(pCount).DataValue.lByte
! push PushParam
Case %ArrayType_Word
PushParam = @lFunction.params(pCount).DataValue.lWord
! push PushParam
END SELECT
I have ZERO knowledge of ASM but it seems to me I'm making a useless assignment to PushParam variable, at least for parameters that are exactly 4 bytes long (DWORD, LONG, SINGLE). For example, is there a way to pass from:
CASE %ArrayType_Long
PushParam = @lFunction.params(pCount).DataValue.lLong
! push PushParam
to something like:
CASE %ArrayType_Long
! push @lFunction.params(pCount).DataValue.lLong
and avoid one assignment?
Thanks a lot
Eros
Eros,
Perhaps you could speed it up a little, if using the ON GOTO statement instead of SELECT CASE LONG.
(nothing is faster than a direct jump)
...
Thanks a lot Patrice.
Done but not able to note significantly changes:
On @lFunction.params(pCount).ParamSubType GoTo _
goto_ArrayType_Byte , _' 1&
goto_ArrayType_Integer , _' 2&
goto_ArrayType_Word , _' 3&
goto_ArrayType_DWord , _' 4&
goto_ArrayType_Long , _' 5&
goto_ArrayType_Quad , _' 6&
goto_ArrayType_Single , _' 7&
goto_ArrayType_Double , _' 8&
goto_ArrayType_Currency , _' 9&
goto_ArrayType_Ext '10&
GoTo goto_End_Select
goto_ArrayType_Byte:
PushParam = @lFunction.params(pCount).DataValue.lByte
! push PushParam
GoTo goto_End_Select
goto_ArrayType_Integer:
PushParam = @lFunction.params(pCount).DataValue.lInteger
! push PushParam
GoTo goto_End_Select
goto_ArrayType_Word:
PushParam = @lFunction.params(pCount).DataValue.lWord
! push PushParam
GoTo goto_End_Select
goto_ArrayType_DWord:
PushParam = @lFunction.params(pCount).DataValue.lDWord
! push PushParam
GoTo goto_End_Select
goto_ArrayType_Long:
PushParam = @lFunction.params(pCount).DataValue.lLong
! push PushParam
GoTo goto_End_Select
goto_ArrayType_Quad:
PushParam = VarPtr(@lFunction.params(pCount).DataValue.lQuad)
!mov eax,PushParam ;'point EAX TO the 8 BYTE variable
!mov edx,[eax+4] ;'GET latest 4 bytes
!push edx ;'push them
!mov edx,[eax] ;'GET the 1st 4 bytes
!push edx ;'push them
GoTo goto_End_Select
goto_ArrayType_Single:
PushParamSingle = @lFunction.params(pCount).DataValue.lSingle
! push PushParamSingle
GoTo goto_End_Select
goto_ArrayType_Double:
PushParam = VarPtr(@lFunction.params(pCount).DataValue.lDouble)
!mov eax,PushParam ;'point EAX TO the 8 BYTE variable
!mov edx,[eax+4] ;'GET latest 4 bytes
!push edx ;'push them
!mov edx,[eax] ;'GET the 1st 4 bytes
!push edx ;'push them
goto_ArrayType_Currency:
GoTo goto_End_Select
goto_ArrayType_Ext:
PushParam = VarPtr(@lFunction.params(pCount).DataValue.lExt)
!mov eax,PushParam ;'point EAX TO the 8 BYTE variable
!mov edx,[eax+8] ;'GET latest 4 bytes
!push edx ;'push them
!mov edx,[eax+4] ;'GET mid 4 bytes
!push edx ;'push them
!mov edx,[eax] ;'GET the 1st 4 bytes
!push edx ;'push them
goto_End_Select:
Hi Eros,
Cases translate into very efficient machine code, but if you can avoid arrays in favour of pointering, there is some performance gain to be had. Translating an array index into an offset from the array base address is extra work for the CPU.
REGISTER lpf AS LONG
lpf = @lFunction.params(pCount).ParamSubType
ON lpf GOTO
Try also to make the matching sub/function STATIC.
...
Thanks.
I will try some pointers tricks and data pre-fetching
Looking at this code, I think that you will hardly get significant speedups anyway from this code.
If its a lot (more the 110) CASES, and they are called randomly, you can try SELECT CASE AS CONST instead of OS LONG.
Replacing the CASE AS LONG with a Chain of IF's will not help you anyway here.
If you make Cache-Tricks, the result may differ from CPU to CPU.
If you really see speed problems, i guess there should be other places to look at.
Eros,
Do you encode thinBasic scripts into interprative tokens or do you execute directly from the script? You could embed function pointers as tokens, - patch them into scripts when and where they are needed.
Yes, I'm already doing that.
All is tokenized and resolved once and before script execution is started. The rest are only pointers to data structures.
Only pointers to local variables data are redetermined at runtime because they change at every function execution.
I was hoping that could be a way to avoid assignment in
CASE %ArrayType_Long
PushParam = @lFunction.params(pCount).DataValue.lLong
! push PushParam
and pass to something like:
CASE %ArrayType_Long
! push @lFunction.params(pCount).DataValue.lLong
Thanks
Eros
Both of those expressions would involve the same steps being taken at CPU level:
The Assembler will only take variables that have been resolved to a specific address.
Typically: local variable address are offset from the ebp register
local a,b,c as long
A: [ebp-4]
B: [ebp-8]
C: [ebp-12]
the values contained in these can be pushed onto the stack directly
The equivalent of push A would be
push [ebp-4]
For passing byref, the adresses must first be resolved, using the lea instruction [load effective address]
lea eax,A
push eax
For an array element the index has to scaled and then added to the address before pushing
mov eax,i
imul eax,eax,s
lea ecx,A
add eax,ecx
push eax
Getting the value, at a pointer (VARPTR) is done like this:
mov eax,[eax]
Similarly STRPTR (load the first character of a dynamic string)
mov eax,[eax]
mov al,[eax]
I hope this gives you a rough idea of what is involved. :)
Eros, you can onlysave time at a place where time is wasted.
PushParam = @lFunction.params(pCount).DataValue.lLong
! push PushParam
this is a operation that can be performed millions of times in a second.
If you just execute it
"code executed thousands of times per secs " there will not be any significant speedup here even if you could double its speed.
Powerbasic has a nice feature, which is the "PROFILE" Command.
Use it to really find the sections in your code that need the amount of time which is significant.
A menmonic which does not significant CPU cycles or where the usage is heavily dependent on cpu architecture is normaly not a good target for optimization.
This assignment mainly uses time for the memory access. Therefore the needed time will depend on cache architecture of the used cpu as well as on which code was executed before.