On Gosub Solution to replace Select/Case conditionals

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

On Gosub Solution to replace Select/Case conditionals

Post by Randy Walker »

I made a similar post a long time ago but could not find it so posting again.
This post demonstrates the speed advantage using CallFunctionFast() instead of Select/Case.
Both "Huge" loops effectively accomplish the same thing. Granted Select/Case is much simpler but... Compare times.

Code: Select all

 ;/           YOUR FAMILIAR, STANDARD, EVERY DAY "SELECT / CASE" SAMPLE CODE
 ;  Going to test duration on "Select...Case Filtering" loop.
 Global *a
capturedTime.i = ElapsedMilliseconds()
For l.i = 1 To 100000      ; Huge number of iterations to demonstrate concept.
  For operation.w = 1 To 52   ; Using 52 operations here is arbitrary.
    
    Select operation    ; <<These "operation.i values" could also be random!!
      Case 1            ;  and same would apply to alternative method below.
        dummy.i = dummy.i & 5
      Case 2                          ;  Notice how ...
        dummy.i = dummy.i & 5   ;The
      Case 3
        dummy.i = dummy.i & 5   ;more
      Case 4
        dummy.i = dummy.i & 5   ;Cases
      Case 5
        dummy.i = dummy.i & 5   ;there
      Case 6
        dummy.i = dummy.i & 5   ;are
      Case 7
        dummy.i = dummy.i & 5   ;the
      Case 8
        dummy.i = dummy.i & 5   ;less
      Case 9
        dummy.i = dummy.i & 5   ;efficient
      Case 10
        dummy.i = dummy.i & 5   ;Select
      Case 11
        dummy.i = dummy.i & 5   ;Case
      Case 12
        dummy.i = dummy.i & 5
      Case 13
        dummy.i = dummy.i & 5   ; Select/Case is a "filtering system"
      Case 14                  ; that must fail numerous tests before
        dummy.i = dummy.i & 5   ; encountering matched values.  Only
      Case 15                  ; after numurous failures, your code
        dummy.i = dummy.i & 5   ; can then begin processing.
      Case 16
        dummy.i = dummy.i & 5
      Case 17
        dummy.i = dummy.i & 5   ; The alternative "direct jump" method
      Case 18                   ; always points to the exact fucntion
        dummy.i = dummy.i & 5   ; without wasting any time "filtering".
      Case 19
        dummy.i = dummy.i & 5
      Case 20
        dummy.i = dummy.i & 5   ; For sample purposes, the functions
      Case 21                   ; used in each "Case value" have been
        dummy.i = dummy.i & 5   ; kept identical (dummy=dummy & 5) to
      Case 22                   ; standardize comparisons.
        dummy.i = dummy.i & 5
      Case 23
        dummy.i = dummy.i & 5
      Case 24
        dummy.i = dummy.i & 5
      Case 25
        dummy.i = dummy.i & 5
      Case 26
        dummy.i = dummy.i & 5
      Case 27
        dummy.i = dummy.i & 5
      Case 28
        dummy.i = dummy.i & 5
      Case 29
        dummy.i = dummy.i & 5
      Case 30
        dummy.i = dummy.i & 5
      Case 31
        dummy.i = dummy.i & 5
      Case 32
        dummy.i = dummy.i & 5
      Case 33
        dummy.i = dummy.i & 5
      Case 34
        dummy.i = dummy.i & 5
      Case 35
        dummy.i = dummy.i & 5
      Case 36
        dummy.i = dummy.i & 5
      Case 37
        dummy.i = dummy.i & 5
      Case 38
        dummy.i = dummy.i & 5
      Case 39
        dummy.i = dummy.i & 5
      Case 40
        dummy.i = dummy.i & 5
      Case 41
        dummy.i = dummy.i & 5
      Case 42
        dummy.i = dummy.i & 5
      Case 43
        dummy.i = dummy.i & 5
      Case 44
        dummy.i = dummy.i & 5
      Case 45
        dummy.i = dummy.i & 5
      Case 46
        dummy.i = dummy.i & 5
      Case 47
        dummy.i = dummy.i & 5
      Case 48
        dummy.i = dummy.i & 5
      Case 49
        dummy.i = dummy.i & 5
      Case 50
        dummy.i = dummy.i & 5
      Case 51
        dummy.i = dummy.i & 5
      Case 52
        dummy.i = dummy.i & 5
    EndSelect
  Next operation
