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}}$$

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.0.tar.gz (8.7 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.0-py3-none-any.whl (7.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: exectimeit-0.2.0.tar.gz
  • Upload date:
  • Size: 8.7 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.0.tar.gz
Algorithm Hash digest
SHA256 42f44d04f2b41490a640ccdac5f0ba3adbb2aaa9064a8a4f3671b0ba9e16ea18
MD5 49a1200193e5d86f49ca0ac80db88f2a
BLAKE2b-256 b5012693e5ba6ca870610d31d80524b04246e7655679d2acf38489f46e27700f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: exectimeit-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 7.3 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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d7fbd338479bf1ee4c80be73f586022a395ac0da41d0a302aca90414672cb546
MD5 a30fa89afbb4a5cdaae89d73109d74c9
BLAKE2b-256 e9420cd69e242afe493f7e55738964bbd694c1c4f23f2112c57d2ae1ef1c8f63

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