Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ jobs:
include: ${{ fromJSON(needs.arches.outputs.matrix) }}
name: Compare with base (${{ matrix.arch }}${{ matrix.cpu-features && format(', VG_CPU_FEATURES={0}', matrix.cpu-features) || '' }})
runs-on: ${{ matrix.os }}
timeout-minutes: 15
# Running every benchmark (for a shared change, or by hand) takes about
# 14 minutes on the aarch64 runners and longer on the others.
timeout-minutes: 30
# With an empty image the job runs directly on the runner.
container:
image: ${{ matrix.image || '' }}
Expand Down
30 changes: 26 additions & 4 deletions ci/bench_arches.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@
whose own files changed: `src/asm/<arch>/<module>.rs`, or the Rust API's
`src/<module>.rs` or `src/hashes/<module>.rs` (for every architecture), or
`src/<family>/<hash>.rs`, whose module is `<family>_<hash>` (e.g.
`src/hmac/sha256.rs` is `hmac_sha256`, as in `src/asm/`). The
`src/hmac/sha256.rs` is `hmac_sha256`, as in `src/asm/`), or the
modules a changed benchmark `bench/benches/primitives/<name>.rs` lists in
its `USES` (on every architecture), whose benchmarks include it. The
benchmarks decide which of them run (each lists the modules it `USES`, see
bench/benches/primitives/main.rs), and run everything for a module none of
them uses (e.g. `cpu`, `lib`, or `hashes/mod.rs`'s `mod`). Any other change
it benchmarks (e.g. the benchmarks themselves) runs every benchmark, and
`modules` is empty. The generated `src/asm/<arch>/mod.rs` only declares the
modules, so it narrows nothing either way.
it benchmarks (e.g. the benchmarks' `main.rs`, or a benchmark whose `USES`
it cannot read) runs every benchmark, and `modules` is empty. The generated
`src/asm/<arch>/mod.rs` only declares the modules, so it narrows nothing
either way.

An architecture with primitives that choose among implementations by CPU
feature is benchmarked once with every feature the runner has, and once
Expand Down Expand Up @@ -68,10 +71,25 @@
ASM = re.compile(r"src/asm/([a-z0-9_]+)/([a-z0-9_]+)\.rs$")
API = re.compile(r"src/(?:hashes/)?([a-z0-9_]+)\.rs$")
FAMILY = re.compile(r"src/(?!asm/|hashes/)([a-z0-9_]+)/([a-z0-9_]+)\.rs$")
# One algorithm's benchmark, and the modules it lists in its `USES`.
BENCH = re.compile(r"bench/benches/primitives/(?!main\.rs$)[a-z0-9_]+\.rs$")
USES = re.compile(r"pub const USES: &\[&str\] = &\[([^\]]*)\];")

ALL = None


def bench_uses(path):
"""The modules the benchmark at `path` lists in its `USES`, or None if
it cannot be read (e.g. a deleted benchmark) or lists none."""
try:
with open(path) as f:
m = USES.search(f.read())
except OSError:
return None
modules = re.findall(r'"([a-z0-9_]+)"', m[1]) if m else []
return modules or None


def arches(changed):
# The modules to benchmark on each architecture that needs it, or ALL.
needed = {}
Expand All @@ -94,6 +112,10 @@ def need(arch, module):
name = family[1] if family[2] == "mod" else f"{family[1]}_{family[2]}"
for a in PLATFORMS:
need(a, name)
elif BENCH.match(path) and (uses := bench_uses(path)):
for a in PLATFORMS:
for m in uses:
need(a, m)
elif SHARED.match(path):
for a in PLATFORMS:
needed[a] = ALL
Expand Down
Loading