Skip to content

Commit f538b49

Browse files
Use the "nearly divisionless" algorithm on all targets. (#37920)
* Use the "nearly divisionless" algorithm on all targets. We have multipliedFullWidth available everywhere now, so we don't need to carry around the old implementation on 32b targets. Also adds a few more benchmarks for random number generation. * Obscure the range boundaries for some of the new random value benchmarks. When these are visible compile-time constants, the compiler is smart enough to evaluate the division in the "nearly divisionless" algorithm, which makes it completely divisionless. That's good, but it obscures what the runtime performance of the algorithm will be when the bounds are _not_ available as compile-time constants. Thus, for some of the newly-added benchmarks, we pass the upper bound through `identity` to hide it from the optimizer (this is imperfect, but it's the simplest tool we have). We don't want to do this for all the tests for two reasons: - compile-time constant bounds are a common case that should still be reflected in our testing - we don't want to perturb existing benchmark results more than we have to.
1 parent c0ccec3 commit f538b49

File tree

2 files changed

+120
-18
lines changed

2 files changed

+120
-18
lines changed

benchmark/single-source/RandomValues.swift

Lines changed: 120 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,26 @@ public let RandomValues = [
2323
tags: [.api], legacyFactor: 100),
2424
BenchmarkInfo(name: "RandomIntegersLCG", runFunction: run_RandomIntegersLCG,
2525
tags: [.api]),
26+
BenchmarkInfo(name: "RandomInt8Def", runFunction: run_RandomInt8Def,
27+
tags: [.api], legacyFactor: 100),
28+
BenchmarkInfo(name: "RandomInt8LCG", runFunction: run_RandomInt8LCG,
29+
tags: [.api]),
30+
BenchmarkInfo(name: "RandomInt64Def", runFunction: run_RandomInt64Def,
31+
tags: [.api], legacyFactor: 100),
32+
BenchmarkInfo(name: "RandomInt64LCG", runFunction: run_RandomInt64LCG,
33+
tags: [.api]),
2634
BenchmarkInfo(name: "RandomDoubleDef", runFunction: run_RandomDoubleDef,
2735
tags: [.api], legacyFactor: 100),
2836
BenchmarkInfo(name: "RandomDoubleLCG", runFunction: run_RandomDoubleLCG,
29-
tags: [.api], legacyFactor: 2),
37+
tags: [.api]),
38+
BenchmarkInfo(name: "RandomDoubleOpaqueDef", runFunction: run_RandomDoubleOpaqueDef,
39+
tags: [.api], legacyFactor: 100),
40+
BenchmarkInfo(name: "RandomDoubleOpaqueLCG", runFunction: run_RandomDoubleOpaqueLCG,
41+
tags: [.api]),
42+
BenchmarkInfo(name: "RandomDouble01Def", runFunction: run_RandomDouble01Def,
43+
tags: [.api], legacyFactor: 100),
44+
BenchmarkInfo(name: "RandomDouble01LCG", runFunction: run_RandomDouble01LCG,
45+
tags: [.api]),
3046
]
3147

3248
/// A linear congruential PRNG.
@@ -49,19 +65,70 @@ public func run_RandomIntegersDef(_ N: Int) {
4965
for _ in 0 ..< N {
5066
var x = 0
5167
for _ in 0 ..< 1_000 {
52-
x &+= Int.random(in: 0...10_000)
68+
x &+= .random(in: 0...10_000)
5369
}
5470
blackHole(x)
5571
}
5672
}
5773

