Skip to content

Commit d282b1c

Browse files
authored
Merge pull request #16413 from Azoy/random-unification
[stdlib] Random unification
2 parents e22bed2 + 54b3b8b commit d282b1c

File tree

15 files changed

+1181
-19
lines changed

15 files changed

+1181
-19
lines changed

benchmark/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ set(SWIFT_BENCH_MODULES
124124
single-source/Queue
125125
single-source/RC4
126126
single-source/RGBHistogram
127+
single-source/RandomShuffle
128+
single-source/RandomValues
127129
single-source/RangeAssignment
128130
single-source/RangeIteration
129131
single-source/RangeReplaceableCollectionPlusDefault
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//===--- RandomShuffle.swift ----------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import TestsUtils
14+
15+
//
16+
// Benchmark that shuffles arrays of integers. Measures the performance of
17+
// shuffling large arrays.
18+
//
19+
20+
public let RandomShuffle = [
21+
BenchmarkInfo(name: "RandomShuffleDef", runFunction: run_RandomShuffleDef,
22+
tags: [.api], setUpFunction: setup_RandomShuffle),
23+
BenchmarkInfo(name: "RandomShuffleLCG", runFunction: run_RandomShuffleLCG,
24+
tags: [.api], setUpFunction: setup_RandomShuffle),
25+
]
26+
27+
/// A linear congruential PRNG.
28+
struct LCRNG: RandomNumberGenerator {
29+
private var state: UInt64
30+
31+
init(seed: Int) {
32+
state = UInt64(truncatingIfNeeded: seed)
33+
for _ in 0..<10 { _ = next() }
34+
}
35+
36+
mutating func next() -> UInt64 {
37+
state = 2862933555777941757 &* state &+ 3037000493
38+
return state
39+
}
40+
}
41+
42+
var numbers = Array(0...3_000_000)
43+
44+
@inline(never)
45+
func setup_RandomShuffle() {
46+
_ = numbers.count
47+
}
48+
49+
@inline(never)
50+
public func run_RandomShuffleDef(_ N: Int) {
51+
for _ in 0 ..< N {
52+
numbers.shuffle()
53+
blackHole(numbers.first!)
54+
}
55+
}
56+
57+
@inline(never)
58+
public func run_RandomShuffleLCG(_ N: Int) {
59+
var generator = LCRNG(seed: 0)
60+
for _ in 0 ..< N {
61+
numbers.shuffle(using: &generator)
62+
blackHole(numbers.first!)
63+
}
64+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//===--- RandomValues.swift -----------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import TestsUtils
14+
15+
//
16+
// Benchmark generating lots of random values. Measures the performance of
17+
// the default random generator and the algorithms for generating integers
18+
// and floating-point values.
19+
//
20+
21+
public let RandomValues = [
22+
BenchmarkInfo(name: "RandomIntegersDef", runFunction: run_RandomIntegersDef, tags: [.api]),
23+
BenchmarkInfo(name: "RandomIntegersLCG", runFunction: run_RandomIntegersLCG, tags: [.api]),
24+
BenchmarkInfo(name: "RandomDoubleDef", runFunction: run_RandomDoubleDef, tags: [.api]),
25+
BenchmarkInfo(name: "RandomDoubleLCG", runFunction: run_RandomDoubleLCG, tags: [.api]),
26+
]
27+
28+
/// A linear congruential PRNG.
29+
struct LCRNG: RandomNumberGenerator {
30+
private var state: UInt64
31+
32+
init(seed: Int) {
33+
state = UInt64(truncatingIfNeeded: seed)
34+
for _ in 0..<10 { _ = next() }
35+
}
36+
37+
mutating func next() -> UInt64 {
38+
state = 2862933555777941757 &* state &+ 3037000493
39+
return state
40+
}
41+
}
42+
43+
@inline(never)
44+
public func run_RandomIntegersDef(_ N: Int) {
45+
for _ in 0 ..< N {
46+
var x = 0
47+
for _ in 0 ..< 100_000 {
48+
x &+= Int.random(in: 0...10_000)
49+
}
50+
blackHole(x)
51+
}
52+
}
53+
54+
@inline(never)
55+
public func run_RandomIntegersLCG(_ N: Int) {
56+
for _ in 0 ..< N {
57+
var x = 0
58+
var generator = LCRNG(seed: 0)
59+
for _ in 0 ..< 100_000 {
60+
x &+= Int.random(in: 0...10_000, using: &generator)
61+
}
62+
CheckResults(x == 498214315)
63+
}
64+
}
65+
66+
@inline(never)
67+
public func run_RandomDoubleDef(_ N: Int) {
68+
for _ in 0 ..< N {
69+
var x = 0.0
70+
for _ in 0 ..< 100_000 {
71+
x += Double.random(in: -1000...1000)
72+
}
73+
blackHole(x)
74+
}
75+
}
76+
77+
@inline(never)
78+
public func run_RandomDoubleLCG(_ N: Int) {
79+
for _ in 0 ..< N {
80+
var x = 0.0
81+
var generator = LCRNG(seed: 0)
82+
for _ in 0 ..< 100_000 {
83+
x += Double.random(in: -1000...1000, using: &generator)
84+
}
85+
blackHole(x)
86+
}
87+
}

benchmark/utils/main.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -112,6 +112,8 @@ import ProtocolDispatch2
112112
import Queue
113113
import RC4
114114
import RGBHistogram
115+
import RandomShuffle
116+
import RandomValues
115117
import RangeAssignment
116118
import RangeIteration
117119
import RangeReplaceableCollectionPlusDefault
@@ -268,6 +270,8 @@ registerBenchmark(QueueGeneric)
268270
registerBenchmark(QueueConcrete)
269271
registerBenchmark(RC4Test)
270272
registerBenchmark(RGBHistogram)
273+
registerBenchmark(RandomShuffle)
274+
registerBenchmark(RandomValues)
271275
registerBenchmark(RangeAssignment)
272276
registerBenchmark(RangeIteration)
273277
registerBenchmark(RangeReplaceableCollectionPlusDefault)

stdlib/public/SwiftShims/LibcShims.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -149,6 +149,10 @@ __swift_uint32_t _stdlib_cxx11_mt19937(void);
149149
SWIFT_RUNTIME_STDLIB_INTERNAL
150150
__swift_uint32_t _stdlib_cxx11_mt19937_uniform(__swift_uint32_t upper_bound);
151151

152+
// Random number for stdlib
153+
SWIFT_RUNTIME_STDLIB_INTERNAL
154+
void _stdlib_random(void *buf, __swift_size_t nbytes);
155+
152156
// Math library functions
153157
static inline SWIFT_ALWAYS_INLINE
154158
float _stdlib_remainderf(float _self, float _other) {

stdlib/public/core/Bool.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -86,6 +86,28 @@ public struct Bool {
8686
public init(_ value: Bool) {
8787
self = value
8888
}
89+
90+
/// Returns a random Boolean value
91+
///
92+
/// - Parameter generator: The random number generator to use when getting a
93+
/// random Boolean.
94+
/// - Returns: A random Boolean value.
95+
@inlinable
96+
public static func random<T: RandomNumberGenerator>(
97+
using generator: inout T
98+
) -> Bool {
99+
return (generator.next() >> 17) & 1 == 0
100+
}
101+
102+
/// Returns a random Boolean value
103+
///
104+
/// - Returns: A random Boolean value.
105+
///
106+
/// This uses the standard library's default random number generator.
107+
@inlinable
108+
public static func random() -> Bool {
109+
return Bool.random(using: &Random.default)
110+
}
89111
}
90112

91113
extension Bool : _ExpressibleByBuiltinBooleanLiteral, ExpressibleByBooleanLiteral {

stdlib/public/core/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#
33
# This source file is part of the Swift.org open source project
44
#
5-
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
# Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
66
# Licensed under Apache License v2.0 with Runtime Library Exception
77
#
88
# See https://swift.org/LICENSE.txt for license information
@@ -97,6 +97,7 @@ set(SWIFTLIB_ESSENTIAL
9797
Policy.swift
9898
PrefixWhile.swift
9999
Print.swift
100+
Random.swift
100101
RandomAccessCollection.swift
101102
Range.swift
102103
RangeReplaceableCollection.swift

stdlib/public/core/Collection.swift

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -803,6 +803,25 @@ public protocol Collection: Sequence where SubSequence: Collection {
803803
/// `endIndex`.
804804
func formIndex(after i: inout Index)
805805

806+
/// Returns a random element of the collection, using the given generator as
807+
/// a source for randomness.
808+
///
809+
/// You use this method to select a random element from a collection when you
810+
/// are using a custom random number generator. For example, call
811+
/// `randomElement(using:)` to select a random element from an array of names.
812+
///
813+
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
814+
/// let randomName = names.randomElement(using: &myGenerator)!
815+
/// // randomName == "Amani" (maybe)
816+
///
817+
/// - Parameter generator: The random number generator to use when choosing
818+
/// a random element.
819+
/// - Returns: A random element from the collection. If the collection is
820+
/// empty, the method returns `nil`.
821+
func randomElement<T: RandomNumberGenerator>(
822+
using generator: inout T
823+
) -> Element?
824+
806825
@available(*, deprecated, message: "all index distances are now of type Int")
807826
typealias IndexDistance = Int
808827
}
@@ -1016,6 +1035,54 @@ extension Collection {
10161035
return count
10171036
}
10181037

1038+
/// Returns a random element of the collection, using the given generator as
1039+
/// a source for randomness.
1040+
///
1041+
/// You use this method to select a random element from a collection when you
1042+
/// are using a custom random number generator. For example, call
1043+
/// `randomElement(using:)` to select a random element from an array of names.
1044+
///
1045+
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
1046+
/// let randomName = names.randomElement(using: &myGenerator)!
1047+
/// // randomName == "Amani" (maybe)
1048+
///
1049+
/// - Parameter generator: The random number generator to use when choosing
1050+
/// a random element.
1051+
/// - Returns: A random element from the collection. If the collection is
1052+
/// empty, the method returns `nil`.
1053+
@inlinable
1054+
public func randomElement<T: RandomNumberGenerator>(
1055+
using generator: inout T
1056+
) -> Element? {
1057+
guard !isEmpty else { return nil }
1058+
let random = generator.next(upperBound: UInt(count))
1059+
let index = self.index(
1060+
startIndex,
1061+
offsetBy: numericCast(random)
1062+
)
1063+
return self[index]
1064+
}
1065+
1066+
/// Returns a random element of the collection.
1067+
///
1068+
/// For example, call `randomElement()` to select a random element from an
1069+
/// array of names.
1070+
///
1071+
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
1072+
/// let randomName = names.randomElement()!
1073+
/// // randomName == "Amani" (perhaps)
1074+
///
1075+
/// This method uses the default random generator, `Random.default`. The call
1076+
/// to `names.randomElement()` above is equivalent to calling
1077+
/// `names.randomElement(using: &Random.default)`.
1078+
///
1079+
/// - Returns: A random element from the collection. If the collection is
1080+
/// empty, the method returns `nil`.
1081+
@inlinable
1082+
public func randomElement() -> Element? {
1083+
return randomElement(using: &Random.default)
1084+
}
1085+
10191086
/// Do not use this method directly; call advanced(by: n) instead.
10201087
@inlinable
10211088
@inline(__always)

0 commit comments

Comments
 (0)