Announcement

Collapse
No announcement yet.

VAL

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

  • VAL

    After parsing text I get numbers formatted like:
    $2,279.59
    and
    (212) 718-1288
    Which need to be turned into a LONG and QUAD respectivly.


    From PB help:
    >VAL stops analyzing string_expression when non-numeric characters are encountered...
    Oh darn.

    So I have been using:

    sNumRem = "+?/\()$*%, " ' Stuff to remove from numbers
    MyLong = VAL( REMOVE$( sNum, ANY sNumRem ) )

    This works but is not clock cycle friendly but does resolve
    floating point numbers by rounding them.

    checking poffs I do not find any Nifty ASM contribution from the
    usual suspects so here is my contribution:

    Code:
    #COMPILE EXE "Val2.exe" ' 
    #DIM ALL 
                         
    
    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'         
    SUB time_stamp_count(tick AS QUAD) ' CPU Clock count    Charles E V Pegge
    
      '---------------------------'
      '                           ' approx because it is not a serialised instruction
      '                           ' it may execute before or after other instructions
      '                           ' in the pipeline.
      ! mov ebx,tick              ' var address where count is to be stored.
      ! db  &h0f,&h31             ' RDTSC read time-stamp counter into edx:eax hi lo.
      ! mov [ebx],eax             ' save low order 4 bytes.
      ! mov [ebx+4],edx           ' save high order 4 bytes.
      '---------------------------'  
    
    END SUB
    
        
    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤' 
    FUNCTION Val2( sNum AS STRING ) AS LONG  ' Approx 2500 clock cycles
            
      LOCAL i, j, StrLen, NegFlag, Power, RetVal AS LONG          
      LOCAL a, b AS BYTE PTR  
      LOCAL sBuff AS STRING  
             
        StrLen = LEN(sNum)                
        sBuff = SPACE$(StrLen) ' make an empty buffer
                       
        a = STRPTR(sNum)
        b = STRPTR(sBuff) 
        j = 0
        FOR i = 0 TO StrLen-1
          SELECT CASE @a[i]
            CASE 48 TO 57 ' 0 thru 9
              @b[j] = @a[i] ' xfer the number to the buffer
              INCR j
    
            CASE 46 ' Decimal point
              EXIT FOR ' truncate floating point number (need a better solution)
                   
            CASE 45 ' Minus sign 
              NegFlag = 1 ' change LSB of long?
    
            CASE ELSE
    
          END SELECT  
        NEXT i ' MSGBOX STR$(j),64,sBuff
    
        FOR i = 0 TO j-1 
          Power = j-i-1 
          RetVal = RetVal + (@b[i]-48)*10^Power
        NEXT
    
      FUNCTION = RetVal
    
    
    END FUNCTION
    
    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'          
    FUNCTION PBMAIN( )
             
      LOCAL cBeg, cEnd AS QUAD  ' for time stamp, measuring cpu clock cycles
      LOCAL i, RetVal, nLoops AS LONG 
      LOCAL sNumRem AS STRING      
                      
    
        sNumRem  = "+?/\()$*%, " ' Stuff to remove from numbers
           
        time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks 
    
          nLoops = 1000
          FOR i = 1 TO nLoops
            RetVal = VAL( REMOVE$( "$2,279.59", ANY sNumRem ) ) ' 5600 clock cycles
          NEXT
    
        time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
    
        MSGBOX "Total Processor Cycles="+STR$( (cEnd-cBeg)\nLoops ),64,"VAL =" + STR$(RetVal)
        '=======================       
    
    
             
        time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks 
    
          nLoops = 1000
          FOR i = 1 TO nLoops
            RetVal = Val2("$2,279.59") ' Approx 2500 clock cycles
          NEXT
    
        time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
    
        MSGBOX "Total Processor Cycles="+STR$( (cEnd-cBeg)\nLoops ),64,"VAL2 =" + STR$(RetVal)
    
    
    END FUNCTION
    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'

    I would like to find a fast method in PB first, and THEN convert it ASM perhaps.

    Comments?



    [This message has been edited by Mike Trader (edited July 26, 2007).]

  • #2
    Mike --

    > Comments?

    FWIW, there are lots of other non-numeric characters to consider. Try...

    VAL("9D9")
    VAL("9E9")
    VAL("&o22")
    VAL("&hf")

    Character position is also important. You are removing "+" so "2+2" will become "22", whereas VAL will correctly interpret it as "2". (If you want 4 you'll need a lot more code.)

    You might consider asking the operating system which characters are used for decimal and thousands-separator. Many countries reverse the USA convention.

    Personally, I prefer to use functions that are fine-tuned for specific types of strings, like FUNCTION DollarVal for your particular situation. All it would have to handle would be "$.,"

    -- Eric

    [This message has been edited by Eric Pearson (edited July 26, 2007).]
    "Not my circus, not my monkeys."

    Comment


    • #3
      Point taken.

      What I want is a very fast function or perhaps set of functions
      that do a very specific task. I do not want to write something
      that can handle all these possabilites.

      I just need to remove no chars and make a number (mostly and integer number)

      About the only complication is the "-" sign. It can appear as a
      seperator in a phone number but also as an indication of -ve
      value. I guess if it is not in front of the first number in the
      final string it can be ignored.

      ------------------
      Kind Regards
      Mike

      Comment


      • #4
        Mike,
        without checking it I'd guess that the bottleneck in your code is this part:
        Code:
        FOR i = 0 TO j-1       
        Power = j-i-1       
        RetVal = RetVal + (@b[i]-48)*10^Power
        NEXT
        Specifically, you don't want to use 10^anything in a loop if you want speed. It's a very slow operation.

        You'd be better off either using the built in VAL() function at that point or re-working your loop so you only multpily by 10 and not 10^Power.
        e.g.something like (untested):
        Code:
        FOR i = 0 TO j-1     
        RetVal = RetVal*10 + (@b[i]-48)
        NEXT
        Paul.

        ------------------

        Comment


        • #5
          Mike,
          having now had a quick test:
          Do as I suggest above and avoid the 10^power.

          Don't pass a literal string to the function, instead assign the literal value to a variable and pass that variable to the function. e.g.
          Replace
          Code:
          RetVal = Val2("$2,279.59")
          with
          Code:
          MyValue$="$2,279.59"
          	..
          	RetVal = Val2(MyValue$)
          Change your
          Code:
          SELECT CASE  @a[i]
          to
          Code:
          SELECT CASE  AS LONG @a[i]

          As your Val2 routine is quite short, consider making a macro of it so it's run inline.

          Paul.

          ------------------

          Comment


          • #6
            Mike,

            I've written several routines to do this type of thing over the
            years and the place to start is with RETAIN$

            I& = VAL(RETAIN$(V$, ANY "+-.0123456789"))

            Of course, when you're working with zip codes, phone numbers,
            SSAN, ISBN, and other such numbers that have specific characters
            alloted for their use it is much easier to trap input errors.

            Naturally, however, it is best to create an environment where the
            user is unable to enter characters that are not allowed. When I ask
            for a phone# or zip code I first unformat the string to only the 10
            digits then re-format it when the user exits the field.

            ------------------
            --
            C'ya
            Don
            don at DASoftVSS dot com
            http://www.DASoftVSS.com
            I program better than
            those who program faster
            and faster than those who program better.
            C'ya
            Don

            http://www.ImagesBy.me

            Comment


            • #7
              'Think pennies and only 0123456789- are valid.
              'Only made a couple changes to your code.
              Code:
              FUNCTION Pennies(sNum AS STRING) AS LONG
                LOCAL StrLen AS LONG
                LOCAL sBuff AS STRING
                LOCAL a AS BYTE PTR
                LOCAL b AS BYTE PTR
                LOCAL j AS LONG
                LOCAL i AS LONG
                LOCAL c AS LONG
                LOCAL NegFlag AS LONG
              
                StrLen = LEN(sNum)
                sBuff = SPACE$(StrLen)
                a = STRPTR(sNum)
                b = STRPTR(sBuff)
                j = 0
                FOR i = 0 TO StrLen-1
                  c = @a[i]
                  IF c > 47 AND c < 58 THEN
                      '@b[j] = @a[i]
                      @b[j] = c '@a[i]
                      INCR j
                   ELSEIF c = 45 THEN
                      NegFlag = -1
                   END IF
                NEXT i
                IF NegFlag = 0 THEN
                  FUNCTION = VAL(sBuff)
                ELSE
                  FUNCTION = VAL(sBuff) * NegFlag
                END IF
              END FUNCTION



              [This message has been edited by Mike Doty (edited July 26, 2007).]

              Comment


              • #8
                Wow this is down to under 370 clock cycles from all most 5200!

                Thx for the sugestions guys

                I have used most of them and this is the winning code:

                Code:
                #COMPILE EXE "Val2.exe" ' 
                #DIM ALL 
                                     
                
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'         
                SUB time_stamp_count(tick AS QUAD) ' CPU Clock count    Charles E V Pegge
                
                  '---------------------------'
                  '                           ' approx because it is not a serialised instruction
                  '                           ' it may execute before or after other instructions
                  '                           ' in the pipeline.
                  ! mov ebx,tick              ' var address where count is to be stored.
                  ! db  &h0f,&h31             ' RDTSC read time-stamp counter into edx:eax hi lo.
                  ! mov [ebx],eax             ' save low order 4 bytes.
                  ! mov [ebx+4],edx           ' save high order 4 bytes.
                  '---------------------------'  
                
                END SUB
                
                    
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤' 
                FUNCTION Val2( sNum AS STRING) AS LONG
                                 
                  LOCAL i, NegFlag, Sum AS LONG          
                  LOCAL a AS BYTE PTR               
                  
                    a = STRPTR(sNum)
                           
                    NegFlag = 1
                
                    FOR i = 0 TO LEN(sNum)
                      SELECT CASE AS LONG @a[i] ' slightly faster than IF ELSE
                        CASE 48 TO 57 ' 0 - 9
                          sum = sum*10 + @a[i] - 48 
                 
                       CASE 45 ' minus sign
                          IF Sum = 0 THEN NegFlag = -1  ' first - before number
                 
                       CASE 46 ' decimal point
                          EXIT FOR
                
                      END SELECT
                    NEXT i  
                           
                FUNCTION = Sum * NegFlag
                             
                END FUNCTION
                
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'          
                FUNCTION PBMAIN( )
                         
                  LOCAL cBeg, cEnd AS QUAD  ' for time stamp, measuring cpu clock cycles
                  LOCAL k, RetVal, nLoops AS LONG 
                  LOCAL sNumRem, sNum, s AS STRING      
                                        
                
                
                    sNum = "$-2,279.59"
                
                    sNumRem  = "+?/\()$*%, " ' Stuff to remove from numbers
                       
                    time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks 
                
                      nLoops = 1000
                      FOR k = 1 TO nLoops
                        RetVal = VAL( REMOVE$( sNum, ANY sNumRem ) ) ' Approx 5200 clock cycles
                      NEXT
                
                    time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                
                    s = s + "VAL =" + STR$(RetVal) + ",   Clock Cycles="+STR$( (cEnd-cBeg)\nLoops ) + $CRLF + $CRLF
                    '=======================       
                
                
                         
                    time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks 
                                 
                      nLoops = 1000
                      FOR k = 1 TO nLoops  
                        RetVal = Val2(sNum)  ' Approx 370 clock cycles
                      NEXT
                
                    time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                
                    s = s + "VAL2 =" + STR$(RetVal) + ",  Clock Cycles="+STR$( (cEnd-cBeg)\nLoops )
                    '=======================   
                         
                    MSGBOX s
                
                
                END FUNCTION
                '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                Obviously it truncates floats to make a long.

                Now the next step would be to make a MACRO out of it.
                I did not have much luck with that, but I still do not understand
                when to use MACRO TEMP for the vars.
                It seems to me I should just pick obscure var names. That way
                whatever function I use the MACRO in will just get a few more
                variables declared.

                After that conversion to ASM...

                ------------------
                Kind Regards
                Mike



                [This message has been edited by Mike Trader (edited July 27, 2007).]

                Comment


                • #9
                  Code:
                  'This is faster than the routine you compared to
                  'INSTR alone takes longer than your function!
                  FUNCTION MakeWhole(sNum AS STRING) AS LONG
                     LOCAL i AS LONG
                     i = INSTR(sNum,".")
                     IF i THEN
                       sNum = LEFT$(sNum,i-1)
                     END IF
                     FUNCTION = VAL(RETAIN$(sNum,ANY "0123456789-"))
                  END FUNCTION
                  ------------------

                  Comment


                  • #10
                    Code:
                    #COMPILE EXE
                    #DIM ALL
                    
                    '******************************************************************************************
                    'Not sure how to handle macro variables being re-used in error?
                    'Appended them with _WholeNumMacro.  Can they be local to the MACRO?
                      
                    MACRO WholeNum(sNum_WholeNumMacro)
                        LOCAL i_WholeNumMacro, NegFlag_WholeNumMacro, Sum_WholeNumMacro AS LONG
                        LOCAL a_WholeNumMacro AS BYTE PTR
                    
                        a_WholeNumMacro = STRPTR(sNum_WholeNumMacro)
                        NegFlag_WholeNumMacro = 1    'init
                        Sum_WholeNumMacro = 0        'init
                        FOR i_WholeNumMacro = 0 TO LEN(sNum_WholeNumMacro)
                          SELECT CASE AS LONG @a_WholeNumMacro[i_WholeNumMacro] ' slightly faster than IF ELSE
                            CASE 48 TO 57 ' 0 - 9
                              sum_WholeNumMacro = sum_WholeNumMacro*10 + @a_WholeNumMacro[i_WholeNumMacro] - 48
                    
                           CASE 45 ' minus sign
                              IF Sum_WholeNumMacro = 0 THEN NegFlag_WholeNumMacro = -1  ' first - before number
                    
                           CASE 46 ' decimal point
                              EXIT FOR
                    
                          END SELECT
                        NEXT i
                        RetVal = Sum_WholeNumMacro * NegFlag_WholeNumMacro
                    END MACRO
                    
                    
                    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                    SUB time_stamp_count(tick AS QUAD) ' CPU Clock count    Charles E V Pegge
                    
                      '---------------------------'
                      '                           ' approx because it is not a serialised instruction
                      '                           ' it may execute before or after other instructions
                      '                           ' in the pipeline.
                      ! mov ebx,tick              ' var address where count is to be stored.
                      ! db  &h0f,&h31             ' RDTSC read time-stamp counter into edx:eax hi lo.
                      ! mov [ebx],eax             ' save low order 4 bytes.
                      ! mov [ebx+4],edx           ' save high order 4 bytes.
                      '---------------------------'
                    
                    END SUB
                    
                    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                    FUNCTION Val2( sNum AS STRING) AS LONG
                    
                      LOCAL i, NegFlag, Sum AS LONG
                      LOCAL a AS BYTE PTR
                    
                        a = STRPTR(sNum)
                    
                        NegFlag = 1
                    
                        FOR i = 0 TO LEN(sNum)
                          SELECT CASE AS LONG @a[i] ' slightly faster than IF ELSE
                            CASE 48 TO 57 ' 0 - 9
                              sum = sum*10 + @a[i] - 48
                    
                           CASE 45 ' minus sign
                              IF Sum = 0 THEN NegFlag = -1  ' first - before number
                    
                           CASE 46 ' decimal point
                              EXIT FOR
                    
                          END SELECT
                        NEXT i
                    
                    FUNCTION = Sum * NegFlag
                    
                    END FUNCTION
                    
                    '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                    
                    
                    FUNCTION PBMAIN( )
                    
                      LOCAL cBeg, cEnd AS QUAD  ' for time stamp, measuring cpu clock cycles
                      LOCAL k, RetVal, nLoops AS LONG
                      LOCAL sNumRem, sNum, s AS STRING
                    
                        sNum = "$-2,279.59"
                    
                        sNumRem  = "+?/\()$*%, " ' Stuff to remove from numbers
                    
                        time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                    
                          nLoops = 1000
                          FOR k = 1 TO nLoops
                            RetVal = VAL( REMOVE$( sNum, ANY sNumRem ) ) ' Approx 5200 clock cycles
                          NEXT
                    
                        time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                    
                        s = s + "VAL =" + STR$(RetVal) + ",   Clock Cycles="+STR$( (cEnd-cBeg)\nLoops ) + $CRLF + $CRLF
                        '=======================
                    
                        time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                    
                          nLoops = 1000
                          FOR k = 1 TO nLoops
                            RetVal = Val2(sNum)  ' Approx 370 clock cycles
                          NEXT
                    
                        time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                    
                        s = s + "VAL2 =" + STR$(RetVal) + ",  Clock Cycles="+STR$( (cEnd-cBeg)\nLoops ) + $CRLF + $CRLF
                        '=======================
                       
                       time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                    
                       nLoops = 1000
                       FOR k = 1 TO nLoops
                          'RetVal = 999999      'Additional error check, should never be returned
                          WholeNum(sNum)
                       NEXT
                    
                    
                        time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                    
                        s = s + "MakeWhole =" + STR$(RetVal) + ",  Clock Cycles="+STR$( (cEnd-cBeg)\nLoops )
                    
                    
                        ? s
                    
                    END FUNCTION

                    ------------------




                    [This message has been edited by Mike Doty (edited July 27, 2007).]

                    Comment


                    • #11
                      Mike,

                      Thats what I was thinking but I would do it with

                      MACRO FUNCTION WholeNum(sNum_WholeNumMacro)
                      '
                      '
                      '
                      END MACRO = Sum_WholeNumMacro * NegFlag_WholeNumMacro

                      then
                      RetVal = WholeNum(sNum)

                      This can slot into existing code (lots of it)

                      The problem is that I also do things like this

                      Result = WholeNum( LEFT$(sNum, 10) ) * 999

                      This will generate a compile time error.

                      I don't know MACRO tricks well enough to know if there is a
                      way around this...

                      I think my only option is to just convert the function to ASM to
                      wring out the last ounce of speed.

                      ------------------
                      Kind Regards
                      Mike

                      Comment


                      • #12
                        '
                        Code:
                        #COMPILE EXE
                        #DIM ALL
                        
                        '******************************************************************************************
                        'Not sure how to handle macro variables being re-used in error?   MACROTEMP?
                        'Not sure how to return a value like a function unless turned into a 1-line MACRO
                          
                        MACRO WholeNum(Parameter1)
                        
                            MACROTEMP i,NegFlag,Sum,a
                            
                            LOCAL i, Sum, NegFlag AS LONG   'suggestion taken from Paul, below
                            LOCAL a AS BYTE PTR
                        
                            a = STRPTR(Parameter1)
                            NegFlag = 1    'init
                            Sum = 0        'init
                            FOR i = 0 TO LEN(Parameter1)
                              SELECT CASE AS LONG @a[i] ' slightly faster than IF ELSE
                                CASE 48 TO 57 ' 0 - 9
                                  sum = sum*10 + @a[i] - 48
                        
                               CASE 45 ' minus sign
                                  IF Sum = 0 THEN NegFlag = -1  ' first - before number
                        
                               CASE 46 ' decimal point
                                  EXIT FOR
                        
                              END SELECT
                            NEXT i
                            RetVal = Sum * NegFlag
                        END MACRO
                        
                        
                        '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                        SUB time_stamp_count(tick AS QUAD) ' CPU Clock count    Charles E V Pegge
                        
                          '---------------------------'
                          '                           ' approx because it is not a serialised instruction
                          '                           ' it may execute before or after other instructions
                          '                           ' in the pipeline.
                          ! mov ebx,tick              ' var address where count is to be stored.
                          ! db  &h0f,&h31             ' RDTSC read time-stamp counter into edx:eax hi lo.
                          ! mov [ebx],eax             ' save low order 4 bytes.
                          ! mov [ebx+4],edx           ' save high order 4 bytes.
                          '---------------------------'
                        
                        END SUB
                        
                        '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                        FUNCTION Val2( sNum AS STRING) AS LONG
                        
                          LOCAL i, NegFlag, Sum AS LONG
                          LOCAL a AS BYTE PTR
                        
                            a = STRPTR(sNum)
                        
                            NegFlag = 1
                        
                            FOR i = 0 TO LEN(sNum)
                              SELECT CASE AS LONG @a[i] ' slightly faster than IF ELSE
                                CASE 48 TO 57 ' 0 - 9
                                  sum = sum*10 + @a[i] - 48
                        
                               CASE 45 ' minus sign
                                  IF Sum = 0 THEN NegFlag = -1  ' first - before number
                        
                               CASE 46 ' decimal point
                                  EXIT FOR
                        
                              END SELECT
                            NEXT i
                        
                        FUNCTION = Sum * NegFlag
                        
                        END FUNCTION
                        
                        '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                        
                        
                        FUNCTION PBMAIN( )
                        
                          LOCAL cBeg, cEnd AS QUAD  ' for time stamp, measuring cpu clock cycles
                          LOCAL k, RetVal, nLoops AS LONG
                          LOCAL sNumRem, sNum, s AS STRING
                        
                            sNum = "$-2,279.59"
                        
                            sNumRem  = "+?/\()$*%, " ' Stuff to remove from numbers
                        
                            time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                        
                              nLoops = 1000
                              FOR k = 1 TO nLoops
                                RetVal = VAL( REMOVE$( sNum, ANY sNumRem ) ) ' Approx 5200 clock cycles
                              NEXT
                        
                            time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                        
                            s = s + "VAL =" + STR$(RetVal) + ",   Clock Cycles="+STR$( (cEnd-cBeg)\nLoops ) + $CRLF + $CRLF
                            '=======================
                        
                            time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                        
                              nLoops = 1000
                              FOR k = 1 TO nLoops
                                RetVal = Val2(sNum)  ' Approx 370 clock cycles
                              NEXT
                        
                            time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                        
                            s = s + "VAL2 =" + STR$(RetVal) + ",  Clock Cycles="+STR$( (cEnd-cBeg)\nLoops ) + $CRLF + $CRLF
                            '=======================
                        
                           LOCAL sNum2 AS STRING
                           sNum2 = "$-2,279.59"
                             
                           time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                        
                           nLoops = 1000
                           FOR k = 1 TO nLoops
                              'RetVal = 999999      'Additional error check, should never be returned
                              WholeNum(sNum2)
                           NEXT
                        
                        
                            time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                        
                            s = s + "MakeWhole =" + STR$(RetVal) + ",  Clock Cycles="+STR$( (cEnd-cBeg)\nLoops )
                            ? s
                        
                        
                        END FUNCTION'
                        ------------------




                        [This message has been edited by Mike Doty (edited July 27, 2007).]

                        Comment


                        • #13
                          Mike,
                          you just stated it's now running in under 370 down from 5200 clks. That's 14 times faster. Are you ever going to be satisfied?

                          Paul.


                          ------------------

                          Comment


                          • #14
                            PS a quick look at where the time is being used shows (on my PC)
                            Total time = 207 clks of which 3 is the loop overhead, 23 is the call/return overhead so 181 is the time taken processing in the VAL2 function. That's where you need to concentrate next if you want to save time. Changing to a MACRO will only save you a part of 26 clks, changing the contents of the FUNCTION can save you part of 207clks.

                            ------------------
                            PPS
                            change the line
                            Code:
                            LOCAL i, NegFlag, Sum AS LONG
                            to
                            Code:
                            LOCAL i, Sum, NegFlag AS LONG
                            That saves another few clks on mine. The compiler will allocate REGISTER variables to the first 2 suitable variables declared in each SUB/FUNCTION unless you specifically allocate them yourself. In this case, NegFlag is being given a register but it's rarely used so swapping the order around puts the more used SUM variable in a register where it saves more time. You could specifically allocate SUM and I as REGSTER variables to make it clearer.
                            That's brings the time down from 207 to 195clks on my PC.


                            [This message has been edited by Paul Dixon (edited July 27, 2007).]

                            Comment


                            • #15
                              Floats:

                              Code:
                              '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤' 
                              FUNCTION VALS( sNum AS STRING) AS SINGLE ' For Floats
                                               
                                LOCAL i, NegFlag, Sum, FracFlag AS LONG          
                                LOCAL a AS BYTE PTR  
                                LOCAL Fraction AS SINGLE             
                                
                                  a = STRPTR(sNum)
                                         
                                  NegFlag = 1
                              
                                  FOR i = 0 TO LEN(sNum)
                                    SELECT CASE AS LONG @a[i] ' slightly faster than IF ELSE
                                      CASE 48 TO 57 ' 0 - 9  
                                        IF FracFlag = 0 THEN
                                          Sum = Sum*10 + @a[i] - 48  
                                        ELSE
                                          Fraction = Fraction/10 + @a[i] - 48) ' This wont work <--------------------  
                                        END IF
                               
                                     CASE 45 ' minus sign
                                        IF Sum = 0 THEN NegFlag = -1  ' first - before number
                               
                                     CASE 46 ' decimal point
                                        IF Sum THEN FracFlag = 1 ' now the fractional part
                              
                                    END SELECT
                                  NEXT i  
                                         
                                FUNCTION = Sum+Fraction * NegFlag
                                           
                              END FUNCTION
                              ------------------
                              Kind Regards
                              Mike

                              Comment


                              • #16
                                Paul,

                                I forgot about that!
                                Of course I should use the register variables.

                                Will I be satisfied?
                                I am allready very happy, but there are many things to be learned
                                here that I can apply in my coding as my ability starts to encompass
                                the clks cost of my development approaches.

                                I realized that in my code I use certain string operations
                                extensivly and they are expensive. Now I am coding using pointers
                                to strings so no strings will be harmed at run time!



                                ------------------
                                Kind Regards
                                Mike

                                Comment


                                • #17
                                  Mike,
                                  if you use ASCIIZ string instead of STRING for the parameter it'll be faster still.
                                  Remember to change the STRPTR to VARPTR and don't use LEN(), just scan the string until you hit the terminating zero.
                                  This gets it down from 195 to 168clks on my PC. The main reason seems to be that determining the length of a STRING take a lot longer than I expected so you save time on the call to LEN().

                                  Paul.
                                  Code:
                                  #COMPILE EXE "Val2.exe" '
                                  #DIM ALL
                                  
                                  
                                  '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                                  SUB time_stamp_count(tick AS QUAD) ' CPU Clock count    Charles E V Pegge
                                  
                                    '---------------------------'
                                    '                           ' approx because it is not a serialised instruction
                                    '                           ' it may execute before or after other instructions
                                    '                           ' in the pipeline.
                                    ! mov ebx,tick              ' var address where count is to be stored.
                                    ! db  &h0f,&h31             ' RDTSC read time-stamp counter into edx:eax hi lo.
                                    ! mov [ebx],eax             ' save low order 4 bytes.
                                    ! mov [ebx+4],edx           ' save high order 4 bytes.
                                    '---------------------------'
                                  
                                  END SUB
                                  
                                  
                                  '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                                  FUNCTION Val2( sNum AS ASCIIZ ) AS LONG
                                  ' EXIT FUNCTION
                                  '#register none
                                    LOCAL a AS BYTE PTR
                                    LOCAL i AS LONG
                                    LOCAL Sum AS LONG
                                    LOCAL NegFlag AS LONG
                                  
                                  
                                   '   a = STRPTR(sNum)
                                      a=VARPTR(sNum)
                                  '    i = len(sNum)
                                  
                                      NegFlag = 1
                                  
                                  
                                   
                                   '   FOR i = 0 TO LEN(sNum)
                                      i=0
                                      DO
                                        SELECT CASE AS LONG @a[i] ' slightly faster than IF ELSE
                                          CASE 48 TO 57 ' 0 - 9
                                            sum = sum*10 + @a[i] - 48
                                  
                                  
                                         CASE 45 ' minus sign
                                            IF Sum = 0 THEN NegFlag = -1  ' first - before number
                                  
                                         CASE 46 ' decimal point
                                            EXIT DO 'FOR
                                  
                                        END SELECT
                                   '   NEXT i
                                      INCR i
                                      LOOP UNTIL @a[i]=0
                                  
                                     
                                  FUNCTION = Sum * NegFlag
                                  
                                  
                                  END FUNCTION
                                  
                                  '¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
                                  FUNCTION PBMAIN( )
                                  
                                    LOCAL cBeg, cEnd AS QUAD  ' for time stamp, measuring cpu clock cycles
                                    LOCAL k, RetVal, nLoops AS LONG
                                    LOCAL sNumRem, s AS STRING 
                                    LOCAL sNum AS ASCIIZ*100
                                  
                                      sNum = "$-2,279.59"
                                  
                                      sNumRem  = "+?/\()$*%, " ' Stuff to remove from numbers
                                  
                                      time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                                  
                                        nLoops = 1000
                                        FOR k = 1 TO nLoops
                                          RetVal = VAL( REMOVE$( sNum, ANY sNumRem ) ) ' Approx 5200 clock cycles
                                        NEXT
                                  
                                      time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                                  
                                      s = s + "VAL =" + STR$(RetVal) + ",   Clock Cycles="+STR$( (cEnd-cBeg)\nLoops ) + $CRLF + $CRLF
                                      '=======================
                                  
                                      time_stamp_count(cBeg) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                                  
                                        nLoops = 1000
                                        FOR k = 1 TO nLoops
                                          RetVal = Val2(sNum)  ' Approx 370 clock cycles
                                        NEXT
                                  
                                      time_stamp_count(cEnd) ' measuring cpu clock cycles. The overhead just for making this call is about 25 clocks
                                  
                                      s = s + "VAL2 =" + STR$(RetVal) + ",  Clock Cycles="+STR$( (cEnd-cBeg)\nLoops )
                                      '=======================
                                  
                                      MSGBOX s
                                  
                                  END FUNCTION
                                  ------------------

                                  Comment


                                  • #18
                                    That's where you need to concentrate next if you want to save time. Changing to a MACRO will only save you a part of 26 clks, changing the contents of the FUNCTION can save you part of 207clks.
                                    Spending your time where it might actually deliver a payback? Radical Thinking, sir!

                                    Now I am coding using pointers to strings so no strings will be harmed at run time!
                                    I can show you how to harm strings at run time using pointers. It's not hard at all.

                                    Michael Mattias
                                    Tal Systems (retired)
                                    Port Washington WI USA
                                    [email protected]
                                    http://www.talsystems.com

                                    Comment


                                    • #19
                                      mcm,
                                      shhh. keep your voice down, i don't want people thinking i'm a radical.

                                      paul.

                                      ------------------

                                      Comment


                                      • #20
                                        Mike,
                                        that's about as far as I can get with BASIC although there might be a fewminor tweaks.
                                        Must be time to use ASM!
                                        This get's it down to 90clks including the 23 clk overhead from the function call/return.
                                        Code:
                                        FUNCTION Val2( sNum AS ASCIIZ ) AS LONG
                                         
                                        !mov edi,sNum                   ;get the pointer to the string
                                        !xor edx,edx                    ;the SUM =0
                                        !xor ecx,ecx                    ;the NEG flag =0
                                                  
                                                 
                                        !movzx eax,byte ptr [edi]       ;get the first character
                                        lp:
                                        !cmp eax,"0"                    ;is character < a "0"?
                                        !jl  LessThan0                  ;yes, skip to the non-digit check
                                        !cmp eax,"9"                    ;is it greater than a "9"?
                                        !jg  NextCharacter              ;yes, get next char.
                                         
                                        'to get here it must be a digit
                                        !imul edx,10                    ;sum=sum*10 ..
                                        !add edx,eax                    ;    + digit
                                        !sub edx,48                     ;    - 48
                                        !jmp NextCharacter
                                         
                                        LessThan0:
                                        !cmp eax,"."                    ;is it a "."
                                        !je done                        ;yes, then exit loop
                                        !cmp eax,"-"                    ;is it a "-"
                                        !jne NextCharacter              ;no, get next character
                                        !mov ecx,1                      ;set the NEG flag
                                         
                                        NextCharacter:
                                        !inc edi                        ;increment the string pointer
                                        !movzx eax,byte ptr [edi]       ;get next character
                                        !or eax,eax                     ;test if zero
                                        !jnz lp                         ;not zero, go back and do next character
                                         
                                        done:
                                        !cmp ecx,0                      ;is NEG flag set?
                                        !je skip                        ;no, skip next instruction
                                        !neg edx                        ;yes, negate answer
                                         
                                        skip:
                                        !mov function,edx               ;write answer to function
                                         
                                         
                                        END FUNCTION
                                        Paul.

                                        ------------------

                                        Comment

                                        Working...
                                        X