code-timer

Measure the execution time

Measuring the execution time of a script or program is part of a developer’s daily job.

It is not enough to see if it consumes too much memory or too much CPU. We need to see if the software in its entire life cycle is fast enough.

This is because the business logic of any software expects a certain task to be completed with a maximum time established by the programmer himself.

In a linux kernel operating system, there is a command called time which serves this purpose:

$ time wget google.com

real	0m0,239s
user	0m0,007s
sys	0m0,007s

The output of this command is interpreted like this:

  • real: is the time from start to finish of the call.
  • user: amount of CPU time spent in user mode.
  • sys: amount of CPU time spent in kernel mode.

This way, we have a general idea of how long our program takes from the start of execution to the end. But how can we measure a single piece of software? We could break it up into parts and use the time command, but imagine our software contains at least a hundred functions.

Time your code

Generally, languages do not have a timer that measures the execution time of portions of code. That said, we have to somehow implement it ourselves.

Create a timer

Creating a timer is quite simple, in any language: java, go, etc…

The principle is the same. You save an initial zero time, start counting seconds or milliseconds (depending on the software) and save the final time when the last code has been executed.

CodeTimer in java:

final long startTime = System.currentTimeMillis();
// Do something
final long endTime = System.currentTimeMillis();
System.out.println("Call to do something took  " + (endTime - startTime));

CodeTimer in go:

elapsed := time.Since(time.Now())
// Do something
log.Printf("Call to do something took %s", elapsed)

CodeTimer in javascript:

var t0 = performance.now()
// Do something
var t1 = performance.now()
console.log("Call to do something took " + (t1 - t0) + " milliseconds.")

As you can see, the pattern is similar. But in this way, I always have to enclose my code between the start and end variable call.

Optimize the timer

We can optimize this process by writing a function so that we can call it to measure the elapsed time of our piece of code. For example, in go, I created a CodeTimer function that will return elapsed time (go::defer) after the calling function returns. This is an optimization of our goal.

func CodeTimer(start time.Time, name string) {
    elapsed := time.Since(start)
    log.Printf("%s took %s", name, elapsed)
}

func factorial(n *big.Int) (result *big.Int) {
    defer CodeTimer(time.Now(), "factorial")
    // Do something
    return n
}

This doesn’t force us to write the same lines of code to count the time every time we have to, but we’ll call our CodeTimer function as needed. The only problem is that we should insert our function inside other functions and therefore, modify our final code.

Timing manager

There is a better way to use a function that measures code. However, we cannot encode this with any language.

In python exists Context manager. Context manager is a protocol that the python doc defines like this:

A context manager is an object that defines the runtime context to be established when executing a with statement. The context manager handles the entry into, and the exit from, the desired runtime context for the execution of the block of code. Context managers are normally invoked using the with statement (described in section The with statement), but can also be used by directly invoking their methods.

Python code timer

Context managers allow you to allocate and release resources precisely when you want to. The most widely used example of context managers is the with statement. Suppose you have two related operations which you’d like to execute as a pair, with a block of code in between. Context managers allow you to do specifically that.

For example, the open built-in class uses this protocol to automate file closing to avoid errors. This is an example of how this protocol works:

with open('file.txt', 'w') as f
    f.write('Hi python!')

This piece of code can also be written in another way:

f = open('file.txt', 'w')
try:
    f.write('Hi python!')
finally:
    f.close()

We can use this protocol to write our CodeTimer class which we will save in a codetimer.py module. Here I am how it will be:

from time import time

class CodeTimer:

    def __init__(self):
        self.start = 0
        self.elapsed = 0

    def __enter__(self):
        self.start = time()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.elapsed = int(time() - self.start)

    def __repr__(self):
        return str(self.elapsed)

Now, let’s try using this class to measure execution time.

from time import sleep
from codetimer import CodeTimer

def test():
    print('Wait 2 seconds!')
    sleep(2)

timer = CodeTimer()

with timer as tm:
    print(tm)
    test()
print(timer)

Python contextlib

As this protocol covers a lot of other tasks, the standard python library offers a module that has various objects for creating and using context managers.

The contexmanager decorator creates an object like the one I wrote earlier. It works just the same way.

from contextlib import contextmanager

@contextmanager
def code_timer():
    start = 0
    try:
        start = time()
        yield start
    finally:
        print(int(time() - start))

Conclusion

We have seen how to count the execution of an entire software, a piece of code and how to optimize this technique in certain languages.

Also, in python, we have learned that there is an even better way to optimize this technique via the Context manager protocol. This allows us not only to use an object to count the time of a given code, but to optimize code writing. Furthermore, we can create Context managers from a function, thanks to python’s standard contextlib library.

Counting the time your code runs is a good starting point for writing great code.