Skip to content

Commit f31c344

Browse files
natecook1000Azoy
authored andcommitted
Revise documentation, add benchmarks (#3)
* [stdlib] Revise documentation for new random APIs * [stdlib] Fix constraints on random integer generation * [test] Isolate failing Random test * [benchmark] Add benchmarks for new random APIs Fix Float80 test
1 parent 47e25db commit f31c344

File tree

10 files changed

+450
-180
lines changed

10 files changed

+450
-180
lines changed

benchmark/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ set(SWIFT_BENCH_MODULES
122122
single-source/Queue
123123
single-source/RC4
124124
single-source/RGBHistogram
125+
single-source/RandomShuffle
126+
single-source/RandomValues
125127
single-source/RangeAssignment
126128
single-source/RangeIteration
127129
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) 2014 - 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+
final class 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+
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+
let 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) 2014 - 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+
final class 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+
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+
let 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+
let 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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ import ProtocolDispatch2
110110
import Queue
111111
import RC4
112112
import RGBHistogram
113+
import RandomShuffle
114+
import RandomValues
113115
import RangeAssignment
114116
import RangeIteration
115117
import RangeReplaceableCollectionPlusDefault
@@ -264,6 +266,8 @@ registerBenchmark(QueueGeneric)
264266
registerBenchmark(QueueConcrete)
265267
registerBenchmark(RC4Test)
266268
registerBenchmark(RGBHistogram)
269+
registerBenchmark(RandomShuffle)
270+
registerBenchmark(RandomValues)
267271
registerBenchmark(RangeAssignment)
268272
registerBenchmark(RangeIteration)
269273
registerBenchmark(RangeReplaceableCollectionPlusDefault)

