Skip to content

Commit 9a7caa7

Browse files
committed
Merge remote-tracking branch 'origin/master' into codspeed-optim-replace-weak-hash-with-fnv-1a-and-add-stored-hash-1784905415320
2 parents 0dc6597 + b0d4717 commit 9a7caa7

3 files changed

Lines changed: 91 additions & 11 deletions

File tree

.github/workflows/codspeed.yml

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,18 @@ on:
66
- master
77
pull_request:
88
workflow_dispatch:
9+
schedule:
10+
# Keep the master-scoped caches warm: GitHub evicts caches unused for
11+
# 7 days, and PRs can only restore caches from their own ref or master.
12+
- cron: "0 6 * * 1,4"
913

1014
jobs:
1115
benchmarks:
1216
runs-on: codspeed-macro
13-
timeout-minutes: 15
17+
# Bumped from 15m: the walltime benchmarks now run a minimum number of
18+
# rounds (see the "Generate CodSpeed config" step) instead of a single
19+
# sample, which increases the benchmarking phase's duration.
20+
timeout-minutes: 25
1421
strategy:
1522
fail-fast: false
1623
matrix:
@@ -51,7 +58,6 @@ jobs:
5158

5259
# Build and install Valgrind
5360
- name: Update apt-get cache
54-
if: steps.valgrind-cache.outputs.cache-hit != 'true'
5561
run: |
5662
sudo apt-get update
5763
@@ -85,7 +91,6 @@ jobs:
8591
just install ${{ matrix.valgrind }}
8692
8793
# Ensure libc6-dev is installed for Valgrind to work properly
88-
sudo apt-get update
8994
sudo apt-get install -y libc6-dev stress-ng
9095
9196
- name: Verify Valgrind build
@@ -104,9 +109,20 @@ jobs:
104109
# Generate the codspeed.yml for this Valgrind version. The script derives
105110
# the version label from `valgrind --version`, so each matrix job emits its
106111
# own config (e.g. valgrind.codspeed / valgrind-3.26.0 / valgrind-3.25.1).
112+
#
113+
# The walltime sampling knobs are passed explicitly so the
114+
# stability/duration trade-off is visible and tunable here. `min-rounds`
115+
# gives each benchmark several samples for a stable estimate; `max-time`
116+
# caps per-benchmark wall time to keep the job within `timeout-minutes`.
107117
- name: Generate CodSpeed config
108118
working-directory: bench
109-
run: ./generate_config.py --valgrind /usr/local/bin/valgrind --output codspeed.yml
119+
run: >-
120+
./generate_config.py
121+
--valgrind /usr/local/bin/valgrind
122+
--output codspeed.yml
123+
--warmup-time 1s
124+
--min-rounds 5
125+
--max-time 20s
110126
111127
- name: Run the benchmarks
112128
uses: CodSpeedHQ/action@main

Justfile

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,9 @@ build-in dir:
3434
# 64-bit Capstone, so the 32-bit secondary build (which has no Capstone) is
3535
# skipped.
3636
./configure --enable-only64bit
37-
make include/vgversion.h
38-
make -j$(nproc) -C VEX
39-
make -j$(nproc) -C coregrind
40-
make -j$(nproc) -C callgrind
37+
# Full parallel build: `make install` depends on `all`, so anything
38+
# skipped here gets rebuilt serially at install time instead.
39+
make -j$(nproc)
4140
4241
4342
install version:

bench/generate_config.py

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,26 @@
9393
# Label produced by `valgrind_version` for CodSpeed's custom build.
9494
CODSPEED_VERSION = "valgrind.codspeed"
9595

96+
# Default walltime sampling settings applied to every benchmark.
97+
#
98+
# Valgrind runs are slow (a single execution can take seconds), so with the
99+
# runner's default `max-time` of 3s the slowest benchmarks only complete a
100+
# single round. A one-sample estimate is dominated by run-to-run noise, which
101+
# is what makes these benchmarks unstable.
102+
#
103+
# We therefore ask the harness for a minimum number of measured rounds so
104+
# CodSpeed always has several samples to pick the representative time from,
105+
# plus one warmup round to discard cold-start effects (process spawn, page
106+
# faults, disk cache). `max-time` bounds the total wall time so fast
107+
# benchmarks don't over-run; when it is reached before `min-rounds`, the
108+
# harness stops early (max-time takes priority), keeping the workflow bounded.
109+
#
110+
# These are exposed as CLI flags so the CI workflow (and local runs) can tune
111+
# the stability/duration trade-off without editing this script.
112+
DEFAULT_WARMUP_TIME = "1s"
113+
DEFAULT_MIN_ROUNDS = 5
114+
DEFAULT_MAX_TIME = "20s"
115+
96116

97117
def valgrind_version(valgrind_path: str) -> str:
98118
"""Return the normalized version label used in benchmark ids.
@@ -115,7 +135,12 @@ def valgrind_version(valgrind_path: str) -> str:
115135
return version
116136

117137

118-
def build_config(valgrind_paths: list) -> dict:
138+
def build_config(
139+
valgrind_paths: list,
140+
warmup_time: str = DEFAULT_WARMUP_TIME,
141+
min_rounds: int = DEFAULT_MIN_ROUNDS,
142+
max_time: str = DEFAULT_MAX_TIME,
143+
) -> dict:
119144
"""Build the codspeed.yml document for all valgrind builds and commands."""
120145
benchmarks = []
121146
for valgrind_path in valgrind_paths:
@@ -132,7 +157,18 @@ def build_config(valgrind_paths: list) -> dict:
132157
)
133158
benchmarks.append({"name": name, "exec": exec_cmd})
134159

160+
# Root-level walltime options apply to every benchmark so all runs share the
161+
# same sampling policy. `min-rounds` guarantees several samples for a stable
162+
# estimate; `warmup-time` discards cold-start effects; `max-time` caps the
163+
# total per-benchmark wall time so the workflow stays bounded.
164+
walltime_options = {
165+
"warmup-time": warmup_time,
166+
"min-rounds": min_rounds,
167+
"max-time": max_time,
168+
}
169+
135170
return {
171+
"options": {"walltime": walltime_options},
136172
"benchmarks": benchmarks,
137173
}
138174

@@ -157,17 +193,46 @@ def main():
157193
default="codspeed.yml",
158194
help="Path to write the generated config (default: codspeed.yml)",
159195
)
196+
parser.add_argument(
197+
"--warmup-time",
198+
type=str,
199+
default=DEFAULT_WARMUP_TIME,
200+
help="Walltime warmup duration applied to every benchmark, discarded "
201+
f"before measurement (default: {DEFAULT_WARMUP_TIME}). Set to '0s' to disable.",
202+
)
203+
parser.add_argument(
204+
"--min-rounds",
205+
type=int,
206+
default=DEFAULT_MIN_ROUNDS,
207+
help="Minimum number of measured rounds per benchmark; more rounds give "
208+
f"a more stable estimate (default: {DEFAULT_MIN_ROUNDS}).",
209+
)
210+
parser.add_argument(
211+
"--max-time",
212+
type=str,
213+
default=DEFAULT_MAX_TIME,
214+
help="Maximum total wall time per benchmark (includes warmup). Bounds the "
215+
"workflow duration; when reached before --min-rounds it takes priority "
216+
f"(default: {DEFAULT_MAX_TIME}).",
217+
)
160218
args = parser.parse_args()
161219

162-
config = build_config(args.valgrinds)
220+
config = build_config(
221+
args.valgrinds,
222+
warmup_time=args.warmup_time,
223+
min_rounds=args.min_rounds,
224+
max_time=args.max_time,
225+
)
163226

164227
with open(args.output, "w") as f:
165228
json.dump(config, f, indent=2)
166229
f.write("\n")
167230

168231
print(
169232
f"Wrote {args.output} with {len(config['benchmarks'])} benchmarks "
170-
f"({len(args.valgrinds)} valgrind builds x {len(COMMANDS)} commands x {len(CONFIGS)} configs)",
233+
f"({len(args.valgrinds)} valgrind builds x {len(COMMANDS)} commands x {len(CONFIGS)} configs); "
234+
f"walltime options: warmup-time={args.warmup_time}, min-rounds={args.min_rounds}, "
235+
f"max-time={args.max_time}",
171236
file=sys.stderr,
172237
)
173238

0 commit comments

Comments
 (0)