Skip to content

Track C++ Google Benchmark microbenchmarks via ASV#2943

Open
vasil-pashov wants to merge 7 commits intomasterfrom
track-google-bench-asv
Open

Track C++ Google Benchmark microbenchmarks via ASV#2943
vasil-pashov wants to merge 7 commits intomasterfrom
track-google-bench-asv

Conversation

@vasil-pashov
Copy link
Copy Markdown
Collaborator

@vasil-pashov vasil-pashov commented Feb 27, 2026

What

Adds C++ microbenchmark tracking alongside the existing Python ASV benchmarks.
Each Google Benchmark function becomes a separate ASV class so results appear
in the same dashboard with the same tooling.

New files

  • python/benchmarks/cpp_microbenchmarks.py — ASV module that runs the C++
    benchmark binary (ARCTICDB_CPP_BENCHMARKS_BINARY) and exposes each function
    as an ASV track_* class. Parameter variants (BM_foo/10000/1) become ASV
    params; the unit = "ms" attribute is set so graphs show the correct Y-axis
    label. Per-benchmark flag overrides are supported via BENCHMARK_FLAGS.
  • .github/workflows/benchmark_cpp_microbenchmarks.yml — callable workflow
    that builds the benchmarks binary, then runs asv run --bench "^cpp_microbenchmarks\.".
    Results are stored in a separate ArcticDB library
    (cpp_microbenchmarks_asv_results) to avoid key conflicts with Python results
    stored under the same 8-char commit hash.

Changes to analysis_workflow.yml

  • New benchmark_cpp_microbenchmarks job runs in parallel with benchmark_commits,
    each on its own EC2 runner — no shared machine contention.
  • continue-on-error: true so C++ regressions appear as a visible failed job
    but do not block PRs or the Python benchmark publish step.
  • The publish job's if condition uses always() so a C++ failure does not
    prevent Python results from being published.
  • run-asv-check-script now builds the C++ benchmarks binary before running
    asv_checks.py. Without the binary cpp_microbenchmarks.py registers zero
    classes, causing asv_checks.py to rewrite benchmarks.json without C++
    entries and fail the hash-change check on every PR.

Why

C++ micro-benchmark results were previously invisible. This change surfaces
regressions in CI alongside the Python benchmark history without blocking
existing workflows.

Pre-existing behaviour unchanged

Python ASV benchmarks (benchmark_commits) are unmodified. The publish job and
regression detection logic for Python results are unchanged.

@vasil-pashov vasil-pashov force-pushed the track-google-bench-asv branch from 0bd2cee to d1b5fb4 Compare February 27, 2026 17:30
@vasil-pashov vasil-pashov added no-release-notes This PR shouldn't be added to release notes. patch Small change, should increase patch version labels Feb 27, 2026
@vasil-pashov vasil-pashov marked this pull request as ready for review February 27, 2026 17:35
## What

Adds C++ microbenchmark tracking alongside the existing Python ASV benchmarks.
Each Google Benchmark function becomes a separate ASV class so results appear
in the same dashboard with the same tooling.

### New files
- `python/benchmarks/cpp_microbenchmarks.py` — ASV module that runs the C++
  benchmark binary (`ARCTICDB_CPP_BENCHMARKS_BINARY`) and exposes each function
  as an ASV `track_*` class. Parameter variants (`BM_foo/10000/1`) become ASV
  params; the `unit = "ms"` attribute is set so graphs show the correct Y-axis
  label. Per-benchmark flag overrides are supported via `BENCHMARK_FLAGS`.
- `.github/workflows/benchmark_cpp_microbenchmarks.yml` — callable workflow
  that builds the benchmarks binary, then runs `asv run --bench "^cpp_microbenchmarks\."`.
  Results are stored in a separate ArcticDB library
  (`cpp_microbenchmarks_asv_results`) to avoid key conflicts with Python results
  stored under the same 8-char commit hash.

### Changes to `analysis_workflow.yml`
- New `benchmark_cpp_microbenchmarks` job runs in parallel with `benchmark_commits`,
  each on its own EC2 runner — no shared machine contention.
- `continue-on-error: true` so C++ regressions appear as a visible failed job
  but do not block PRs or the Python benchmark publish step.