stdlib/public/core/Collection.swift

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -790,24 +790,21 @@ public protocol Collection: Sequence where SubSequence: Collection {
790790
/// `endIndex`.
791791
func formIndex(after i: inout Index)
792792

793-
/// Returns a random element from this collection.
793+
/// Returns a random element of the collection, using the given generator as
794+
/// a source for randomness.
794795
///
795-
/// - Parameter generator: The random number generator to use when getting
796-
/// a random element.
797-
/// - Returns: A random element from this collection.
798-
///
799-
/// A good example of this is getting a random greeting from an array:
800-
///
801-
/// let greetings = ["hi", "hey", "hello", "hola"]
802-
/// let randomGreeting = greetings.random()
796+
/// You use this method to select a random element from a collection when you
797+
/// are using a custom random number generator. For example, call
798+
/// `random(using:)` to select a random element from an array of names.
803799
///
804-
/// If the collection is empty, the value of this function is `nil`.
800+
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
801+
/// let randomName = names.random(using: myGenerator)!
802+
/// // randomName == "Amani" (maybe)
805803
///
806-
/// let numbers = [10, 20, 30, 40, 50]
807-
/// if let randomNumber = numbers.random() {
808-
/// print(randomNumber)
809-
/// }
810-
/// // Could print "20", perhaps
804+
/// - Parameter generator: The random number generator to use when choosing
805+
/// a random element.
806+
/// - Returns: A random element from the collection. If the collection is
807+
/// empty, the method returns `nil`.
811808
func random<T: RandomNumberGenerator>(using generator: T) -> Element?
812809

813810
@available(*, deprecated, message: "all index distances are now of type Int")
@@ -1023,24 +1020,21 @@ extension Collection {
10231020
return count
10241021
}
10251022

1026-
/// Returns a random element from this collection.
1027-
///
1028-
/// - Parameter generator: The random number generator to use when getting
1029-
/// a random element.
1030-
/// - Returns: A random element from this collection.
1031-
///
1032-
/// A good example of this is getting a random greeting from an array:
1023+
/// Returns a random element of the collection, using the given generator as
1024+
/// a source for randomness.
10331025
///
1034-
/// let greetings = ["hi", "hey", "hello", "hola"]
1035-
/// let randomGreeting = greetings.random()
1026+
/// You use this method to select a random element from a collection when you
1027+
/// are using a custom random number generator. For example, call
1028+
/// `random(using:)` to select a random element from an array of names.
10361029
///
1037-
/// If the collection is empty, the value of this function is `nil`.
1030+
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
1031+
/// let randomName = names.random(using: myGenerator)!
1032+
/// // randomName == "Amani" (maybe)
10381033
///
1039-
/// let numbers = [10, 20, 30, 40, 50]
1040-
/// if let randomNumber = numbers.random() {
1041-
/// print(randomNumber)
1042-
/// }
1043-
/// // Could print "20", perhaps
1034+
/// - Parameter generator: The random number generator to use when choosing
1035+
/// a random element.
1036+
/// - Returns: A random element from the collection. If the collection is
1037+
/// empty, the method returns `nil`.
10441038
@inlinable
10451039
public func random<T: RandomNumberGenerator>(
10461040
using generator: T
@@ -1054,26 +1048,21 @@ extension Collection {
10541048
return self[index]
10551049
}
10561050

1057-
/// Returns a random element from this collection.
1058-
///
1059-
/// - Parameter generator: The random number generator to use when getting
1060-
/// a random element.
1061-
/// - Returns: A random element from this collection.
1062-
///
1063-
/// A good example of this is getting a random greeting from an array:
1051+
/// Returns a random element of the collection.
10641052
///
1065-
/// let greetings = ["hi", "hey", "hello", "hola"]
1066-
/// let randomGreeting = greetings.random()
1053+
/// For example, call `random()` to select a random element from an
1054+
/// array of names.
10671055
///
1068-
/// If the collection is empty, the value of this function is `nil`.
1056+
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
1057+
/// let randomName = names.random()!
1058+
/// // randomName == "Amani" (perhaps)
10691059
///
1070-
/// let numbers = [10, 20, 30, 40, 50]
1071-
/// if let randomNumber = numbers.random() {
1072-
/// print(randomNumber)
1073-
/// }
1074-
/// // Could print "20", perhaps
1060+
/// This method uses the default random generator, `Random.default`. The call
1061+
/// to `names.random()` above is equivalent to calling
1062+
/// `names.random(using: Random.default)`.
10751063
///
1076-
/// This uses the standard library's default random number generator.
1064+
/// - Returns: A random element from the collection. If the collection is
1065+
/// empty, the method returns `nil`.
10771066
@inlinable
10781067
public func random() -> Element? {
10791068
return random(using: Random.default)

stdlib/public/core/CollectionAlgorithms.swift

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -265,11 +265,23 @@ extension MutableCollection where Self : BidirectionalCollection {
265265
//===----------------------------------------------------------------------===//
266266

267267
extension Sequence {
268-
/// Returns the elements of the sequence, shuffled.
268+
/// Returns the elements of the sequence, shuffled using the given generator
269+
/// as a source for randomness.
270+
///
271+
/// You use this method to randomize the elements of a sequence when you
272+
/// are using a custom random number generator. For example, you can shuffle
273+
/// the numbers between `0` and `9` by calling the `shuffled(using:)` method
274+
/// on that range:
275+
///
276+
/// let numbers = 0...9
277+
/// let shuffledNumbers = numbers.shuffled(using: myGenerator)
278+
/// // shuffledNumbers == [8, 9, 4, 3, 2, 6, 7, 0, 5, 1]
269279
///
270280
/// - Parameter generator: The random number generator to use when shuffling
271281
/// the sequence.
272-
/// - Returns: A shuffled array of this sequence's elements.
282+
/// - Returns: An array of this sequence's elements in a shuffled order.
283+
///
284+
/// - Complexity: O(*n*)
273285
@inlinable
274286
public func shuffled<T: RandomNumberGenerator>(
275287
using generator: T
@@ -281,22 +293,44 @@ extension Sequence {
281293

282294
/// Returns the elements of the sequence, shuffled.
283295
///
296+
/// For example, you can shuffle the numbers between `0` and `9` by calling
297+
/// the `shuffled()` method on that range:
298+
///
299+
/// let numbers = 0...9
300+
/// let shuffledNumbers = numbers.shuffled()
301+
/// // shuffledNumbers == [1, 7, 6, 2, 8, 9, 4, 3, 5, 0]
302+
///
303+
/// This method uses the default random generator, `Random.default`. The call
304+
/// to `numbers.shuffled()` above is equivalent to calling
305+
/// `numbers.shuffled(using: Random.default)`.
306+
///
284307
/// - Parameter generator: The random number generator to use when shuffling
285308
/// the sequence.
286309
/// - Returns: A shuffled array of this sequence's elements.
287310
///
288-
/// This uses the standard library's default random number generator.
311+
/// - Complexity: O(*n*)
289312
@inlinable
290313
public func shuffled() -> [Element] {
291314
return shuffled(using: Random.default)
292315
}
293316
}
294317

295318
extension MutableCollection {
296-
/// Shuffles the collection in place.
319+
/// Shuffles the collection in place, using the given generator as a source
320+
/// for randomness.
321+
///
322+
/// You use this method to randomize the elements of a collection when you
323+
/// are using a custom random number generator. For example, you can use the
324+
/// `shuffle(using:)` method to randomly reorder the elements of an array.
325+
///
326+
/// var names = ["Alejandro", "Camila", "Diego", "Luciana", "Luis", "Sofía"]
327+
/// names.shuffle(using: myGenerator)
328+
/// // names == ["Sofía", "Alejandro", "Camila", "Luis", "Diego", "Luciana"]
297329
///
298330
/// - Parameter generator: The random number generator to use when shuffling
299331
/// the collection.
332+
///
333+
/// - Complexity: O(*n*)
300334
@inlinable
301335
public mutating func shuffle<T: RandomNumberGenerator>(
302336
using generator: T
@@ -317,10 +351,18 @@ extension MutableCollection {
317351

318352
/// Shuffles the collection in place.
319353
///
320-
/// - Parameter generator: The random number generator to use when shuffling
321-
/// the collection.
354+
/// Use the `shuffle()` method to randomly reorder the elements of an
355+
/// array.
356+
///
357+
/// var names = ["Alejandro", "Camila", "Diego", "Luciana", "Luis", "Sofía"]
358+
/// names.shuffle(using: myGenerator)
359+
/// // names == ["Luis", "Camila", "Luciana", "Sofía", "Alejandro", "Diego"]
322360
///
323-
/// This uses the standard library's default random number generator.
361+
/// This method uses the default random generator, `Random.default`. The call
362+
/// to `names.shuffle()` above is equivalent to calling
363+
/// `names.shuffle(using: Random.default)`.
364+
///
365+
/// - Complexity: O(*n*)
324366
@inlinable
325367
public mutating func shuffle() {
326368
shuffle(using: Random.default)

0 commit comments

Comments
 (0)