From fe3c0c66e73e2e5ed61066246d65d109167f9dc7 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Mon, 26 Oct 2026 17:32:40 +0100 Subject: [PATCH] feat: seed Go's global math/rand deterministically Set GODEBUG=randautoseed=0 on benchmark processes in every mode, so the global math/rand source starts from Seed(1) instead of a random seed and code that draws from it behaves the same on every run. The setting is prepended to the user's GODEBUG. Go uses the last occurrence of a setting, so the user's other settings are kept and an explicit randautoseed from the user still wins. Refs COD-4013 --- crates/runner-shared/src/runtime_env/go.rs | 34 +++++++++++++++++++++ crates/runner-shared/src/runtime_env/mod.rs | 2 ++ 2 files changed, 36 insertions(+) create mode 100644 crates/runner-shared/src/runtime_env/go.rs diff --git a/crates/runner-shared/src/runtime_env/go.rs b/crates/runner-shared/src/runtime_env/go.rs new file mode 100644 index 000000000..ba00d8ce9 --- /dev/null +++ b/crates/runner-shared/src/runtime_env/go.rs @@ -0,0 +1,34 @@ +const GODEBUG: &str = "GODEBUG"; +/// Starts the global `math/rand` source from `Seed(1)` instead of a random seed. +const RANDAUTOSEED_OFF: &str = "randautoseed=0"; + +/// Go env: +/// - `GODEBUG=randautoseed=0`: deterministic global `math/rand` in every mode. +pub(super) fn env() -> Vec<(&'static str, String)> { + let user_godebug = std::env::var(GODEBUG).ok(); + vec![(GODEBUG, godebug(user_godebug.as_deref()))] +} + +/// Prepends our settings to the user's `GODEBUG`. Go uses the last occurrence +/// of a setting, so an explicit `randautoseed` from the user still wins. +fn godebug(user_godebug: Option<&str>) -> String { + match user_godebug { + Some(user) if !user.is_empty() => format!("{RANDAUTOSEED_OFF},{user}"), + _ => RANDAUTOSEED_OFF.to_owned(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn keeps_user_settings_after_ours() { + assert_eq!(godebug(None), "randautoseed=0"); + assert_eq!(godebug(Some("")), "randautoseed=0"); + assert_eq!( + godebug(Some("gctrace=1,randautoseed=1")), + "randautoseed=0,gctrace=1,randautoseed=1" + ); + } +} diff --git a/crates/runner-shared/src/runtime_env/mod.rs b/crates/runner-shared/src/runtime_env/mod.rs index 8b70f1b16..fc3b2631f 100644 --- a/crates/runner-shared/src/runtime_env/mod.rs +++ b/crates/runner-shared/src/runtime_env/mod.rs @@ -1,5 +1,6 @@ use crate::measurement_mode::MeasurementMode; +mod go; mod java; mod python; @@ -10,5 +11,6 @@ pub fn env(mode: MeasurementMode) -> Vec<(&'static str, String)> { let mut env = vec![(RUNNER_MODE_ENV, mode.runner_mode_env_value().to_owned())]; env.extend(python::env(mode)); env.extend(java::env(mode)); + env.extend(go::env()); env }