Python decorator to measure execution time
Tags: python, programming, beginners, webdev
Published: 2022-04-21 07:02:29

In the previous articles on the [Python decorator series](https://sureshdsk.dev/series/python-decorators), we have learnt decorators, how they work and to implement a simple function based decorator and a class based decorator and decorator that supports parameters. In this article, we will create reusable decorator utility to measure execution time of a function and instance method in a class.


## Measure execution time of a function
```python
from functools import wraps
import time


def timeit(func):
    @wraps(func)
    def timeit_wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        total_time = end_time - start_time
        print(f'Function {func.__name__}{args} {kwargs} Took {total_time:.4f} seconds')
        return result
    return timeit_wrapper


@timeit
def calculate_something(num):
    """
    Simple function that returns sum of all numbers up to the square of num.
    """
    total = sum((x for x in range(0, num**2)))
    return total

if __name__ == '__main__':
    calculate_something(10)
    calculate_something(100)
    calculate_something(1000)
    calculate_something(5000)
    calculate_something(10000)

```

### How it works
1. We decorate the function with timeit decorator
2. decorator makes note of start time 
3. then executes the function
4. decorator marks end time
5. calculates time difference and prints the time taken for the function


Output
```
Function calculate_something(10,) {} Took 0.0000 seconds
Function calculate_something(100,) {} Took 0.0008 seconds
Function calculate_something(1000,) {} Took 0.0760 seconds
Function calculate_something(5000,) {} Took 2.4503 seconds
Function calculate_something(10000,) {} Took 7.9202 seconds
```


## Measure execution time of a method inside a class
```python
from functools import wraps
import time


def timeit(func):
    @wraps(func)
    def timeit_wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        total_time = end_time - start_time
        # first item in the args, ie `args[0]` is `self`
        print(f'Function {func.__name__}{args} {kwargs} Took {total_time:.4f} seconds')
        return result
    return timeit_wrapper


class Calculator:
    @timeit
    def calculate_something(self, num):
        """
        an example function that returns sum of all numbers up to the square of num
        """
        total = sum((x for x in range(0, num**2)))
        return total
    
    def __repr__(self):
        return f'calc_object:{id(self)}'


if __name__ == '__main__':
    calc = Calculator()
    calc.calculate_something(10)
    calc.calculate_something(100)
    calc.calculate_something(1000)
    calc.calculate_something(5000)
    calc.calculate_something(10000)

```
### How it works
1. we decorate a method inside a class with timeit
2. timeit takes all arguments, note that args[0] is `self` so you can call any other method inside the class using self
3. decorator makes note of start time 
4. then executes the function
5. decorator marks end time
6. calculates time difference and prints the time taken for the function


Output
```
Function calculate_something(calc_object:140246512997904, 10) {} Took 0.0000 seconds
Function calculate_something(calc_object:140246512997904, 100) {} Took 0.0007 seconds
Function calculate_something(calc_object:140246512997904, 1000) {} Took 0.1820 seconds
Function calculate_something(calc_object:140246512997904, 5000) {} Took 1.9241 seconds
Function calculate_something(calc_object:140246512997904, 10000) {} Took 7.4005 seconds
```

In the next article, we will implement various kinds decorator recipes. Stay tuned for upcoming articles. Subscribe to the newsletter and Connect with me on [twitter](https://twitter.com/sureshdsk) to get my future articles.