Next l
MessageRequester("Select...Case Elaped Time",Str(ElapsedMilliseconds() - capturedTime.i))
;
;/         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.i(52)
Procedure test_a()     ;  CallFunctionFast() will be used later to call
  dummy.i = dummy.i & 5 ;  these procedures directly by numeric value.
EndProcedure           ;  This section equates 1 to 1 with the code above.
Procedure test_b()
  dummy.i = dummy.i & 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.i = dummy.i & 5
EndProcedure
Procedure test_d()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_e()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_f()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_g()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_h()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_i()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_j()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_k()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_l()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_m()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_n()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_o()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_p()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_q()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_r()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_s()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_t()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_u()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_v()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_w()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_x()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_y()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_z()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_aa()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ab()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ac()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ad()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ae()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_af()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ag()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ah()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ai()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_aj()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ak()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_al()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_am()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_an()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ao()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ap()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_aq()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ar()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_as()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_at()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_au()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_av()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_aw()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ax()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_ay()
  dummy.i = dummy.i & 5
EndProcedure
Procedure test_az()
  dummy.i = dummy.i & 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.i = ElapsedMilliseconds()
Macro OnGosub
  CallFunctionFast(*a) ;NOTE: the *a variable must be global
EndMacro
For l.i = 1 To 100000  ; Huge number of iterations to demonstrate concept.
  For operation.w = 1 To 52
    
    *a = p(1) ; Any value inside p() "means" call operation directly!!!
    OnGosub ;NOTE: the *a variable must be global
    ;CallFunctionFast(*a)  ; << JUST DO IT -- NO FILTERING REQUIRED !!!
    
  Next operation
Next l
MessageRequester("CallFunctionFast() Elapsed Time",Str(ElapsedMilliseconds() - capturedTime.i))
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
User avatar
STARGÅTE
Addict
Addict
Posts: 2260
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: On Gosub Solution to replace Select/Case conditionals

Post by STARGÅTE »

As it is written in the documentation, "CallFunctionFast is not very flexible and does not handle string/float/double/quad parameters or string/float/double/quad returns. The use of prototypes is now strongly recommended."

Here is the recommended version:

Code: Select all

; Define all your cases as procedures

Procedure Case_0(Double.d, String.s)
	Debug "Case 0: "+StrD(Double)+", "+String
EndProcedure

Procedure Case_1(Double.d, String.s)
	Debug "Case 1: "+StrD(Double)+", "+String
EndProcedure

Procedure Case_2(Double.d, String.s)
	Debug "Case 2: "+StrD(Double)+", "+String
EndProcedure

; Define a prototype of all cases and a structure containing the addresses.

Prototype UseCase(Double.d, String.s)

Structure AllCases
	UseCase.UseCase[0]
EndStructure

; Define a data section with all procedure addresses and define the caller.

DataSection
	ListOfCases:
	Data.i @Case_0(), @Case_1(), @Case_2()
EndDataSection

Define *AllCases.AllCases = ?ListOfCases

; Here is the example of calling a specific case.

Define I.i
For I = 0 To 2
	*AllCases\UseCase[I](3.1415, "Hello")
Next
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
Randy Walker
Addict
Addict
Posts: 1109
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Re: On Gosub Solution to replace Select/Case conditionals

Post by Randy Walker »

Way over my head STARGATE.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
Post Reply