TBTK
Timer

See more details about the Timer in the API

Profiling

In a typical program, most of the execution time is spent in a small fraction of the code. It is, therefore, useful to be able to time different parts of the code to figure out where optimization may be needed. The Timer helps with this. It can either be used as a timestamp stack or as a set of accumulators, or both at the same time.

Timestamp stack

To time a section, all that is required is to enclose it between a Timer::tick() and a Timer::tock() call.

Timer::tick("A custom tag");
//Some code that is being timed.
//...
Timer::tock();

The tag string passed to Timer::tick() is optional, but is useful for distinguishing between different timed sections. When Timer::tock() is called, the tag together with the time that elapsed since the tick call is printed.

More specifically, Timer::tick() pushes a timestamp and a tag onto a stack, while Timer::tock() pops one off from it. It is, therefore, possible to nest Timer calls as follows.

Timer::tick("Full loop");
for(unsigned int m = 0; m < 10; m++){
Timer::tock("Inner loop");
for(unsigned int n = 0; n < 100; n++){
//Do something
//...
}
Timer::tock();
}
Timer::tock();

This results in the inner loop being timed ten times, each time printing the execution time together with the tag 'Inner loop'. The entry corresponding to 'Full loop' remains on the stack throughout execution of the two nested loops. When the final call to Timer::tock() occurs, this entry is poped from the stack and the full execution time is printed with the tag 'Full loop'.

Accumulators

To understand the accumulators, assume that the following loop has been identified to be a bottleneck.

for(unsigned int n = 0; n < 1000000; n++){
task1();
task2();
}

Next, we want to figure out which of the two tasks that is responsible for the slow execution. However, task 1 and 2 may have varying execution times. Therefore, only the total time taken by each call over the 1,000,000 iterations is relevant.

In cases like these, we can use accumulators to time the code.

unsigned int accumulatorID1 = Timer::createAccumulator("Task 1");
unsigned int accumulatorID2 = Timer::createAccumulator("Task 2");
for(unsigned int n = 0; n < 1000000; n++){
Timer::tick(accumulatorID1);
task1();
Timer::tock(accumulatorID1);
Timer::tick(accumulatorID2);
task2();
Timer::tock(accumulatorID2);
}
Timer::printAccumulators();

In the first two lines, we create two accumulators called "Task 1" and "Task 2" and get an ID for each of them. We then pass these IDs to the tick and tock calls. The time taken between such a pair of tick-tack calls are added to the accumulators with the given ID. The final line prints a table displaying the total time accumulated in each accumulator.

Next: FourierTransform