From f5957b4625ec4a702d1c1f7657bc32d853a0c986 Mon Sep 17 00:00:00 2001 From: Easton Man Date: Fri, 10 Jul 2026 15:48:19 +0800 Subject: [PATCH] feat(gapbs): size per-case trial counts to a runtime target bfs, sssp, and bc pick a fresh random source each trial, so more trials sample more sources; pr, cc, and tc repeat identical work regardless. Size the trial count of the source-dependent kernels per case from measured single-trial guest runtimes, so each such case runs for roughly TARGET_TRIAL_SECONDS -- cheap cases (bfs/road) get many trials, expensive ones (sssp/web) stay at one. Source-independent kernels remain single-trial. This trades source coverage against cost through one tunable target. Assisted-by: Claude Opus 4.8 --- workloads/linux/gapbs/README.md | 25 +++++++++++++++--- workloads/linux/gapbs/gapbs-package.py | 35 +++++++++++++++++++++----- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/workloads/linux/gapbs/README.md b/workloads/linux/gapbs/README.md index 0343096..2685213 100644 --- a/workloads/linux/gapbs/README.md +++ b/workloads/linux/gapbs/README.md @@ -3,21 +3,38 @@ NEMU SimPoint checkpoint images for the [GAP Benchmark Suite](https://github.com/sbeamer/gapbs). Each workload is one `kernel × graph` pair: a static, single-threaded GAPBS kernel that loads a -pre-serialized reference graph and runs a single trial. There are 18 cases — +pre-serialized reference graph and runs one or more trials. There are 18 cases — the cartesian product of six kernels and three graphs. | kernel | graph file | arguments | |--------|---------------|-----------| -| bfs | `.sg` | `-n 1` | -| sssp | `.wsg` | `-d -n 1` (road `50000`; twitter/web `2`) | +| bfs | `.sg` | `-n ` | +| sssp | `.wsg` | `-d -n ` (delta: road `50000`; twitter/web `2`) | | pr | `.sg` | `-i 1000 -t 1e-4 -n 1` | | cc | `.sg` | `-n 1` | -| bc | `.sg` | `-i 1 -n 1` | +| bc | `.sg` | `-i 1 -n ` | | tc | `U.sg` | `-n 1` | Graphs: `road`, `twitter`, `web`. Cases are named `_` (e.g. `bfs_road`, `sssp_web`). +## Trials + +`bfs`, `sssp`, and `bc` pick a new random source each trial, so more trials +sample more sources. Their trial count is sized per case (`num_trials` in +`gapbs-package.py`) so every such case runs for roughly the same time — cheap +cases get many trials, expensive ones get few: + +| kernel | road | twitter | web | +|--------|-----:|--------:|----:| +| bfs | 33 | 13 | 2 | +| sssp | 39 | 2 | 1 | +| bc | 26 | 2 | 1 | + +`pr`, `cc`, and `tc` are source-independent — every trial is identical work, so +they stay at a single trial regardless. Raise or lower `TARGET_TRIAL_SECONDS` +to rebalance. + ## Building ```sh diff --git a/workloads/linux/gapbs/gapbs-package.py b/workloads/linux/gapbs/gapbs-package.py index 2f31987..6f22d14 100755 --- a/workloads/linux/gapbs/gapbs-package.py +++ b/workloads/linux/gapbs/gapbs-package.py @@ -10,17 +10,40 @@ "sssp": ".wsg", "tc": "U.sg"} SSSP_DELTA = {"road": "50000", "twitter": "2", "web": "2"} +# Only bfs/sssp/bc pick a new random source per trial, so only they gain from +# more trials; pr/cc/tc repeat identical work and stay at a single trial. +SOURCE_DEPENDENT = {"bfs", "sssp", "bc"} + +# Measured single-trial guest runtime (seconds) from the n=1 checkpoint run: +# (graph-load "Read Time", one-kernel "Trial Time"). Trials are sized so each +# source-dependent case runs for roughly TARGET_TRIAL_SECONDS, balancing runtime +# across cases; adjust the target to trade source coverage against cost. +_TRIAL_CALIB = { + ("bfs", "road"): (16, 24), ("bfs", "twitter"): (251, 43), ("bfs", "web"): (316, 298), + ("sssp", "road"): (23, 20), ("sssp", "twitter"): (474, 170), ("sssp", "web"): (609, 207), + ("bc", "road"): (16, 30), ("bc", "twitter"): (251, 301), ("bc", "web"): (316, 358), +} +TARGET_TRIAL_SECONDS = 800 +MAX_TRIALS = 64 + +def num_trials(kernel, graph): + if kernel not in SOURCE_DEPENDENT: + return 1 + read, trial = _TRIAL_CALIB[(kernel, graph)] + return max(1, min(MAX_TRIALS, round((TARGET_TRIAL_SECONDS - read) / trial))) + # initramfs newc cpio caps each file at 4 GiB (2**32); split any graph at/above it. SPLIT_THRESHOLD = 4 * 1024**3 PART_SIZE = "3G" # each part is well under the 4 GiB cpio limit def kernel_args(kernel, graph): - if kernel == "bfs": return ["-n", "1"] - if kernel == "cc": return ["-n", "1"] - if kernel == "pr": return ["-i", "1000", "-t", "1e-4", "-n", "1"] - if kernel == "bc": return ["-i", "1", "-n", "1"] - if kernel == "sssp": return ["-d", SSSP_DELTA[graph], "-n", "1"] - if kernel == "tc": return ["-n", "1"] + n = str(num_trials(kernel, graph)) + if kernel == "bfs": return ["-n", n] + if kernel == "cc": return ["-n", n] + if kernel == "pr": return ["-i", "1000", "-t", "1e-4", "-n", n] + if kernel == "bc": return ["-i", "1", "-n", n] + if kernel == "sssp": return ["-d", SSSP_DELTA[graph], "-n", n] + if kernel == "tc": return ["-n", n] raise RuntimeError(f"unknown kernel {kernel}") def all_cases():