@@ -104,6 +104,9 @@ struct TestConfig {
104
104
// Should we log the measurement metadata?
105
105
let logMeta : Bool
106
106
107
+ // Allow running with nondeterministic hashing?
108
+ var allowNondeterministicHashing : Bool
109
+
107
110
/// After we run the tests, should the harness sleep to allow for utilities
108
111
/// like leaks that require a PID to run on the test harness.
109
112
let afterRunSleep : UInt32 ?
@@ -128,6 +131,7 @@ struct TestConfig {
128
131
var verbose : Bool ?
129
132
var logMemory : Bool ?
130
133
var logMeta : Bool ?
134
+ var allowNondeterministicHashing : Bool ?
131
135
var action : TestAction ?
132
136
var tests : [ String ] ?
133
137
}
@@ -191,6 +195,10 @@ struct TestConfig {
191
195
p. addArgument ( " --list " , \. action, defaultValue: . listTests,
192
196
help: " don't run the tests, just log the list of test \n " +
193
197
" 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 " )
194
202
p. addArgument ( nil , \. tests) // positional arguments
195
203
196
204
let c = p. parse ( )
@@ -208,6 +216,7 @@ struct TestConfig {
208
216
logMeta = c. logMeta ?? false
209
217
afterRunSleep = c. afterRunSleep
210
218
action = c. action ?? . run
219
+ allowNondeterministicHashing = c. allowNondeterministicHashing ?? false
211
220
tests = TestConfig . filterTests ( registeredBenchmarks,
212
221
tests: c. tests ?? [ ] ,
213
222
tags: c. tags ?? [ ] ,
@@ -671,6 +680,17 @@ final class TestRunner {
671
680
}
672
681
}
673
682
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
+
674
694
public func main( ) {
675
695
let config = TestConfig ( registeredBenchmarks)
676
696
switch ( config. action) {
@@ -682,6 +702,18 @@ public func main() {
682
702
print ( testDescription)
683
703
}
684
704
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
+ }
685
717
TestRunner ( config) . runBenchmarks ( )
686
718
if let x = config. afterRunSleep {
687
719
sleep ( x)
0 commit comments