5874
@inline(never)
5975
public func run_RandomIntegersLCG(_ N: Int) {
76+
for _ in 0 ..< N {
77+
var x = 0
78+
var generator = LCRNG(seed: 0)
79+
for _ in 0 ..< 100_000 {
80+
x &+= .random(in: 0 ... 10_000, using: &generator)
81+
}
82+
blackHole(x)
83+
}
84+
}
85+
86+
@inline(never)
87+
public func run_RandomInt8Def(_ N: Int) {
88+
for _ in 0 ..< N {
89+
var x: Int8 = 0
90+
for _ in 0 ..< 1_000 {
91+
x &+= .random(in: -65 ... identity(65))
92+
}
93+
blackHole(x)
94+
}
95+
}
96+
97+
@inline(never)
98+
public func run_RandomInt8LCG(_ N: Int) {
99+
for _ in 0 ..< N {
100+
var x: Int8 = 0
101+
var generator = LCRNG(seed: 0)
102+
for _ in 0 ..< 100_000 {
103+
x &+= .random(in: -65 ... identity(65), using: &generator)
104+
}
105+
blackHole(x)
106+
}
107+
}
108+
109+
@inline(never)
110+
public func run_RandomInt64Def(_ N: Int) {
111+
for _ in 0 ..< N {
112+
var x: Int64 = 0
113+
for _ in 0 ..< 1_000 {
114+
x &+= .random(in:
115+
-5_000_000_000_000_000_000 ... identity(5_000_000_000_000_000_000)
116+
)
117+
}
118+
blackHole(x)
119+
}
120+
}
121+
122+
@inline(never)
123+
public func run_RandomInt64LCG(_ N: Int) {
60124
for _ in 0 ..< N {
61125
var x: Int64 = 0
62126
var generator = LCRNG(seed: 0)
63127
for _ in 0 ..< 100_000 {
64-
x &+= Int64.random(in: 0...10_000, using: &generator)
128+
x &+= .random(in:
129+
-5_000_000_000_000_000_000 ... identity(5_000_000_000_000_000_000),
130+
using: &generator
131+
)
65132
}
66133
blackHole(x)
67134
}
@@ -72,7 +139,7 @@ public func run_RandomDoubleDef(_ N: Int) {
72139
for _ in 0 ..< N {
73140
var x = 0.0
74141
for _ in 0 ..< 1_000 {
75-
x += Double.random(in: -1000...1000)
142+
x += .random(in: -1000 ... 1000)
76143
}
77144
blackHole(x)
78145
}
@@ -83,9 +150,56 @@ public func run_RandomDoubleLCG(_ N: Int) {
83150
for _ in 0 ..< N {
84151
var x = 0.0
85152
var generator = LCRNG(seed: 0)
86-
for _ in 0 ..< 50_000 {
87-
x += Double.random(in: -1000...1000, using: &generator)
153+
for _ in 0 ..< 100_000 {
154+
x += .random(in: -1000 ... 1000, using: &generator)
155+
}
156+
blackHole(x)
157+
}
158+
}
159+
160+
@inline(never)
161+
public func run_RandomDoubleOpaqueDef(_ N: Int) {
162+
for _ in 0 ..< N {
163+
var x = 0.0
164+
for _ in 0 ..< 1_000 {
165+
x += .random(in: -1000 ... identity(1000))
166+
}
167+
blackHole(x)
168+
}
169+
}
170+
171+
@inline(never)
172+
public func run_RandomDoubleOpaqueLCG(_ N: Int) {
173+
for _ in 0 ..< N {
174+
var x = 0.0
175+
var generator = LCRNG(seed: 0)
176+
for _ in 0 ..< 100_000 {
177+
x += .random(in: -1000 ... identity(1000), using: &generator)
178+
}
179+
blackHole(x)
180+
}
181+
}
182+
183+
@inline(never)
184+
public func run_RandomDouble01Def(_ N: Int) {
185+
for _ in 0 ..< N {
186+
var x = 0.0
187+
for _ in 0 ..< 1_000 {
188+
x += .random(in: 0 ..< 1)
189+
}
190+
blackHole(x)
191+
}
192+
}
193+
194+
@inline(never)
195+
public func run_RandomDouble01LCG(_ N: Int) {
196+
for _ in 0 ..< N {
197+
var x = 0.0
198+
var generator = LCRNG(seed: 0)
199+
for _ in 0 ..< 100_000 {
200+
x += .random(in: 0 ..< 1, using: &generator)
88201
}
89202
blackHole(x)
90203
}
91204
}
205+

stdlib/public/core/Random.swift

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,6 @@ extension RandomNumberGenerator {
103103
upperBound: T
104104
) -> T {
105105
_precondition(upperBound != 0, "upperBound cannot be zero.")
106-
#if arch(i386) || arch(arm) || arch(arm64_32) // TODO(FIXME) SR-10912
107-
let tmp = (T.max % upperBound) + 1
108-
let range = tmp == upperBound ? 0 : tmp
109-
var random: T = 0
110-
111-
repeat {
112-
random = next()
113-
} while random < range
114-
115-
return random % upperBound
116-
#else
117106
var random: T = next()
118107
var m = random.multipliedFullWidth(by: upperBound)
119108
if m.low < upperBound {
@@ -124,7 +113,6 @@ extension RandomNumberGenerator {
124113
}
125114
}
126115
return m.high
127-
#endif
128116
}
129117
}
130118

0 commit comments

Comments
 (0)