Skip to main content

Accurate Measurement of Small Execution Times

Project description

logo exectimeit

Accurate Measurement of Small Execution Times

PyPI - Version PyPI - Python Version GitHub License GitHub Actions Workflow Status GitHub last commit

Measuring small execution times (especially for fast routines $< 100$ milliseconds) in Python can be extremely challenging. The traditional way of timing code is to take the difference of system times before and after a function execution. However, this is highly susceptible to two types of errors:

  1. Random error: Environmental noise (OS scheduling, CPU frequency scaling) causes variation. This can be reduced by averaging multiple measurements.
  2. Systematic error: The overhead of invoking the timing instructions themselves is added to the measured duration, which is difficult to filter out.

This library implements the mathematical model proposed by Carlos Moreno and Sebastian Fischmeister [1] to circumvent systematic measurement overhead.


The Mathematics Behind It

Instead of measuring a single execution of a function $f$, we measure the total duration of executing the function sequentially $k$ times inside a single timing block, for $k = 1, 2, \dots, n$ (where $n$ is the number of repetitions, recommended $\ge 3$).

The total measured time $T_k$ for $k$ executions is modeled as a linear function: $$T_k = k \cdot t_{\text{exec}} + t_{\text{overhead}} + \epsilon_k$$

Where:

  • $t_{\text{exec}}$ is the actual, true execution time of a single run.
  • $t_{\text{overhead}}$ is the fixed overhead of starting/stopping the timer.
  • $\epsilon_k$ is the random measurement error for trial $k$.

By fitting a simple linear regression $y = m \cdot x + b$ to the data points $(k, T_k)$:

  • The slope ($m$) represents the true single-run execution time ($t_{\text{exec}}$), entirely free of the timer's start/stop overhead.
  • The intercept ($b$) represents the fixed timer overhead ($t_{\text{overhead}}$).
  • The Residual Standard Error (RSE) provides a precise measure of the variation or noise: $$rse = \sqrt{\frac{\sum (y_i - \hat{y}_i)^2}{n - 2}}$$
Linear Fit Representation

To eliminate loop iteration overhead in Python, exectimeit dynamically compiles and caches unrolled execution routines for each $k$.


Installation

pip install exectimeit

Or install from GitHub:

pip install git+https://github.com/mariolpantunes/exectimeit.git@main

Usage

Option 1: Direct function call wrapper

You can measure any callable using the timeit function. It returns an ExecTimeResult named tuple containing:

  • time: estimated execution time per run (in seconds)
  • rse: residual standard error of the linear fit (in seconds)
  • value: the return value of the function
from exectimeit import timeit
import time

def my_fast_function(x, y):
    time.sleep(0.001)  # Simulating some work
    return x + y

# Measure with n=5 repetitions
result = timeit(5, my_fast_function, 2, y=3)

# You can access properties by name:
print(f"Time: {result.time:.6f}s, RSE: {result.rse:.6f}s, Return value: {result.value}")

# Or unpack like a standard tuple:
t, rse, val = result

Option 2: Decorator

You can decorate your functions with @exectime to automatically wrap them. When called, the decorated function will return the ExecTimeResult.

from exectimeit import exectime

@exectime(n=5)
def my_decorated_function(a):
    return a * a

# Executing the function returns the measurement named tuple:
t, rse, val = my_decorated_function(10)
print(f"Time: {t:.6f}s, Return value: {val}")

Running Unit Tests

The test suite validates both deterministic mock-timing regression math and real-time execution. Run them with:

python3 -m unittest discover -s test

Documentation

Detailed package documentation is hosted on GitHub Pages: https://mariolpantunes.github.io/exectimeit/

To generate the docs locally:

pip install pdoc
PYTHONPATH=src pdoc --math -d google -o docs exectimeit \
  --logo "assets/logo.svg" \
  --favicon "assets/logo.svg"

References

[1] C. Moreno and S. Fischmeister, "Accurate Measurement of Small Execution Times—Getting Around Measurement Errors," in IEEE Embedded Systems Letters, vol. 9, no. 1, pp. 17-20, March 2017, doi: 10.1109/LES.2017.2654160.


Authors


License

This project is licensed under the MIT License - see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

exectimeit-0.2.1.tar.gz (8.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

exectimeit-0.2.1-py3-none-any.whl (7.6 kB view details)

Uploaded Python 3

File details

Details for the file exectimeit-0.2.1.tar.gz.

File metadata

  • Download URL: exectimeit-0.2.1.tar.gz
  • Upload date:
  • Size: 8.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for exectimeit-0.2.1.tar.gz
Algorithm Hash digest
SHA256 4fb5eb497f5b700c5bec744e6f4c4a19507b592d701d601d290c2afa3d43328e
MD5 5f565cf813039123fb186200df0954dd
BLAKE2b-256 a08ba6877f93668efc9fb450bb8c270e1291253f63e5c19cdb2161d396858977

See more details on using hashes here.

File details

Details for the file exectimeit-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: exectimeit-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 7.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for exectimeit-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ff4e5d42c834d960ca9fe5eaeab711afd03a6b5596ad5bbd623a0777d6601cbe
MD5 97636f4b7204d832634bc44783974856
BLAKE2b-256 cc13fb3a0a54e5767c47be2ffb118daaefd043764c5b87ccc33eb7c011c15a7a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page