A declarative framework for composable performance evaluation of system software.

Describe an experiment once as a campaign: benchkit fetches, builds, runs, and collects, then turns the results into publication-ready plots.

pip install pybenchkit
campaign.py Python
from benchkit import CampaignCartesianProduct
from benchkit.benches.leveldb import LevelDBBench

campaign = CampaignCartesianProduct(
    name="leveldb_campaign",
    benchmark=LevelDBBench(),
    variables={
        "bench_name": ["readrandom", "seekrandom"],
        "nb_threads": [2, 4, 8],
    },
    nb_runs=3,
    duration_s=10,
)

campaign.run()
campaign.generate_graph(
    plot_name="lineplot",
    x="nb_threads",
    y="throughput",
    hue="bench_name",
)

Overview

What it is

benchkit is a declarative framework for composable performance evaluation of system software. Instead of the ad-hoc shell scripts that performance studies typically accumulate, an experiment is described once as a campaign, and the framework automates the full fetch, build, run, and collect pipeline. Benchmarks, system mechanisms, and profilers are composed as reusable components, so the same experiment description runs reproducibly and unattended across machines and architectures, and produces self-documenting result directories.

benchkit ships integrations for standard benchmarks (SPEC CPU 2017 and 2026, LevelDB, RocksDB, KyotoCabinet, microbenchmarks) and makes it straightforward to add custom ones, composable system mechanisms (lock interposition, user-space scheduling, CPU placement), and profiling tools (perf stat, perf record, differential flame graphs, and paper-quality plots).

Features

Pieces that snap together

Declarative campaigns

Define what to benchmark, not how. Campaigns describe benchmarks, parameter spaces, and run counts as pure data.

Composable wrappers

Layer mechanisms freely: taskset pinning, perf profiling, custom schedulers. Each wrapper is independent and reusable.

Multi-platform

The same campaign runs on x86 laptops, ARM servers, and NUMA machines. A platform abstraction handles the differences.

Publication-ready plots

Automatic CSV collection and graph generation: strip plots, bar charts, and flame graphs, ready for your paper.

From the paper

Six case studies, six real figures

Every figure below comes from the ICPE 2026 paper and was generated by a benchkit campaign.

Two charts from the CPU placement case study: a strip plot of runtime under no pinning, P-core pinning, and E-core pinning, where P-cores are fastest and E-cores slowest, and a bar chart of operations per CPU id that exposes the fast and slow cores of a hybrid CPU.

CPU placement policies

How thread-to-core pinning affects performance on hybrid-core CPUs (P-cores and E-cores).

  • TasksetWrap
  • Hybrid x86

Placement campaign Heater campaign

Line charts of throughput versus number of threads on KyotoCabinet, LevelDB, and RocksDB workloads for eight lock implementations: CAS, ticket, MCS, Hemlock, TTAS, CNA, and HMCS locks against the glibc pthread mutex baseline.

Lock library comparison

Compare pthread mutexes, CAS spinlocks, and MCS queue locks under contention via transparent library interposition (tilt).

  • tilt
  • NUMA ARM

View the campaign

Line charts of throughput versus number of threads on KyotoCabinet, LevelDB, and RocksDB workloads for six scheduling policies, including the default Linux scheduler, NUMA-aware FAR and CLOSE placement, AsymSched, and sharing-aware mapping and scheduling.

Custom scheduler hooks

Plug in user-space schedulers via start/end hooks; benchmark policies without kernel changes (schedkit).

  • schedkit
  • NUMA ARM

View the campaign

Bar charts per scheduler showing throughput next to perf stat counters: context switches, CPU migrations, page faults, and cache misses.

Hardware counters

Wrap benchmarks with perf stat to collect cache misses, mispredictions, and IPC, merged into results.

  • PerfStatWrap

View the campaign

Differential flame graph of LevelDB readrandom comparing an MCS lock against a CAS lock at 32 threads; the pthread_mutex_lock frame is highlighted in bright red, indicating more time spent in that call with the CAS lock than with the MCS lock.

Differential flame graphs

Record perf profiles and generate differential flame graphs to see where time shifts between configs.

  • PerfRecordWrap

View the campaign

Strip plot of SPEC CPU runtime under no pinning, P-core pinning, and E-core pinning; runs pinned to P-cores are fastest and runs pinned to E-cores are slowest.

SPEC CPU 2017

Run industry-standard SPEC benchmarks with the same declarative API and the same wrappers.

  • SPECCPU2017Bench

View the campaign

Research

Cite benchkit

If you use benchkit in your research, consider citing the ICPE 2026 paper, by Antonio Paolillo, Mats Van Molle, and Ken Hasselmann.

The paper explains the design of the framework and walks through the six case studies shown above, from CPU placement on hybrid-core machines to differential flame graphs.

BibTeX
@inproceedings{Paolillo_benchkit_2026,
  author    = {Paolillo, Antonio and Van Molle, Mats and Hasselmann, Ken},
  title     = {benchkit: A Declarative Framework for Composable Performance Evaluation of System Software},
  booktitle = {Proceedings of the 17th ACM/SPEC International Conference on Performance Engineering (ICPE '26)},
  year      = {2026},
  pages     = {170--183},
  publisher = {ACM},
  doi       = {10.1145/3777884.3796997},
  url       = {https://doi.org/10.1145/3777884.3796997}
}