Faster than "Select...Case" (ON n GOSUB alternati

Share your advanced PureBasic knowledge/code with the community.
Randy Walker
Addict
Addict
Posts: 1064
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Faster than "Select...Case" (ON n GOSUB alternati

Post by Randy Walker »

[RE RE-EDITED FOR PUREBASIC VERSION 4.0 - 3/23/06]
Copy, paste, test and examine. It's pretty straight forward. Use as you wish.

Code: Select all

 ; See code below \/ 
Last edited by Randy Walker on Thu Mar 23, 2006 2:13 pm, edited 4 times in total.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
vanleth
User
User
Posts: 79
Joined: Sat Jun 28, 2003 4:39 am
Location: Denmark - Valby

Post by vanleth »

Hmm could be a debugger issue. Maybe the debugger does a ekstra safe check when meeting a Select statement.
Randy Walker
Addict
Addict
Posts: 1064
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Post by Randy Walker »

[RE RE-EDITED FOR PUREBASIC VERSION 4.0 - 3/23/06]
Based on your speculation, I extended the code and replaced debug using MessageRequestor() instead. The net results are the same because there is no list of if/then comparisons when using the CallFunctionFast() alternative. This is my point. The debugger has noting to do with the difference in speed. It is purely because I replaced the list of conditionnals with direct jumps according to value in c.w

Code: Select all

 ;/           YOUR FAMILIAR, STANDARD, EVERY DAY "SELECT / CASE" SAMPLE CODE
;  Going to test duration on "Select...Case Filtering" loop. 
capturedTime.l = ElapsedMilliseconds() 
For l.l = 1 To 100000      ; These two loops only here to demonstrate concept.
  For operation.w = 1 To 52   ; Using 52 operations here is arbitrary.
    
    Select operation    ; <<These "operation.l values" could also be random!!
      Case 1            ;  and same would apply to alternative method below.
        dummy.l = dummy.l & 5
      Case 2                          ;  Notice how ...
        dummy.l = dummy.l & 5   ;The
      Case 3 
        dummy.l = dummy.l & 5   ;more
      Case 4 
        dummy.l = dummy.l & 5   ;Cases
      Case 5 
        dummy.l = dummy.l & 5   ;there
      Case 6 
        dummy.l = dummy.l & 5   ;are
      Case 7 
        dummy.l = dummy.l & 5   ;the
      Case 8 
        dummy.l = dummy.l & 5   ;less
      Case 9 
        dummy.l = dummy.l & 5   ;efficient
      Case 10 
        dummy.l = dummy.l & 5   ;Select
      Case 11 
        dummy.l = dummy.l & 5   ;Case
      Case 12 
        dummy.l = dummy.l & 5 
      Case 13 
        dummy.l = dummy.l & 5   ; Select/Case is a "filtering system"
      Case 14                  ; that must fail numerous tests before
        dummy.l = dummy.l & 5   ; encountering matched values.  Only
      Case 15                  ; after numurous failures, your code
        dummy.l = dummy.l & 5   ; can then begin processing.
      Case 16 
        dummy.l = dummy.l & 5 
      Case 17 
        dummy.l = dummy.l & 5   ; The alternative "direct jump" method
      Case 18                   ; always points to the exact fucntion
        dummy.l = dummy.l & 5   ; without wasting any time "filtering".
      Case 19 
        dummy.l = dummy.l & 5 
      Case 20 
        dummy.l = dummy.l & 5   ; For sample purposes, the functions
      Case 21                   ; used in each "Case value" have been
        dummy.l = dummy.l & 5   ; kept identical (dummy=dummy & 5) to
      Case 22                   ; standardize comparisons.
        dummy.l = dummy.l & 5 
      Case 23 
        dummy.l = dummy.l & 5 
      Case 24 
        dummy.l = dummy.l & 5 
      Case 25 
        dummy.l = dummy.l & 5 
      Case 26 
        dummy.l = dummy.l & 5 
      Case 27
        dummy.l = dummy.l & 5 
      Case 28
        dummy.l = dummy.l & 5 
      Case 29
        dummy.l = dummy.l & 5 
      Case 30
        dummy.l = dummy.l & 5 
      Case 31
        dummy.l = dummy.l & 5 
      Case 32
        dummy.l = dummy.l & 5 
      Case 33
        dummy.l = dummy.l & 5 
      Case 34
        dummy.l = dummy.l & 5 
      Case 35
        dummy.l = dummy.l & 5 
      Case 36
        dummy.l = dummy.l & 5 
      Case 37
        dummy.l = dummy.l & 5 
      Case 38
        dummy.l = dummy.l & 5 
      Case 39
        dummy.l = dummy.l & 5 
      Case 40
        dummy.l = dummy.l & 5 
      Case 41
        dummy.l = dummy.l & 5 
      Case 42
        dummy.l = dummy.l & 5 
      Case 43
        dummy.l = dummy.l & 5 
      Case 44
        dummy.l = dummy.l & 5 
      Case 45
        dummy.l = dummy.l & 5 
      Case 46
        dummy.l = dummy.l & 5 
      Case 47
        dummy.l = dummy.l & 5 
      Case 48
        dummy.l = dummy.l & 5 
      Case 49
        dummy.l = dummy.l & 5 
      Case 50
        dummy.l = dummy.l & 5 
      Case 51
        dummy.l = dummy.l & 5 
      Case 52
        dummy.l = dummy.l & 5 
    EndSelect 
  Next operation
Next l 
MessageRequester("Select...Case Elaped Time",StrQ(ElapsedMilliseconds() - capturedTime.l)) 
;
;/         NOW THE ALTERNATIVE TO "SELECT / CASE FILTERING" AS SEEN ABOVE
;/           Initialize procedures that will correlate to "Case values"
; Array used to index what will be our "value dependant" procedure calls
Global Dim p.l(52)
Procedure test_a()     ;  CallFunctionFast() will be used later to call
  dummy.l = dummy.l & 5 ;  these procedures directly by numeric value.
EndProcedure           ;  This section equates 1 to 1 with the code above.
Procedure test_b() 
  dummy.l = dummy.l & 5  ; Procedures seen here are layed out and numbered
EndProcedure            ; sequencially to match "Select/Case" equivilants
Procedure test_c()      ; used in sample code above.
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_d() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_e() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_f() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_g() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_h() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_i() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_j() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_k() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_l() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_m() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_n() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_o() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_p() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_q() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_r() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_s() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_t() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_u() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_v() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_w() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_x() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_y() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_z() 
  dummy.l = dummy.l & 5 
EndProcedure 
Procedure test_aa()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ab()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ac()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ad()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ae()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_af()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ag()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ah()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ai()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_aj()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ak()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_al()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_am()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_an()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ao()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ap()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_aq()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ar()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_as()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_at()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_au()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_av()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_aw()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ax()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_ay()
  dummy.l = dummy.l & 5
EndProcedure 
Procedure test_az()
  dummy.l = dummy.l & 5
EndProcedure 
;/               ;  Initialize array for code demonstration below
p(1) = @test_a() ; Equivelent syntax under GFA_Basic for this operation 
p(2) = @test_b() ; would be as follows: 
p(3) = @test_c() ;   ON operation GOSUB proc1, proc2, proc3, proc4, ... 
p(4) = @test_d() ; 
p(5) = @test_e() ; Could just as well be:
p(6) = @test_f() ;   ON operation GOSUB left(), right(), bendover(), ...
p(7) = @test_g() ;   ON operation GOSUB test_a(), test_b(), test_c(), ...
p(8) = @test_h() ; 
p(9) = @test_i() ;  The idea being procedures are called by corresponding
p(10) = @test_j() ; position following the term "Gosub".  (1, 2, 3, ...)
p(11) = @test_k() ;
p(12) = @test_l() ; A comma delimnited list is not available in PureBasic
p(13) = @test_m() ; so instead we need to create our own index.  That's
p(14) = @test_n() ; why we set up the procedures above. Now we index.
p(15) = @test_o() ; 
p(16) = @test_p() ; The "@"procedure does the magic here by allowing each
p(17) = @test_q() ; procedure to be assigned a "Case value" according to
p(18) = @test_r() ; the procedure address in memory.
p(19) = @test_s() 
p(20) = @test_t() 
p(21) = @test_u() 
p(22) = @test_v() 
p(23) = @test_w() 
p(24) = @test_x() 
p(25) = @test_y() 
p(26) = @test_z() 
p(27) = @test_aa()
p(28) = @test_ab()
p(29) = @test_ac()
p(30) = @test_ad()
p(31) = @test_ae()
p(32) = @test_af()
p(33) = @test_ag()
p(34) = @test_ah()
p(35) = @test_ai()
p(36) = @test_aj()
p(37) = @test_ak()
p(38) = @test_al()
p(39) = @test_am()
p(40) = @test_an()
p(41) = @test_ao()
p(42) = @test_ap()
p(43) = @test_aq()
p(44) = @test_ar()
p(45) = @test_as()
p(46) = @test_at()
p(47) = @test_au()
p(48) = @test_av()
p(49) = @test_aw()
p(50) = @test_ax()
p(51) = @test_ay()
p(52) = @test_az()
; Test duration on "CallFunctionFast()" loop. 
capturedTime.l = ElapsedMilliseconds() 
For l.l = 1 To 100000  ; These two loops only to demonstrate concept.
  For operation.w = 1 To 52
    
    *a = p(operation) ; Any value inside p() "means" call operation directly!!!
    CallFunctionFast(*a)  ; << JUST DO IT -- NO FILTERING REQUIRED !!! 
    
  Next operation
Next l 
MessageRequester("CallFunctionFast() Elapsed Time",StrQ(ElapsedMilliseconds() - capturedTime.l)) 
Last edited by Randy Walker on Thu Mar 23, 2006 2:10 pm, edited 4 times in total.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
Randy Walker
Addict
Addict
Posts: 1064
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Post by Randy Walker »

Sorry I'm only a novice at programming so this is probably not the best code to demonstrate. Appearntly Windows has some mutlitask thing going on that may give false results on occasion. If you run the code a number of times you should see on the average, about 1 half the processing time when you hit the CallFunctionFast() section. Also, I am running on a P3 @ 600 Mhz so you may need to increase the loop values to see a difference.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

it may be faster but i consider it horrible coding, personal opinion etc :-)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Randy Walker
Addict
Addict
Posts: 1064
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Post by Randy Walker »

blueznl wrote:it may be faster but i consider it horrible coding, personal opinion etc :-)
Exactly why I submitted it as a requested function. As the comment noted in the code above. GFA accomplished this with a very simple command that made Select/Case look complicated. The code above only emulates the functionality in GFA and therefore, as you point out, is nothing close to ideal. Didn't you use GFA?
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
User avatar
Hades
Enthusiast
Enthusiast
Posts: 188
Joined: Tue May 17, 2005 8:39 pm

Post by Hades »

It really isn't that horrible.

For example, if you have to render different objects and can't do it with the same procedure, it's a bit like object oriented coding.
You could just write CallFunctionFast(Object\Render) instead of a long select / case list.
Much nicer :D :wink:
Randy Walker
Addict
Addict
Posts: 1064
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Post by Randy Walker »

Hades wrote: It really isn't that horrible ... it's a bit like object oriented coding.
Thanks Hades. (Uhh ... whatever object oriented coding is :? )

I think the horible part is in my sample presentation. It is not the best example in terms demonstrating possible applications. I tried to keep the code as inert as possible to demonstrate the timing advantage. In actuality, when placed side by side, the code is even shorter than the Select/Case alternative.

Here the Select/Case alternative occupies 58 lines of code:

Code: Select all

 For l.w = 1 To 10000 
  For c.w = 1 To 26 
    Select c.w 
      Case 1 
        test_a()  ;The 
      Case 2 
        test_b()  ;more 
      Case 3 
        test_c()  ;Cases 
      Case 4 
        test_d()  ;the 
      Case 5 
        test_e()  ;less 
      Case 6 
        test_f()  ;efficient 
      Case 7 
        test_g()  ;Select 
      Case 8 
        test_h()  ;Case 
      Case 9 
        test_i() 
      Case 10 
        test_j() 
      Case 11 
        test_k() 
      Case 12 
        test_l() 
      Case 13 
        test_m() 
      Case 14 
        test_n() 
      Case 15 
        test_o() 
      Case 16 
        test_p() 
      Case 17 
        test_q() 
      Case 18 
        test_r() 
      Case 19 
        test_s() 
      Case 20 
        test_t() 
      Case 21 
        test_u() 
      Case 22 
        test_v() 
      Case 23 
        test_w() 
      Case 24 
        test_x() 
      Case 25 
        test_y() 
      Case 26 
        test_z() 
    EndSelect 
  Next c 
