32 bit seconds math (how do I include the upper 16 bits?)


Closed Thread
Results 1 to 17 of 17

Hybrid View

  1. #1
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612


    Did you find this post helpful? Yes | No

    Default Re: 32 bit seconds math (how do I include the upper 16 bits?)

    Hi guys,
    Thanks a lot for testing the code and for the analasis!

    I didn't write it with speed in mind but I certainly didn't expect it to run THAT FREAKIN slow, geeez 400ms.....can't have that.
    It's clear that it's the owDays routine that is the culprit and the problem is not with the division (the DIV32 and div by 4) commands as these executes in about 600 instruction cycles. It varies a little depending on the actual numbers but around 600 instructions.

    The big hog is the "subtraction loop" and to be honest I didn't see that one comming. It's a about 120 instructions which, at first doesn't seem like much but when it is being executed 4015 times (11 years) it does indeed add up.

    So, here's a suggested workaround. What it does is subtracting the seconds in steps (if possible). First 250 days worth (for as many times as possible), then 25 days worth (for as many times as possible) untill it finally does day by day.

    My test show the complete owDays routine for Test Case 5 now takes around 3400 cycles instead of 420 000, that's roughly 125 times faster. Yes, it comes at the cost of a little more code space but it might be worth it.

    Here's the modified section, everything else is the same, run it through its paces.

    Code:
    if AB < $5460 then  ' Result of DIV32 will return a quotient that is 16-bit
                        ' and caps the total seconds to 31-bits.
        R0 = AB			' High word of seconds into system var
        R2 = CD			' Low word of seconds into system var
    
        owDays = DIV32 21600	' Divide by 86400
        owDays = owDays / 4
    
        
        ' Now we need to subtract 86400 seconds from the running time
        ' one time for each day that has passed. Since we're working with
        ' 16-bit words we need to this sort of "manually".
        ' 65536 + 20864 = 86400.
        ' As it turns out, doing this incrementally takes A LOT of time
        ' so we do it in steps. First (if possible) subtract 250 days worth for
        ' for as many times as possible. Then 25 days worth for as long as possible
        ' and finally one days worth for as many times as needed.
       
        if owDays > 0 then  ' As per Henrik, Speeds up the computation instead of wrapping around
            
            i = owDays
          
            WHILE i > 250   ' 21600000 seconds = 250 days
                Temp = CD
                CD = CD - 38656
                IF Temp < CD THEN
                    AB = AB - 1
                ENDIF
        
                AB = AB - 329
                
                i = i - 250
            WEND        
    
            WHILE i > 25   ' 2160000 seconds = 25 days
                Temp = CD
                CD = CD - 62848
                IF Temp < CD THEN
                    AB = AB - 1
                ENDIF
        
                AB = AB - 32
                i = i - 25
            WEND
    
            While i > 0
                Temp = CD
                CD = CD - 20864
            
                IF Temp < CD THEN	' Did we underflow the low word?
                    AB = AB - 1		' If so, decrement high word
                ENDIF
            
                AB = AB - 1            ' Subtract 65536
                i = i - 1
            WEND
        endif
    /Henrik.
    Last edited by HenrikOlsson; - 4th February 2015 at 17:20.

  2. #2
    Join Date
    Jan 2013
    Location
    Texas USA
    Posts
    229


    Did you find this post helpful? Yes | No

    Default Re: 32 bit seconds math (how do I include the upper 16 bits?)

    Henrik,

    No worries on the simulation testing. It's fairly easy as you know using MPLAB Simulator.
    The tools are all there for the using.
    • The ability to setup a PBP project directly in MPLAB so you can edit the PBP source, recompile.
    • Set the breakpoints you want.
    • The beauty though for this kind of testing is really the Stopwatch feature!
      Couple Stopwatch with breakpoints (either PBP source or ASM), it's great.

    Yes, my wording regarding the computationally intensive nature of division was perhaps inadequate.
    My statement about Division in general being "instruction intensive" was my indirect referral to the iterative subtraction routine required.
    By stating that DIV32 "only makes matters worse", was the fact that the division process (iterative subtraction routine) is now exacerbated by a larger dividend (31bit number).
    Again, not the best choice of words.

    On the surface your new routine does indeed look like it will drastically reduces the number of instructions, great thoughts.
    When I get a few minutes I will give the new code a simulation run through and provide back the results.

    Thanks,
    Regards,
    TABSoft

  3. #3
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,612


    Did you find this post helpful? Yes | No

    Default Re: 32 bit seconds math (how do I include the upper 16 bits?)

    Hi,
    OK, I think I've managed to get it a little better still. It struck me that we could just as well use the DIV32 trick in reverse as well, ie instead of preloading the variables we can do the multiplication and get the result as two 16 bit numbers. This allows us to to the subtraction with larger numbers instead of doing the iterative subtraction. Here's the code:
    Code:
    '=============================================================
    ' convert the one wire seconds count to HH:MM:SS
    '=============================================================
    HMS:
    
    ' This uses DIV32 by preloading the system variable DIV32 uses with
    ' the value we want to divide. To find out how many days has passed
    ' we divide the number of seconds with 86400 (number of seconds in a day).
    ' However, DIV32 only supports 16 bit divisor so we need to do the
    ' division in two steps, 21600 * 4 = 86400.
    
    ' Place the entire algorithm in an If/Then test to limit the total seconds to 
    ' a 31-bit number and to check that the DIV32 will return a result <= 16-bits.
    ' This may seem like limit but it's still something like 44 years.
    
    if AB < $5460 then              ' Result of DIV32 will return a quotient that is 16-bit
                                    ' and caps the total seconds to 31-bits.
    
        R0 = AB			            ' Load high word of seconds into system var
        R2 = CD			            ' Load low word of seconds into system var
    
        owDays = DIV32 21600	    ' Divide by 86400 in two steps
        owDays = owDays / 4
    
        ' Now we need to subtract 86400 seconds from the total
        ' running time for each full day that has passed.
         
        ' First we do a dummy multiplication. We would like to multiply by
        ' 86400 but we can't (since it doesn't fit in a WORD) so we start
        ' by multiplying by 1/2 of that, or 43200. 
        Temp1 = owDays * 43200
            
        ' Then we retreive the 32bit result of the multiplication from system variables
        Temp2 = R0                  ' Get high word from system
        Temp3 = R2                  ' Get low word from system
    
        ' And multiply by 2 to get to 86400 
        Temp2 = Temp2 << 1          ' Shift high word 1 bit
        Temp2.0 = Temp3.15          ' Move high bit of LSW into low bit of MSW
        Temp3 = Temp3 << 1          ' Shift low word on bit
           
    
        ' Now do the subtraction
        Gosub SubtractTime
    
    
        
        ' At this point ABCD contains anything from 0 to 86399 and we need to
        ' figure out how many "full hours" is in it. Because 86399 is more than
        ' what fits in a 16-bit word we're using DIV32 trick to divide the
        ' number of seconds by 3600 (number of seconds in an hour). 
        
        R0 = AB			              ' High word of what's left in seconds into system var
        R2 = CD			              ' Low word of what's left in seconds into system var
        owHH = DIV32 3600	          ' Divide by the number of seconds in an hour
        
        ' Now we need to subtract 3600 seconds per full hour from the 
        ' total number of seconds passed. As before we do a dummy multiplication
        ' and retreive the result from the system variables.
    
        Temp1 = owHH * 3600
        Temp2 = R0                  ' Get high word from system
        Temp3 = R2                  ' Get low word from system
        
        ' Now do the subtraction
        Gosub SubtractTime     
    
        ' Now, we're down to minutes and there can be no more than 3599 seconds left
        ' so we can easily handle it with just the low word and normal math
        owMM = CD / 60
        
        ' And finally we can get the seconds by getting the reminder.
        owSS = CD // 60
    
    else    'Result of DIV32 21600 will return 65535 because it is > 16-bit
            'or the total seconds was a 32-bit number.
            
        'Create an error value for output
        owDays = 99
        owHH = 99
        owMM = 99
        owSS = 99    
    endif
    
    return
    
    SubtractTime:
        Temp1 = CD                  ' Remember low word
        CD = CD - Temp3             ' Subtract low word
        If Temp1 < CD THEN          ' Did we underflow
            AB = AB - 1             ' Then borrow
        ENDIF
        AB = AB - Temp2             ' Subtract high word
    RETURN
    It does use two extra WORD variables (and we could possible do something about that if strictly needed) but that's a small price to pay. It's now smaller than before and the complete routine (when fed Test Case 5) runs in about 1730 instruction cycles compared to the original 420200.

    Try it out if you want.

    /Henrik.

  4. #4
    Join Date
    Jan 2013
    Location
    Texas USA
    Posts
    229


    Did you find this post helpful? Yes | No

    Default Re: 32 bit seconds math (how do I include the upper 16 bits?)

    Henrik,

    Great thinking!

    I re-ran the tests with your original algorithm. I then ran the 5 test cases with your second and third algorithms.

    Here are the execution times for test case 5.

    Original: 420.205ms
    New: 7.464ms
    Final: 2.463ms !!

    I also ran a 6th test case which uses the maximum allowable seconds ($545F:FFFF).
    Original: 1701.548ms
    New: 12.502ms
    Final: 2.423ms

    These are great reductions in execution time.
    I have posted the full results.
    Another interesting note is the 3rd algorithm has a near flat execution time as the Total Seconds increase.
    Very efficient.
    A larger dataset would give a better picture, but I might save that when I have further time.

    Thanks for sharing your thoughts and experience.

    Regards,
    Attached Images Attached Images
    Last edited by Tabsoft; - 5th February 2015 at 03:53.
    Regards,
    TABSoft

Similar Threads

  1. 32 bit math
    By fnovau in forum General
    Replies: 4
    Last Post: - 12th February 2008, 23:55
  2. Averaging 16 bit values without using 32 bit math
    By sirvo in forum mel PIC BASIC Pro
    Replies: 2
    Last Post: - 5th October 2007, 22:18
  3. PIC18Fxx math 24 & 32 bit
    By ronsimpson in forum mel PIC BASIC Pro
    Replies: 8
    Last Post: - 2nd December 2006, 12:52
  4. 32 bit math
    By Charles Linquis in forum mel PIC BASIC Pro
    Replies: 12
    Last Post: - 28th August 2006, 13:34
  5. PBP 16-bit ADC result math
    By sonic in forum mel PIC BASIC Pro
    Replies: 0
    Last Post: - 13th March 2005, 14:21

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts