Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

test speed on 2 variations of code

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • test speed on 2 variations of code

    without David Roberts, John Gleason and i think Paul Dixon may of had a hand in the timing code too, this program would not of ever been made.
    it makes use of their timing routines to report speed times of code
    thanks guys.

    a program to test 2 pieces of code using multiple loops, multiple samples, and multiple runs
    averaged times for each set of samples is displayed
    removing of skewed results would have been better, but i took the easy way and only used averages.

    this program is very helpful when code written is to be use many times and where deciding on the fastest code.

    the demo below compares the FORMAT$ to basic string building to create a returned string from a date routine(function).
    the results would show that using the FORMAT$ statement is much slower, so for a date routine that is usually called many times in a program, FORMAT$ would not be the code to use as opposed to simply building a string.

    this program is helping me to find the fastest code in programs that may call a function or repeat a line of code over may times, like 1,000,000 times, which is not unusual, or code in a dll that is used often.


    Code:
    'timermast.bas
    'pbcc 4.04
    '
    #COMPILE EXE
    #DIM ALL
    #REGISTER NONE
    GLOBAL glloopsinasample AS LONG
    GLOBAL glsamples AS LONG
    GLOBAL glrunstomake AS LONG
    GLOBAL TOTALTIME AS SINGLE
    
    DECLARE FUNCTION QueryPerformanceCounter LIB "KERNEL32.DLL" ALIAS "QueryPerformanceCounter" (lpPerformanceCount AS QUAD) AS LONG
    DECLARE FUNCTION QueryPerformanceFrequency LIB "KERNEL32.DLL" ALIAS "QueryPerformanceFrequency" (lpFrequency AS QUAD) AS LONG
    
    '~~~~~~~~~~~A Variation of Dave Roberts' MACRO Timer~~~~~~~~~~~~~~~~~~~~~~~
    MACRO onTimer
      LOCAL qFreq, qOverhead, qStart, qStop AS QUAD
      LOCAL f AS STRING
      f = "#.###"
      QueryPerformanceFrequency qFreq
      QueryPerformanceCounter qStart ' Intel suggestion. First use may be suspect
      QueryPerformanceCounter qStart ' So, wack it twice <smile>
      QueryPerformanceCounter qStop
      qOverhead = qStop - qStart     ' Relatively small
    END MACRO
    MACRO goTimer = QueryPerformanceCounter qStart
    MACRO stopTimer = QueryPerformanceCounter qStop
    MACRO showTimer = USING$(f,(qStop - qStart - qOverhead)*1000000/qFreq /1000) + " milliseconds"
    
    '====================================================================================================
    'EDIT HERE
    'HERE IS WHERE YOU BASICALLY SET UP YOUR VARIABLES AND CODE
    FUNCTION initiateloopsvalues() AS LONG
    glloopsinasample=100'0000 'no of loops in a sample
    glsamples=100              'no of samples in a test, the higher the better
    glrunstomake=3            'no of test to make, at least make it 2 for better results
    END FUNCTION
    
    
    FUNCTION testcode1(temp AS LONG) AS STRING
    LOCAL  m,d,y AS LONG
    testcode1=FORMAT$(M,"00") + "/" + FORMAT$(D,"00") + "/" + FORMAT$(Y,"0000")
    END FUNCTION
    
    FUNCTION testcode2(temp AS LONG) AS STRING
    LOCAL  m,d,y AS LONG
    testcode2=RIGHT$("0"+TRIM$(STR$(M)),2)+"/"+RIGHT$("0"+TRIM$(STR$(D)),2)+ "/" +RIGHT$("000"+TRIM$(STR$(Y)),4)
    END FUNCTION
    '====================================================================================================
    
    
    FUNCTION PBMAIN () AS LONG
    REGISTER I AS LONG
    REGISTER J AS LONG
    LOCAL K AS LONG
    initiateloopsvalues
    onTimer
    K=0
    STARTARUN:
    INCR K
    TOTALTIME=0.0
    J=0&
    START1:
    INCR J
    gotimer
    FOR I=1& TO glloopsinasample
    '=========================================================
    'call to the code tested first goes here
    'EDIT HERE
    testcode1(0)
    '=========================================================
    NEXT I
    stoptimer
    TOTALTIME=TOTALTIME+(qStop - qStart - qOverhead)*1000000/qFreq/1000
    SLEEP 0
    IF J<glsamples THEN GOTO START1
    STDOUT "-------------------------";
    STDOUT "code #1 no of samples"+STR$(glsamples)+" run no"+STR$(K)
    STDOUT USING$(f,(TOTALTIME/glsamples)) + " milliseconds"
    TOTALTIME=0.0
    J=0&
    START2:
    INCR J
    gotimer
    FOR I=1& TO glloopsinasample
    '=========================================================
    'call to the code tested second goes here
    'EDIT HERE
    testcode2(0)
    '=========================================================
    NEXT I
    stoptimer
    TOTALTIME=TOTALTIME+(qStop - qStart - qOverhead)*1000000/qFreq/1000
    SLEEP 0
    IF J<glsamples THEN GOTO START2
    STDOUT "-------------------------";
    STDOUT "code #2 no of samples"+STR$(glsamples)+" run no"+STR$(K)
    STDOUT USING$(f,(TOTALTIME/glsamples)) + " milliseconds"
    IF K<glrunstomake THEN GOTO STARTARUN
    
    WAITKEY$
    END FUNCTION
    Last edited by Paul Purvis; 19 Apr 2008, 11:28 AM.
    p purvis

  • #2
    95% confidence limits added to the above code.

    To avoid incorporating statistical tables the number of samples has been 'hard wired' at 60.

    We can now say that given any number of tests with 60 samples then 95% of the results will fall within the determined range.

    Had we used less than 60 then our confidence would wane and the range would broaden. Conversely, more than 60 would boost our confidence and the range would tighten.

    60 is a good number as the range does not tighten much further as the number of samples tends to infinity.

    'glrunstomake' has been removed - no need for it. An inner loop run sample number of times is all we need.

    I'm not keen on 'Sleep 0' between samples - smacks of synchronization - so 'Sleep Rnd(2,10)' used.

    Paul's style of coding differs to mine - I prefer a bit more white space separating code logic and indentation is used twix gotimer and stoptimer as if they were a structured command pair.

    '1000000/qFreq/1000' changed to '1000/qFreq' on the grounds that it has been 'doing my head in' since the first time I saw it [Mr Gleason ].

    With Paul's examples it is obvious which code to buy. With other examples where one average is less than another then if the upper bound limit of the lesser average exceeds the lower bound of the greater average then the lower average code is not as good as we thought it was and if the greater average is more readable then it may be the better buy.

    Code:
    'timermast.bas
    'pbcc 4.04
    '
    #COMPILE EXE
    #DIM ALL
    #REGISTER NONE
    
    GLOBAL glloopsinasample    AS LONG
    GLOBAL glsamples         AS LONG
    GLOBAL TOTALTIME         AS SINGLE
    
    DECLARE FUNCTION QueryPerformanceCounter LIB "KERNEL32.DLL" ALIAS "QueryPerformanceCounter" ( lpPerformanceCount AS QUAD ) AS LONG
    DECLARE FUNCTION QueryPerformanceFrequency LIB "KERNEL32.DLL" ALIAS "QueryPerformanceFrequency" ( lpFrequency AS QUAD ) AS LONG
    
    '~~~~~~~~~~~A Variation of Dave Roberts' MACRO Timer~~~~~~~~~~~~~~~~~~~~~~~
    MACRO onTimer
    LOCAL qFreq, qOverhead, qStart, qStop AS QUAD
    LOCAL f     AS STRING
    
      f = "#.###"
      QueryPerformanceFrequency qFreq
      QueryPerformanceCounter qStart ' Intel suggestion. First use may be suspect
      QueryPerformanceCounter qStart ' So, wack it twice <smile>
      QueryPerformanceCounter qStop
      qOverhead = qStop - qStart ' Relatively small
    END MACRO
    MACRO goTimer = QueryPerformanceCounter qStart
    MACRO stopTimer = QueryPerformanceCounter qStop
    MACRO showTimer = USING$( f, ( qStop - qStart - qOverhead ) * 1000000 / qFreq / 1000 ) + " milliseconds"
    
    '====================================================================================================
    DECLARE FUNCTION ConfidenceLimits(x() AS SINGLE) AS STRING
    
    'EDIT HERE
    'HERE IS WHERE YOU BASICALLY SET UP YOUR VARIABLES AND CODE
    FUNCTION initiateloopsvalues( ) AS LONG
      glloopsinasample = 1000 '0000 'no of loops in a sample
      glsamples = 60 'no of samples in a test, the higher the better [ Nope, 60 is just fine. DR ]
    END FUNCTION
    
    FUNCTION testcode1( temp AS LONG ) AS STRING
    LOCAL m, d, y AS LONG
      testcode1 = FORMAT$( M, "00" ) + "/" + FORMAT$( D, "00" ) + "/" + FORMAT$( Y, "0000" )
    
    END FUNCTION
    
    FUNCTION testcode2( temp AS LONG ) AS STRING
    LOCAL m, d, y AS LONG
      testcode2 = RIGHT$( "0" + TRIM$( STR$( M ) ), 2 ) + "/" + RIGHT$( "0" + TRIM$( STR$( D ) ), 2 ) + "/" + RIGHT$( "000" + TRIM$( STR$( Y ) ), 4 )
    
    END FUNCTION
    
    '====================================================================================================
    
    FUNCTION PBMAIN ( ) AS LONG
      REGISTER I AS LONG
      REGISTER J AS LONG
      DIM x( 1 TO 60) AS SINGLE
    
      RANDOMIZE
    
      initiateloopsvalues
    
      onTimer
      TOTALTIME = 0.0 ' We could remove as average available in ConfidenceLimits' xbar
      J = 0&
    START1:
      INCR J
    
      gotimer
        FOR I = 1& TO glloopsinasample
    
          '=========================================================
          'call to the code tested first goes here
          'EDIT HERE
          testcode1( 0 )
    
        '=========================================================
    
        NEXT I
      stoptimer
    
      x(j) = ( qStop - qStart - qOverhead ) * 1000/qFreq
      TOTALTIME = TOTALTIME + x(j)
      SLEEP RND(2,10)
      IF J < glsamples THEN GOTO START1
    
      STDOUT "-------------------------";
      STDOUT "code #1 no of samples" + STR$( glsamples )
      STDOUT ConfidenceLimits(x())
      STDOUT USING$( f, ( TOTALTIME / glsamples ) ) + " milliseconds"
      TOTALTIME = 0.0
      J = 0&
      RESET x()
    START2:
      INCR J
    
      gotimer
        FOR I = 1& TO glloopsinasample
    
          '=========================================================
          'call to the code tested second goes here
          'EDIT HERE
          testcode2( 0 )
    
        '=========================================================
    
        NEXT I
      stoptimer
    
      x(j) = ( qStop - qStart - qOverhead ) * 1000/qFreq
      TOTALTIME = TOTALTIME + x(j)
      SLEEP RND(2,10)
      IF J < glsamples THEN GOTO START2
    
      STDOUT "-------------------------";
      STDOUT "code #2 no of samples" + STR$( glsamples )
      STDOUT ConfidenceLimits(x())
      STDOUT USING$( f, ( TOTALTIME / glsamples ) ) + " milliseconds"
    
    
      WAITKEY$
    
    END FUNCTION
    
    FUNCTION ConfidenceLimits( x() AS SINGLE ) AS STRING
      ' Input: x( lb to ub )
      ' Output: Empty string if 60 samples not adhered to
      '         Otherwise "[95%: range]"
    
      LOCAL lb, ub, i AS LONG
      LOCAL sigmaX, sigmaX2, s, xbar, delta, lower, upper AS SINGLE
    
      lb = LBOUND(x) : ub = UBOUND(x)
    
      IF ub - lb + 1 <> 60 THEN
        stdout "I need exactly 60 samples"
        EXIT FUNCTION
      END IF
    
      FOR i = lb TO ub
        sigmaX = sigmaX + x(i)
        sigmaX2 = sigmaX2 + x(i)*x(i)
      NEXT
    
      xbar = sigmaX/60
      s = SQR(sigmaX2/60 - xbar * xbar)
    
      delta = s*0.2581989
      lower = xbar - delta
      upper = xbar + delta
    
      FUNCTION = "[ 95%: " + FORMAT$(lower,"#####.###") + ", " + FORMAT$(upper, "#####.###") + " ]"
    
    END FUNCTION
    Last edited by David Roberts; 20 Apr 2008, 04:34 PM. Reason: Mentioned TOTALTIME could be removed

    Comment

    Working...
    X