You are here: Code Execution Time

Code Execution Time

As you develop your programming skills on the Propeller, it's not uncommon to find yourself concerned about execution time of certain routines. Your knowledge of the Propeller and programming in general may lead you to try variations in code just to realize speed increases, but it can be a guessing game without solid feedback of actual execution time.

This topic will teach you some good techniques for calculating and measuring execution time that can be applied in many ways, from timing just a single line of code to timing entire sophisticated routines.

How can the execution time of a portion of code be determined?

This is quite easy for Propeller Assembly, but more elusive for Spin code. There are three techniques for timing code with definitive results, each with its "ideal" application.

  1. Counting and adding up determinant instruction times (Propeller Assembly only).
  2. Using the System Counter (Spin and Propeller Assembly).
  3. Toggling a pin and measuring the pulse-width (Spin or Propeller Assembly).

For details, see the Code Execution Time article.

Technique 1: Counting and Adding up Determinant Instruction Times

This applies only to Propeller Assembly. Since Propeller Assembly instructions are well-defined and most consume a fixed number of clock cycles, you can simply add up "clock cycles" as you look through the code. This is the preferred technique for assembly programs, especially in applications where timing is critical. Here's a small snippet from an example assembly routine that is serially transmitting a data byte:

                or      Data, #$100             '4
                shl     Data, #1                '4
                mov     BitCount, #10           '4
TxLoop          shr     Data, #1        wc      '4
                muxc    outa, Pin               '4
                djnz    BitCount, #TxLoop       '4,8 (12:120+4)

 

To determine the execution time of this snippet of code, we looked up the instruction times (in the Propeller Quick Reference, for example) and wrote them in the code's comments. As you can see, most Propeller Assembly instructions take 4 clock cycles. However, the djnz instruction takes 4 cycles if it jumps and 8 cycles if it doesn't– since it will often jump and rarely not jump, we noted that as "4, 8."

So, it's easy to see that the first three instructions will take 4 + 4 + 4 = 12 clock cycles to execute. The last three instructions are a bit more complicated since they are in a loop, the TxLoop. Most of the iterations of TxLoop will take 12 cycles (4 + 4 + 4) to execute and the last iteration will take 4 additional cycles for the non-jump. Since it will loop 10 times (as determined by BitCount and the djnz instruction), the total execution of the loop will be 10 × 12 + 4 = 124 cycles. We noted the single-iteration and full-loop times as "(12:120+4)" at the end of the loop.

These cycle counts give us a straight forward way to determine actual execution time with respect to the clock speed in use. If this code was running with a system clock speed of 80 MHz, then we know that each bit of data transmitted (via the muxc instruction in the TxLoop) will be 12 cycles ÷ 80 MHz = 150 ns in width since it takes 12 cycles to get from one execution of muxc to another. With overhead considered, the entire routine as shown will take 12 cycles (first 3 instructions) + 10 × 12 cycles (loop) + 4 cycles (leaving loop) = 136 cycles total, or 1.7 µs (136 cycles ÷ 80 MHz).

This can be verified with an oscilloscope.

Why don't we document the execution time for Spin commands like we do for Propeller Assembly instructions?

Propeller Assembly instructions are very deterministic in their timing since they are the lowest level of execution within the cogs. Since Spin commands are constructed from potentially dozens of Propeller Assembly instructions with many possible execution paths, they are inherently difficult to document as a fixed amount of execution time. Even a single, specific Spin command can take a different amount of time to execute depending on the how its parameters are defined and with the varying complexity of constants used. For instance, constant values are compressed by the Spin compiler into a roughly proportional number of bytes, which affects the command's resulting execution time. For this reason, it is important to use the next techniques for timing Spin code.

Technique 2: Using the System Counter

This applies to both Spin and Propeller Assembly. The System Counter in the Propeller is an immensely helpful tool for timing. We can use it to time portions of code by noting the System Counter value just before and just after the target code and calculating the difference.

Developing Timing Code in Spin

It's always best to remember the technique of doing this kind of timing, rather than trying to remember the actual code that performs it. If you remember the technique, you can always build the code in a few moments when you need it, and perform a quick "test" can determine its validity in the situation you're testing.

You may have realized by now that each "read" of the System Counter takes time also, so our result will naturally be off by some amount. That's true, so before relying on this timing method we'll first have to determine the amount of overhead it introduces.

