Skip to content

Commit 6cf798c

Browse files
committed
[benchmark] Trap if deterministic hashing isn't enabled
1 parent a723e6d commit 6cf798c

File tree

1 file changed

+32
-0
lines changed

1 file changed

+32
-0
lines changed

benchmark/utils/DriverUtils.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ struct TestConfig {
104104
// Should we log the measurement metadata?
105105
let logMeta: Bool
106106

107+
// Allow running with nondeterministic hashing?
108+
var allowNondeterministicHashing: Bool
109+
107110
/// After we run the tests, should the harness sleep to allow for utilities
108111
/// like leaks that require a PID to run on the test harness.
109112
let afterRunSleep: UInt32?
@@ -128,6 +131,7 @@ struct TestConfig {
128131
var verbose: Bool?
129132
var logMemory: Bool?
130133
var logMeta: Bool?
134+
var allowNondeterministicHashing: Bool?
131135
var action: TestAction?
132136
var tests: [String]?
133137
}
@@ -191,6 +195,10 @@ struct TestConfig {
191195
p.addArgument("--list", \.action, defaultValue: .listTests,
192196
help: "don't run the tests, just log the list of test \n" +
193197
"numbers, names and tags (respects specified filters)")
198+
p.addArgument("--allow-nondeterministic-hashing",
199+
\.allowNondeterministicHashing, defaultValue: true,
200+
help: "Don't trap when running without the \n" +
201+
"SWIFT_DETERMINISTIC_HASHING=1 environment variable")
194202
p.addArgument(nil, \.tests) // positional arguments
195203

196204
let c = p.parse()
@@ -208,6 +216,7 @@ struct TestConfig {
208216
logMeta = c.logMeta ?? false
209217
afterRunSleep = c.afterRunSleep
210218
action = c.action ?? .run
219+
allowNondeterministicHashing = c.allowNondeterministicHashing ?? false
211220
tests = TestConfig.filterTests(registeredBenchmarks,
212221
tests: c.tests ?? [],
213222
tags: c.tags ?? [],
@@ -671,6 +680,17 @@ final class TestRunner {
671680
}
672681
}
673682

683+
extension Hasher {
684+
static var isDeterministic: Bool {
685+
// This is a quick test for deterministic hashing.
686+
// When hashing uses a random seed, each `Set` value
687+
// contains its members in some unique, random order.
688+
let set1 = Set(0 ..< 100)
689+
let set2 = Set(0 ..< 100)
690+
return set1.elementsEqual(set2)
691+
}
692+
}
693+
674694
public func main() {
675695
let config = TestConfig(registeredBenchmarks)
676696
switch (config.action) {
@@ -682,6 +702,18 @@ public func main() {
682702
print(testDescription)
683703
}
684704
case .run:
705+
if !config.allowNondeterministicHashing && !Hasher.isDeterministic {
706+
fatalError("""
707+
Benchmark runs require deterministic hashing to be enabled.
708+
709+
This prevents spurious regressions in hashed collection performance.
710+
You can do this by setting the SWIFT_DETERMINISTIC_HASHING environment
711+
variable to 1.
712+
713+
If you know what you're doing, you can disable this check by passing
714+
the option '--allow-nondeterministic-hashing to the benchmarking executable.
715+
""")
716+
}
685717
TestRunner(config).runBenchmarks()
686718
if let x = config.afterRunSleep {
687719
sleep(x)

0 commit comments

Comments
 (0)