Skip to main content

PowerTrack

Measure energy consumption of Python code — CPU · GPU · RAPL · Time-based estimates

PowerTrack provides a unified API to profile power usage with multiple backends—from simple time-based estimates to hardware Intel RAPL counters and NVIDIA GPU power sampling. Use it as a decorator or function call to measure joules, watts, and execution time of any callable.


Table of Contents


Features

  • Multiple backends: Time-based, CPU-weighted, Intel RAPL, and NVIDIA GPU
  • Auto-detection: Automatically uses the best available backend on your system
  • Concurrent measurement: Run multiple backends simultaneously and aggregate results
  • Flexible API: Use as a function call or decorator
  • Zero-config fallback: Works out of the box with the time-based backend (no hardware required)

Installation

From source

Place the powertrack package in your Python path. From the directory containing powertrack:

export PYTHONPATH="${PYTHONPATH}:$(pwd)"
python -c "from powertrack import measure_power; print('OK')"

Core (minimal)

The core package works with the time-based backend only—no extra dependencies required.

Optional backends

Backend Package Command
CPU-weighted psutil pip install psutil
Intel RAPL pyRAPL pip install pyRAPL
NVIDIA GPU pynvml pip install pynvml

Install all optional backends:

pip install powertrack psutil pyRAPL pynvml

Quick Start

Using the decorator

from powertrack import measure_power

@measure_power(backend="time_based")
def compute_sum(n):
    return sum(i**2 for i in range(n))

result = compute_sum(1_000_000)
print(f"Energy: {result.energy_j:.2f} J")
print(f"Time:   {result.time_s:.3f} s")
print(f"Power:  {result.avg_power_w:.2f} W")
print(f"Output: {result.result}")

Using the function API

from powertrack import measure_callable

def heavy_work(x):
    return sum(i**2 for i in range(x))

m = measure_callable(heavy_work, 1_000_000, backend="time_based", return_result=True)
print(f"Energy: {m.energy_j:.2f} J, Result: {m.result}")

Auto backend selection

from powertrack import measure_power

@measure_power(backend="auto")  # Uses best available: RAPL → GPU → CPU-weighted → time-based
def my_function():
    # your code here
    pass

API Reference

measure_callable(func, *args, backend="auto", backend_opts=None, runs=1, warmup=0, return_result=True, **kwargs) -> Measurement

Measure energy consumption of a callable.

Parameter Type Default Description
func Callable — The function or callable to measure
*args — — Positional arguments passed to func
backend str | List | type "auto" Backend name(s), class(es), or instance(s). See Backend selection.
backend_opts Dict None Options passed to backend constructors. See Backend options.
runs int 1 Number of measured runs (results are averaged)
warmup int 0 Number of warmup runs (not measured, used to stabilize caches/CPU)
return_result bool True If True, include the function's return value in Measurement.result
**kwargs — — Keyword arguments passed to func

Returns: A Measurement instance with aggregated energy, time, and power data.

Example:

m = measure_callable(my_func, arg1, arg2, kwarg=3, backend="cpu_weighted", runs=3)

measure(func, *args, **kwargs) -> Measurement

Alias for measure_callable. Same signature and behavior.


measure_power(backend="auto", backend_opts=None, runs=1, warmup=0, return_result=True)

Decorator that measures a function's energy consumption.

Parameter Type Default Description
backend str "auto" Backend to use
backend_opts Dict None Options for the backend
runs int 1 Number of measured runs
warmup int 0 Number of warmup runs
return_result bool True Include return value in Measurement.result

Example:

@measure_power(backend="cpu_weighted", backend_opts={"tdp_w": 45}, runs=3, warmup=1)
def train_model():
    # ...
    return model

Measurement

Dataclass holding measurement results.

Attribute Type Description
time_s float Average wall-clock time in seconds
energy_j float Average energy in joules
avg_power_w float Average power in watts (energy / time)
backend str Always "combined" when using measure_callable
details Dict | None Per-run and per-backend breakdown
result Any Return value of the measured function (if return_result=True)

Methods:

  • to_dict() -> Dict — Convert to a dictionary (excludes result). Useful for JSON serialization.

Example:

m = measure_callable(foo, backend="time_based")
print(m.to_dict())
# {'time_s': 0.5, 'energy_j': 7.5, 'avg_power_w': 15.0, 'backend': 'combined', 'details': {...}}

Backends

Backend selection

The backend parameter accepts:

Value Behavior
"auto" Try, in order: rapl → nvidia_gpu → cpu_weighted → time_based
"time_based" Simple duration × constant power (no sensors)
"cpu_weighted" CPU-time–weighted estimate (requires psutil)
"rapl" Intel RAPL hardware counters (requires pyRAPL, Intel CPU)
"nvidia_gpu" NVIDIA GPU power sampling (requires pynvml, NVIDIA GPU)
["a", "b"] List of backends; all run concurrently, energies summed
Class/instance Custom backend class or instance

Backend options

Pass options via backend_opts:

Global (all backends):

measure_callable(func, backend="time_based", backend_opts={"avg_power_w": 25})

Per-backend:

measure_callable(func, backend=["time_based", "cpu_weighted"],
                 backend_opts={
                     "time_based": {"avg_power_w": 20},
                     "cpu_weighted": {"tdp_w": 45}
                 })

Time-based backend (time_based)

No dependencies. Estimates energy as duration × avg_power_w.

Option Type Default Description
avg_power_w float 15.0 Assumed average system power (W)

Use case: Baseline, CI, or environments without sensors.

from powertrack.backends import TimeBackend