Next l 
And the shorter, faster alternative occupies only 33 lines of code:

Code: Select all

 Dim p.l(26) ; Array used to index case dependant procedure calls using CallFunctionFast() 
p(1) = @test_a() ; Equivelent syntax under GFA_Basic for this operation 
p(2) = @test_b() ; would be as follows: 
p(3) = @test_c() ;   ON n GOSUB proc1, proc2, proc3, proc4, etc... 
p(4) = @test_d() ; 
p(5) = @test_e() ; The value "n" above points to comma delimnited list 
p(6) = @test_f() ;  following the ON n GOSUB command so that direct 
p(7) = @test_g() ; indexing by value is possible.  List items can be any 
p(8) = @test_h() ; order.  There is no "conditional" testing so processing 
p(9) = @test_i() ; time is less than the "Select/Case" 
p(10) = @test_j() 
p(11) = @test_k() 
p(12) = @test_l() 
p(13) = @test_m() 
p(14) = @test_n() 
p(15) = @test_o() 
p(16) = @test_p() 
p(17) = @test_q() 
p(18) = @test_r() 
p(19) = @test_s() 
p(20) = @test_t() 
p(21) = @test_u() 
p(22) = @test_v() 
p(23) = @test_w() 
p(24) = @test_x() 
p(25) = @test_y() 
p(26) = @test_z() 
For l.w = 1 To 10000 
  For c.w = 1 To 26 
    *a = p(c)        ; c.w is the index value for procedure call 
    CallFunctionFast(*a) 
  Next c 
Next l  
Better yet -- If we can get Fred to poke the procedure addresses into the comma delimited list below during compile, we could reduce all the above to only a single line, and the need for the index array is emilinated completely.

Code: Select all

 On c.w GoProc test_a(), test_b(), test_c(), test_d() ..., test_x(), test_y(), test_z()  
Of course he may prefer diferent syntax/command structure, but ... Wouldn't that be sweet??!!
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

ah okay, now i see what you mean, yeah, in certain situations it may help indeed, yes

(and yeah, i was heavily into gfabasic indeed :-))

on n goproc ... or on n call ... would be fine i guess...
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Randy Walker
Addict
Addict
Posts: 1064
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Post by Randy Walker »

blueznl wrote: ... now i see what you mean, yeah, in certain situations it may help indeed, yes ...
Thank you blueznl. I've come to respect your talents and contributions here rather highly so you were really making me question myself on this.

Now I just hope that Fred can justify the advantage and find a way to implement some equivelent command for us. Select/Case is powerfull and has it's place, but in some instances it can be a bit primitive.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

blushing :oops:

reason why i saw the light is stuff like (totally wrong example :-))

Code: Select all

on direction call goleft(), goright(), goup(), godown()
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Post Reply