Page 1 of 2
Jumptable to labels
Posted: Thu Mar 16, 2017 7:37 pm
by Josh
I know how to create a jumptable to procedures. But if I want to create a jumptable to labels, then I have, as far as I know, to use assambler. Unfortunately, I have very little idea of ASM and created the following example:
Code: Select all
Procedure Jump (x)
DataSection
DataSta:
Data.i ?Label_000
Data.i ?Label_001
Data.i ?Label_002
Data.i ?Label_003
Data.i ?Label_004
Data.i ?Label_005
EndDataSection
*AdrLabel = PeekI (?DataSta + x * SizeOf (Integer))
!jmp dword [p.p_AdrLabel]
LabelBack:
;Some other code
ProcedureReturn
Label_000: : Debug "X = 000" : Goto LabelBack
Label_001: : Debug "X = 001" : Goto LabelBack
Label_002: : Debug "X = 002" : Goto LabelBack
Label_003: : Debug "X = 003" : Goto LabelBack
Label_004: : Debug "X = 004" : Goto LabelBack
Label_005: : Debug "X = 005" : Goto LabelBack
EndProcedure
Jump (3)
Now my questions:
- Is the ASM-code correct?
- Is the same ASM-code running on linux and osx too?
- Must 'dword ' be changed for x64?
Thxs
Josh
Re: Jumptable to labels
Posted: Thu Mar 16, 2017 8:35 pm
by Keya
my jump table example is for labels within a procedure, hope it helps -
http://www.purebasic.fr/english/viewtop ... 12&t=67044
- Is the same ASM-code running on linux and osx too?
you should get yourself a free copy of VirtualBox and a free copy of Linux Mint and try for yourself! I would never trust that asm code runs just by looking at it hehe. (if you're asking "can the same asm code run on both Windows and Linux/Mac", the answer is generally yes)
Must 'dword ' be changed for x64?
You can still use dword mods in x64, ie you can "mov dword [rax], $11223344", but that's modifying the
contents at an address - when dealing with the address directly ie JMP'ing to it yes that needs to be a qword on x64
Re: Jumptable to labels
Posted: Thu Mar 16, 2017 8:47 pm
by Josh
My ASM-Line was stolen from this topic
Thxs for your hints
Re: Jumptable to labels
Posted: Thu Mar 16, 2017 9:08 pm
by Keya
Josh wrote:But if I want to create a jumptable to labels, then I have, as far as I know, to use assambler.
btw there is also the GOTO statement, which is basically just a JMP.
Code: Select all
!int 3
Goto MyLabel
!nop
MyLabel:
!inc eax
Code: Select all
0040103F |. CD 03 int 3
00401041 |. EB 01 jmp short 00401044
00401043 | 90 nop
00401044 |> 40 inc eax
Some people argue dogmatically that you should never use GOTO statements (and yes you have to be careful when using them and yes there are usually but not necessarily always better methods), that's already been beaten to death at this and every other programming forum, but to them I say they should never use an app with a JMP instruction
#1stworldproblems
Re: Jumptable to labels
Posted: Thu Mar 16, 2017 9:19 pm
by Josh
Poaaahhhh, the old GOTO discussion. Sure, you can make mistakes, but that's the same with each other code.
In this case here, GOTO doesn't help me. Pb's GOTO needs a fix label but I need an address to be flexible.
Re: Jumptable to labels
Posted: Thu Mar 16, 2017 9:27 pm
by Keya
unfortunately yes. I've already feature-requested the ability to "GOTO <integer>", fairly simple one to implement but has probably already been lost/forgotten among the million other requests!
http://www.purebasic.fr/english/viewtop ... =3&t=67058
Re: Jumptable to labels
Posted: Thu Mar 16, 2017 9:35 pm
by Josh
Good idea. I voted for it.
Re: Jumptable to labels
Posted: Fri Mar 17, 2017 5:37 am
by idle
use enableasm and it'll work on x86 and x64
Code: Select all
Procedure Jump (x)
DataSection
DataSta:
Data.i ?Label_000
Data.i ?Label_001
Data.i ?Label_002
Data.i ?Label_003
Data.i ?Label_004
Data.i ?Label_005
EndDataSection
*AdrLabel = PeekI (?DataSta + x * SizeOf (Integer))
EnableASM
jmp *AdrLabel
DisableASM
LabelBack:
;Some other code
ProcedureReturn
Label_000: : Debug "X = 000" : Goto LabelBack
Label_001: : Debug "X = 001" : Goto LabelBack
Label_002: : Debug "X = 002" : Goto LabelBack
Label_003: : Debug "X = 003" : Goto LabelBack
Label_004: : Debug "X = 004" : Goto LabelBack
Label_005: : Debug "X = 005" : Goto LabelBack
EndProcedure
Jump (3)
Re: Jumptable to labels
Posted: Fri Mar 17, 2017 7:43 am
by davido
I don't know how relevant this is but I did the following two tests:
1.
idle's 'Label' code:
Code: Select all
Procedure Jump (x)
DataSection
DataSta:
Data.i ?Label_000
Data.i ?Label_001
Data.i ?Label_002
Data.i ?Label_003
Data.i ?Label_004
Data.i ?Label_005
EndDataSection
*AdrLabel = PeekI (?DataSta + x * SizeOf (Integer))
EnableASM
jmp *AdrLabel
DisableASM
LabelBack:
;Some other code
ProcedureReturn
Label_000: : Debug "X = 000" : Goto LabelBack
Label_001: : Debug "X = 001" : Goto LabelBack
Label_002: : Debug "X = 002" : Goto LabelBack
Label_003: : Debug "X = 003" : Goto LabelBack
Label_004: : Debug "X = 004" : Goto LabelBack
Label_005: : Debug "X = 005" : Goto LabelBack
EndProcedure
dt = ElapsedMilliseconds()
For M = 1 To 250000000
Jump (5)
Next M
MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")
2. Calling a Procedure:
Code: Select all
dt = ElapsedMilliseconds()
Procedure Test(x.i)
EndProcedure
dt = ElapsedMilliseconds()
For M = 1 To 250000000
Test(3)
Next M
MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")
Results (i7 5960; Windows 10; PureBasic 5.50 x64)
Labels: 250,000,000 in 1 second.
Procedure: 250,000,000 in 0.5 second.
Re: Jumptable to labels
Posted: Fri Mar 17, 2017 8:00 am
by Keya
davido, not a fair test because this is about one simple thing which isn't what you're testing for - replacing a Case or If table with a single jump. On top of that, all your Proc test does is call a Proc - but your Jumptable test calls a Proc, and then does work inside the proc - of course that's going to be slower!
ie. rather than...
Code: Select all
If val = #DO_ADD ;cmp
;je/jmp to add handler
ElseIf val = #DO_SUB ;cmp
;je/jmp to sub handler
ElseIf val = #DO_MUL ;cmp
;je/jmp to mul handler
... etc etc...
- there could be dozens, hundreds even, and every If/ElseIf statement must be tested individually. (And if for example you're doing this for every channel in every pixel in every image and you're processing multiple images it quickly adds up)
So the jump table does away with the multiple comparison tests, instead simply jmp'ing to exactly where it needs to go -- dozens of "cmp, je address" statements are reduced to a single "jmp address". And you don't need a timer to know that one "jmp address" is going to be faster than even a single "cmp + jmp"

Re: Jumptable to labels
Posted: Fri Mar 17, 2017 2:49 pm
by davido
@
Keya,
Thank you for the explanation.

Re: Jumptable to labels
Posted: Sat Mar 18, 2017 11:27 am
by Josh
This is the same code as in my first posting. The once difference, I set *AdrLabel to 'Static' and get an Error. With 'Define' and 'Protected' it works fine. Why?
Code: Select all
Procedure Jump (x)
Static *AdrLabel
DataSection
DataSta:
Data.i ?Label_000
Data.i ?Label_001
Data.i ?Label_002
Data.i ?Label_003
Data.i ?Label_004
Data.i ?Label_005
EndDataSection
*AdrLabel = PeekI (?DataSta + x * SizeOf (Integer))
!jmp dword [p.p_AdrLabel]
LabelBack:
;Some other code
ProcedureReturn
Label_000: : Debug "X = 000" : Goto LabelBack
Label_001: : Debug "X = 001" : Goto LabelBack
Label_002: : Debug "X = 002" : Goto LabelBack
Label_003: : Debug "X = 003" : Goto LabelBack
Label_004: : Debug "X = 004" : Goto LabelBack
Label_005: : Debug "X = 005" : Goto LabelBack
EndProcedure
Jump (3)
Re: Jumptable to labels
Posted: Sat Mar 18, 2017 12:31 pm
by mk-soft
Another way with virtual table
Is faster than 'goto'
Code: Select all
Prototype protoInvoke(*Args = 0)
Structure udtInvoke
*fc.protoInvoke[0]
EndStructure
Procedure Prog0(*Args)
Debug "Prog 0"
EndProcedure
Procedure Prog1(*Args)
Debug "Prog 1"
EndProcedure
Procedure Prog2(*Args)
Debug "Prog 2"
EndProcedure
Procedure Prog3(*Args)
Debug "Prog 3"
EndProcedure
Procedure Prog4(*Args)
Debug "Prog 4"
EndProcedure
DataSection
VT_Prog:
Data.i @Prog0()
Data.i @Prog1()
Data.i @Prog2()
Data.i @Prog3()
Data.i @Prog4()
EndDataSection
Global *Call.udtInvoke
*call = ?VT_Prog
*call\fc[1]()
index = 3
*call\fc[index]()
dt = ElapsedMilliseconds()
For M = 1 To 250000000
*call\fc[3]()
Next M
MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")
Re: Jumptable to labels
Posted: Sat Mar 18, 2017 2:27 pm
by Josh
mk-soft wrote:Another way with virtual table
Is faster than 'goto'
The problem is, I have some hundreds of possibilities, about 50% have to do nothing, about 48% have one command line and only very few have more than 5 command lines. So I don't want to create some hundreds procedures.
Btw, it isn't faster than 'goto'
Code: Select all
Prototype protoInvoke(*Args = 0)
Structure udtInvoke
*fc.protoInvoke[0]
EndStructure
Structure INTEGERARRAY
i.i[0]
EndStructure
Procedure Prog0(*Args)
Debug "Prog 0"
EndProcedure
Procedure Prog1(*Args)
Debug "Prog 1"
EndProcedure
Procedure Prog2(*Args)
Debug "Prog 2"
EndProcedure
Procedure Prog3(*Args)
Debug "Prog 3"
EndProcedure
Procedure Prog4(*Args)
Debug "Prog 4"
EndProcedure
DataSection
VT_Prog:
Data.i @Prog0()
Data.i @Prog1()
Data.i @Prog2()
Data.i @Prog3()
Data.i @Prog4()
VT_Label:
Data.i ?Label0
Data.i ?Label1
Data.i ?Label2
Data.i ?Label3
Data.i ?Label4
EndDataSection
Global *Call.udtInvoke
*call = ?VT_Prog
*call\fc[1]()
index = 3
*call\fc[index]()
dt = ElapsedMilliseconds()
For M = 1 To 250000000
*call\fc[3]()
Next M
MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")
; -------------------------------------
Define *Addresses.INTEGERARRAY
*Addresses = ?VT_Label
EnableASM
dt = ElapsedMilliseconds()
For M = 1 To 250000000
jmp *Addresses\i[4]
LabelRet:
Next M
MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")
DisableASM
; -------------------------------------
End
Label0: : Debug "Label 0" : Goto LabelRet
Label1: : Debug "Label 1" : Goto LabelRet
Label2: : Debug "Label 2" : Goto LabelRet
Label3: : Debug "Label 3" : Goto LabelRet
Label4: : Debug "Label 4" : Goto LabelRet
Re: Jumptable to labels
Posted: Sat Mar 18, 2017 2:59 pm
by mk-soft
Ok. Goto is a faster, but you need all labels too.
With VT can you defined the right functions.
Code: Select all
Prototype protoInvoke(*Args = 0)
Structure udtInvoke
*fc.protoInvoke[0]
EndStructure
Procedure ProgNothing(*Args)
Debug "Do Nothing"
EndProcedure
Procedure Prog0(*Args)
Debug "Prog 0"
EndProcedure
Procedure Prog1(*Args)
Debug "Prog 1"
EndProcedure
Procedure Prog5and6(*Args)
Debug "Prog 5 and 6"
EndProcedure
Procedure Prog7(*Args)
Debug "Prog 7"
EndProcedure
DataSection
VT_Prog:
Data.i @Prog0()
Data.i @Prog1()
Data.i @ProgNothing()
Data.i @ProgNothing()
Data.i @ProgNothing()
Data.i @Prog5and6()
Data.i @Prog5and6()
Data.i @Prog7()
EndDataSection
Global *Call.udtInvoke = ?VT_Prog
dt = ElapsedMilliseconds()
For M = 1 To 250000000
*call\fc[5]()
Next M
MessageRequester("Time: " + Str(ElapsedMilliseconds() - dt),"")
P.S.
I use the same method for my EventDesigner to manage all Events (EventMain.pb)