- The publish job's `if` condition uses `always()` so a C++ failure does not
  prevent Python results from being published.
- `run-asv-check-script` now builds the C++ benchmarks binary before running
  `asv_checks.py`. Without the binary `cpp_microbenchmarks.py` registers zero
  classes, causing `asv_checks.py` to rewrite `benchmarks.json` without C++
  entries and fail the hash-change check on every PR.

## Why

C++ micro-benchmark results were previously invisible. This change surfaces
regressions in CI alongside the Python benchmark history without blocking
existing workflows.

## Pre-existing behaviour unchanged

Python ASV benchmarks (`benchmark_commits`) are unmodified. The publish job and
regression detection logic for Python results are unchanged.
@vasil-pashov vasil-pashov force-pushed the track-google-bench-asv branch from d1b5fb4 to 09d395c Compare February 27, 2026 17:39
python -m asv compare -s -f 1.15 $(cat master_commit_hash.txt) HEAD > results.txt || exit 1

# Get rid of the master results so we don't re-add them to the database.
rm python/.asv/results/**/$(cat master_commit_hash.txt)*.json || exit 1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rm command fails (exits 1) if no files match the glob — which would happen if ASV placed results in an unexpected directory or if master_commit_hash.txt is empty. Because bash globbing passes the unexpanded pattern as-is to rm when there are no matches, rm always sees a "file not found" and exits non-zero. The || exit 1 guard then immediately aborts the step.

Use rm -f to silently succeed when the file is missing:

Suggested change
rm python/.asv/results/**/$(cat master_commit_hash.txt)*.json || exit 1
rm -f python/.asv/results/**/$(cat master_commit_hash.txt)*.json

Also note that ** requires shopt -s globstar to traverse recursively in bash; without it only the immediate directory is searched. Check that the shell default in the container has globstar enabled, or switch to find:

find python/.asv/results -name "$(cat master_commit_hash.txt)*.json" -delete



# Register one ASV class per C++ benchmark function in this module's namespace.
for func_name, variants in group_benchmarks(discover_benchmark_names()).items():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module-level code runs a subprocess at import time. If the binary exists but --benchmark_list_tests exits non-zero (e.g., a crash or mislinked build), result.check_returncode() inside discover_benchmark_names() raises CalledProcessError, which propagates unhandled here and causes the entire module import to fail — with a confusing traceback rather than "no benchmarks registered".

Consider wrapping the discovery to degrade gracefully:

try:
    _names = discover_benchmark_names()
except Exception as exc:
    import warnings
    warnings.warn(f"cpp_microbenchmarks: benchmark discovery failed: {exc}", RuntimeWarning)
    _names = []

for func_name, variants in group_benchmarks(_names).items():
    globals()[to_identifier(func_name)] = make_benchmark_class(func_name, variants)

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Feb 27, 2026

ArcticDB Code Review Summary