@measure_power(backend="time_based", backend_opts={"avg_power_w": 25.0})
def my_func():
    pass

CPU-weighted backend (cpu_weighted)

Requires: psutil

Estimates power as TDP × (cpu_time / wall_time). Calibrate tdp_w for your CPU.

Option Type Default Description
tdp_w float 28.0 CPU TDP in watts (thermal design power)

Use case: Better CPU-bound workload estimates than time-based.

@measure_power(backend="cpu_weighted", backend_opts={"tdp_w": 65})
def cpu_intensive():
    return sum(i**2 for i in range(10**7))

RAPL backend (rapl)

Requires: pyRAPL, Intel CPU with RAPL support (Linux)

Uses hardware energy counters for package (PKG) and DRAM.

Option Type Default Description
devices List[str] None e.g. ["PKG", "DRAM"] (pyRAPL.Device)
socket_ids List[int] None Socket IDs to measure

Use case: Accurate CPU/DRAM energy on supported Intel systems.

@measure_power(backend="rapl", backend_opts={"devices": ["PKG", "DRAM"]})
def measured_code():
    pass

NVIDIA GPU backend (nvidia_gpu)

Requires: pynvml, NVIDIA GPU and drivers

Samples GPU power during execution via NVML.

Option Type Default Description
index int 0 GPU device index
sampling_interval float 0.1 Sampling interval in seconds

Use case: GPU workload energy profiling.

@measure_power(backend="nvidia_gpu", backend_opts={"index": 0, "sampling_interval": 0.05})
def gpu_inference():
    # CUDA/GPU code
    pass

Usage Examples

Multiple runs and warmup

@measure_power(backend="time_based", runs=5, warmup=2)
def benchmark():
    return expensive_computation()

# 2 warmup runs (not measured) + 5 measured runs; results averaged
m = benchmark()

Multiple backends (concurrent)

m = measure_callable(
    my_func,
    backend=["time_based", "cpu_weighted"],
    backend_opts={
        "time_based": {"avg_power_w": 20},
        "cpu_weighted": {"tdp_w": 45}
    }
)
# Energies from both backends are summed
print(m.details["per_run"][0]["per_backend"])

Discard return value

@measure_power(backend="time_based", return_result=False)
def side_effect_only():
    write_to_disk()
    # No need to store return value

Custom backend instance

from powertrack import measure_callable
from powertrack.backends import TimeBackend

be = TimeBackend(avg_power_w=30.0)
m = measure_callable(my_func, backend=be)

Measurement Output

Structure

Measurement(
    time_s=0.123,           # Average wall time (s)
    energy_j=1.85,          # Average energy (J)
    avg_power_w=15.04,      # energy / time (W)
    backend="combined",
    details={
        "runs": 1,
        "active_backends": ["time_based"],
        "per_run": [
            {
                "wall_time_s": 0.123,
                "run_energy_j": 1.85,
                "per_backend": {
                    "time_based": {
                        "duration": 0.123,
                        "energy_j": 1.85,
                        "avg_power_w": 15.0
                    }
                }
            }
        ]
    },
    result=333332833333500  # Function return value
)

Backend-specific fields in per_backend

Backend Extra keys in per_backend[name]
time_based —
cpu_weighted cpu_time_s
rapl pkg_j, dram_j
nvidia_gpu samples_count, samples

Project Structure

powertrack/
├── __init__.py          # Public API: measure, measure_callable, measure_power, Measurement
├── core.py              # measure_callable, backend resolution
├── decorators.py        # measure_power decorator
├── types.py             # Measurement dataclass
├── backends/
│   ├── __init__.py      # TimeBackend, CPUWeightedBackend
│   ├── time_based.py    # Time-based backend
│   ├── cpu_weighted.py  # CPU-weighted backend (psutil)
│   ├── rapl.py          # Intel RAPL backend (pyRAPL)
│   └── gpu.py           # NVIDIA GPU backend (pynvml)
└── README.md

Testing

Run the test suite:

pytest powertrack/test.py -v

From the project root with PYTHONPATH set:

PYTHONPATH=. pytest powertrack/test.py -v

Contributing

Contributions are welcome. Please open an issue or pull request on GitHub.


License

[Specify your license here]

Metadata

Release files for powertrack 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for powertrack 0.1.0
File Size Uploaded Provenance Actions
powertrack-0.1.0.tar.gz 11.5 kB Without provenance Details for powertrack-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for powertrack 0.1.0
File Interpreter ABI Platform Actions
powertrack-0.1.0-py3-none-any.whl Python 3 none any Details for powertrack-0.1.0-py3-none-any.whl

Total release size: 20.6 kB

Release files / powertrack-0.1.0.tar.gz

Download URL powertrack-0.1.0.tar.gz
Size 11.5 kB
Tags Source
SHA-256 checksum
How to use checksums
6274a5e0f042c4912427d01521769f499db1bbaf2af399aaa5013b228d6e05fc
BLAKE2b-256 checksum
How to use checksums
c67fad8e28e5ad9ff83bceb5f850acd05a4d5cbe3ebd8bd4c9e4c0e2fba74b38
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Release files / powertrack-0.1.0-py3-none-any.whl

Download URL powertrack-0.1.0-py3-none-any.whl
Size 9.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cf73c585b152889df46f42b6e8d1c1fc05d50e16813d94e2402ddee0b1e2c5be
BLAKE2b-256 checksum
How to use checksums
1b886f61cd7ba2490a84a6b8cf7336e7062d679914685450160443fe4d55d64a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Security for powertrack 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page