For our overhead calculation, the concept is to take two System Counter readings (Time1 and Time2), one immediately after the other, and subtract the two readings to determine how many clock cycles it took just to record the to values. You can then use an object like "Simple_Serial" or "Parallax Serial Terminal" to transmit the result as a decimal number to the computer screen.

Note that the difference between Time1 and Time2 can be calculated as Time2 - Time1, but by applying a little Algebra we can see that -Time1 + Time2 is an equivalent expression. Using this knowledge and applying some powerful Spin operators allows us to do it all using just one long variable.

VAR 
  long	Time		'Holds elapsed clock cycles
 
PUB Timing
  Time := -cnt		'Read System Counter (Start)  
  Time += cnt		'Read System Counter (End)

 

After the second "Time" line executes (above), Time will be equal to the number of clock cycles it took just to perform these two reads of the System Counter. As it turns out, it takes 544 clock cycles.

Keep in mind it's always important to test the overhead first, then account for it, rather than to always expect it to be 544. For example, the overhead will be different if Time were defined outside of the first eight longs of either the object's global variables or the Timing method's local variables (which includes parameters and the automatic RESULT variable). Click here for more information.

So, now that we've tested the overhead, we can modify the time checking code slightly to accommodate for it:

  Time := -cnt         'Read System Counter (Start)
  Time += cnt - 544    'Read System Counter (End)

 

Now, the clock cycles that elapse between the two statements will be calculated as 0, and adding any additional statements in-between them will increase this value– a direct result of the time required by those additional statements. This is accurate as long as the clock cycles elapsed is less than 231.

Developing Timing Code in Propeller Assembly

A similar technique can be used in Propeller Assembly as it was in Spin. Start with the following:

AsmTiming         org 0         
                  neg Time, cnt     'Read System Counter (Start)
                  add Time, cnt     'Read System Counter (End)

                  {more code here}

Time              res       1       'Holds elapsed clock cycles

 

You'll find that the overhead is, not surprisingly, 4 clock cycles– so adjusting the time-checking code to accommodate for this leads to:

                  neg Time, cnt     'Read System Counter (Start)  
                  add Time, cnt     'Read System Counter (End)
                  sub Time,  #4     'Adjust for overhead

 

Now, the clock cycles elapsed between the first two statements will be calculated as 0, and adding any additional instructions in-between them will increase this value– a direct result of the time required by those additional instructions..

Using it in Practice

Now, for Spin code that you want to time: precede the target code with:

  Time := -cnt 

 

...and follow the target code with:

  Time += cnt - 544

 

..and then serially transmit the result with your favorite communication object, and if desired, grab a calculator and divide the result (clock cycles) by your system clock speed (cycles per second) to determine the actual time elapsed.

For assembly code you want to time, follow a similar technique, except with:

  neg Time, cnt

 

...preceding the target code and

  add Time, cnt 
  sub Time, #4

 

...following it.

Technique 3: Toggling a pin and measuring the pulse-width

This applies to both Spin and Propeller Assembly. Using an oscilloscope to monitor a toggling I/O pin is occasionally the handiest way to time Spin or Propeller Assembly code.

In Spin

In Spin, place something like the following immediately before the target code:

  dira[0]~~             'Set I/O pin 0 to output  
  !outa[0]              'Toggle pin 0

 

...and something like the following immediately after the target code:

  !outa[0]              'Toggle pin 0

 

In Propeller Assembly

In Propeller Assembly, place something like the following immediately before the target code:

   or  dira, #%1        'Set I/O pin 0 to output
   xor outa, #%1        'Toggle pin 0

 

...and something like the following immediately after the target code:

   xor outa, #%1        'Toggle pin 0

 

In both cases (Spin and Propeller Assembly) keep in mind that the toggling statement itself (!outa[0] or xor outa, #%1) takes some time to execute. You can determine the overhead, of course, by first testing the pulse-width generated by two such toggling statements adjacent to each other; just subtract that overhead from all further readings.

Go to Welcome page

 

 

Switch to Mobile View homepage

Propeller P8X32A Questions & Answers

Copyright © Parallax Inc., dba Parallax Semiconductor

Version 1.3.1

10/23/2013

Terms of Use