PR: Track C++ Google Benchmark microbenchmarks via ASV (#2943)
Reviewed: full diff (delta fetch unavailable; all commits reviewed)

API & Compatibility

  • No breaking changes to public Python API (Arctic, Library, NativeVersionStore)
  • Deprecation protocol not applicable (no API removed/renamed)
  • On-disk format unchanged
  • Protobuf schema unchanged
  • Key types and ValueType enum unchanged

Memory & Safety

  • No C++ memory management involved (Python/CI-only change)
  • RAII not applicable
  • No GIL concerns

Correctness

  • WARNING: rm without -f in Benchmark against master step (line 120): rm python/.asv/results/will exit 1 if no files match. Also, ** recursive glob requires shopt -s globstar which is not set. Use rm -f or find -delete. (Still not addressed.)
  • FIXED: github.ref_name sanitization - replace(github.ref_name, '/', '_') is now used in the library name, preventing invalid names from branch names containing /.
  • FIXED: Import-time CalledProcessError - discover_benchmark_names() returns [] on non-zero return code; combined with the binary-exists guard, all failure modes handled gracefully.
  • OK: needs.start_ec2_runner.result == 'success' guard correctly prevents benchmark_commit from running when EC2 start fails.
  • OK: run_benchmark correctly handles iteration vs. aggregate rows, unit conversion, and temp-file cleanup via finally: os.unlink.
  • OK: Closure capture in make_benchmark_class is correct (new scope per call).
  • OK: gbench_escape correctly handles ECMAScript regex escaping.

Code Quality

  • No duplicated logic; new workflow follows patterns from existing benchmark_commits job.
  • always() && !cancelled() pattern on benchmark_commit is consistent with other workflows.
  • always() on stop-ec2-runner and publish jobs correctly ensures cleanup/publishing even on failure.
  • Typo fixes (supress to suppress) in both workflow files.
  • cpp_microbenchmarks.py module docstring clearly explains env var, fallback behaviour, and BENCHMARK_FLAGS override.

Testing

  • Infrastructure/CI change - no behavioural change to ArcticDB data path; no functional tests required.
  • Existing Python ASV benchmarks unaffected.

Build & Dependencies

  • FIXED: actions/checkout@v4 - Upgraded from @v3.3.0 (Node.js 16) to @v4 (Node.js 20).
  • INFO: ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true remains in benchmark_cpp_microbenchmarks.yml - now needed for mozilla-actions/sccache-action@v0.0.9 rather than checkout. Consider upgrading sccache-action to eliminate the flag entirely.
  • OK: asv added to publish step pip install - correctly required for the new ASV check.
  • OK: C++ benchmarks build step correctly ensures benchmark discovery works during the ASV hash-change check.

Security

  • FIXED: Missing permissions: blocks - Both analysis_workflow.yml and benchmark_cpp_microbenchmarks.yml now declare permissions: contents: read. Resolves all GitHub Advanced Security findings.
  • No hardcoded credentials (all via secrets).
  • inputs.commit used as SHA^! in shell - hex-only commit hashes are injection-safe.

PR Title & Description

  • Title is clear and imperative.
  • Description explains what changed and why.
  • No public API changes.
  • No linked GitHub issue referenced.
  • PR is not labelled (consider adding enhancement or ci).

Documentation

  • No public API docstrings affected.
  • cpp_microbenchmarks.py module docstring clearly explains the environment variable, fallback behaviour, and BENCHMARK_FLAGS override mechanism.

- Guard benchmark_commit job on EC2 runner startup success to avoid
  wasting the job slot when runner acquisition fails
- Replace check_returncode() with returncode check in
  discover_benchmark_names to prevent an import-time CalledProcessError
  that ASV cannot handle gracefully
- Extract master_commit_hash.txt into a variable and quote it in the
  shell comparison and rm commands to avoid word-splitting bugs
- Fix typo "supress" -> "suppress" in two workflow comments
vasil-pashov and others added 5 commits March 4, 2026 14:17
…tain permissions

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…tain permissions

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
apt update
apt install -y git
python -m pip install arcticdb[Testing] "protobuf<6"
python -m pip install arcticdb[Testing] asv "protobuf<6"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asv is already in the Testing deps?

env:
CMAKE_BUILD_PARALLEL_LEVEL: ${{vars.CMAKE_BUILD_PARALLEL_LEVEL}}

- name: Build C++ benchmarks binary
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was already done in the other workflow file?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of duplication with the analysis workflow. Can't we just fold the new steps in to that one?

shell: bash -l {0}
run: |
# Look up the most recent master commit stored in our ASV database and compare against it.
ARCTICDB_REAL_S3_BUCKET=${ASV_RESULTS_BUCKET} ./tooling_venv/bin/python build_tooling/transform_asv_results.py --mode extract-recent --arcticdb_library cpp_microbenchmarks_asv_results || exit 1
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a totally separate library to store these? Are they presented properly in the UI?

# python/benchmarks/cpp_microbenchmarks.py → python/benchmarks → python → repo root).
REPO_ROOT = Path(__file__).resolve().parents[2]

CPP_BENCHMARKS_BINARY = Path(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make sure we blow up with a clear error message explaining that you need to build the benchmarks binary, if this is pointing at a binary that doesn't exist. Otherwise it will be hard for people to update the ASV .json file.

@poodlewars
Copy link
Copy Markdown
Collaborator

I'm fine with the script to convert the benchmarks to ASV format, but think it should all be folded in to the existing analysis workflow rather than creating a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-release-notes This PR shouldn't be added to release notes. patch Small change, should increase patch version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants