Skip to content

Performance Timing

Demonstrates c2py23's built-in performance timing instrumentation. Two overloads (double and float) show per-variant timing breakdown.

Interface

# Python dict format equivalent of wsum.c2py
{
    "module": "timing_demomod",
    "source": ["wsum.c"],
    "timing": True,
    "functions": [
        {
            "py_sig": "wsum(data: buffer, weight: float) -> float",
            "doc": "Weighted sum with automatic dispatch. Supports double and float buffers.",
            "c_overloads": [
                {
                    "sig": "weighted_sum_double(const double *data, intptr_t n, float weight) -> double",
                    "map": {
                        "data": "data.ptr",
                        "n": "data.n",
                        "weight": "weight",
                    },
                    "when": "data.format == 'd'",
                    "name": "double",
                },
                {
                    "sig": "weighted_sum_float(const float *data, intptr_t n, float weight) -> double",
                    "map": {
                        "data": "data.ptr",
                        "n": "data.n",
                        "weight": "weight",
                    },
                    "when": "data.format == 'f'",
                    "name": "float",
                },
            ],
            "default_raise": "TypeError: expected float or double buffer",
        },
    ],
}

C Source

#include <stdint.h>

double weighted_sum_double(const double *data, intptr_t n, float weight) {
    double total = 0.0;
    intptr_t i;
    for (i = 0; i < n; i++) {
        total += data[i] * (double)weight;
    }
    return total;
}

double weighted_sum_float(const float *data, intptr_t n, float weight) {
    double total = 0.0;
    intptr_t i;
    for (i = 0; i < n; i++) {
        total += (double)data[i] * (double)weight;
    }
    return total;
}

Jupyter Notebook

The interactive notebook examples/timing_demo/timing_demo.ipynb demonstrates:

  • Reading timing data with c2py23.perf.read_perf()
  • Per-function vs per-overload timing breakdown
  • Comparing c2py23 timing with %%time cell magic
  • Switching tick source: wall-clock (default) vs CPU cycle counter
  • Dynamically enabling/disabling timing per function

Build & Run

pip install -e .                    # from repo root
cd examples/timing_demo
c2py23 wsum.c2py -o wsummod_wrapper.c
python -c "
import ctypes
import timing_demomod as tmod

data = (ctypes.c_double * 5)(1.0, 2.0, 3.0, 4.0, 5.0)
r = tmod.wsum(data, 2.0)
print('Sum:', r)
from c2py23.perf import read_perf
print('Timing:', read_perf(tmod.wsum))
"

How It Works

timing: true enables per-function performance counters. Two c2py_perf_t structs are emitted per function: one for the wrapper overhead and one for each overload's C code. Ticks are recorded without any Python-side interaction -- the wrapper reads the cycle counter or clock_gettime before and after the C call.

The tick source defaults to nanosecond-resolution wall-clock. Switch to CPU cycle counter for higher precision:

mymod._c2py_set_tick_source("cycle")

Fall back to wall-clock:

mymod._c2py_set_tick_source("clock")

When timing is disabled (set_enabled(func, 0)), the tick counters are bypassed entirely -- zero overhead.