Skip to content

Commit c615e59

Browse files
natecook1000lorentey
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 Value type generators random -> randomElement Fix some docs One more doc fix Doc fixes & bool fix Use computed over explicit (cherry picked from commit f146d17)
1 parent 10241a9 commit c615e59

File tree

11 files changed

+484
-206
lines changed

11 files changed

+484
-206
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+
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) 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+
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: 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/Bool.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ public struct Bool {
9494
/// - Returns: A random Boolean.
9595
@inlinable
9696
public static func random<T: RandomNumberGenerator>(
97-
using generator: T
97+
using generator: inout T
9898
) -> Bool {
99-
return generator.next() % 2 == 0
99+
return (generator.next() >> 17) & 1 == 0
100100
}
101101

102102
/// Returns a random Boolean
@@ -108,7 +108,7 @@ public struct Bool {
108108
/// This uses the standard library's default random number generator.
109109
@inlinable
110110
public static func random() -> Bool {
111-
return Bool.random(using: Random.default)
111+
return Bool.random(using: &Random.default)
112112
}
113113
}
114114

stdlib/public/core/Collection.swift

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,27 +1016,24 @@ extension Collection {
10161016
return count
10171017
}
10181018

1019-
/// Returns a random element from this collection.
1019+
/// Returns a random element of the collection, using the given generator as
1020+
/// a source for randomness.
10201021
///
1021-
/// - Parameter generator: The random number generator to use when getting
1022-
/// a random element.
1023-
/// - Returns: A random element from this collection.
1024-
///
1025-
/// A good example of this is getting a random greeting from an array:
1026-
///
1027-
/// let greetings = ["hi", "hey", "hello", "hola"]
1028-
/// let randomGreeting = greetings.random()
1022+
/// You use this method to select a random element from a collection when you
1023+
/// are using a custom random number generator. For example, call
1024+
/// `randomElement(using:)` to select a random element from an array of names.
10291025
///
1030-
/// If the collection is empty, the value of this function is `nil`.
1026+
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
1027+
/// let randomName = names.randomElement(using: &myGenerator)!
1028+
/// // randomName == "Amani" (maybe)
10311029
///
1032-
/// let numbers = [10, 20, 30, 40, 50]
1033-
/// if let randomNumber = numbers.random() {
1034-
/// print(randomNumber)
1035-
/// }
1036-
/// // Could print "20", perhaps
1030+
/// - Parameter generator: The random number generator to use when choosing
1031+
/// a random element.
1032+
/// - Returns: A random element from the collection. If the collection is
1033+
/// empty, the method returns `nil`.
10371034
@inlinable
1038-
public func random<T: RandomNumberGenerator>(
1039-
using generator: T
1035+
public func randomElement<T: RandomNumberGenerator>(
1036+
using generator: inout T
10401037
) -> Element? {
10411038
guard !isEmpty else { return nil }
10421039
let random = generator.next(upperBound: UInt(count))
@@ -1048,29 +1045,24 @@ extension Collection {
10481045
return self[index]
10491046
}
10501047

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

10761068
/// Do not use this method directly; call advanced(by: n) instead.

stdlib/public/core/CollectionAlgorithms.swift

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -372,41 +372,75 @@ extension MutableCollection where Self : BidirectionalCollection {
372372
//===----------------------------------------------------------------------===//
373373

374374
extension Sequence {
375-
/// Returns the elements of the sequence, shuffled.
375+
/// Returns the elements of the sequence, shuffled using the given generator
376+
/// as a source for randomness.
377+
///
378+
/// You use this method to randomize the elements of a sequence when you
379+
/// are using a custom random number generator. For example, you can shuffle
380+
/// the numbers between `0` and `9` by calling the `shuffled(using:)` method
381+
/// on that range:
382+
///
383+
/// let numbers = 0...9
384+
/// let shuffledNumbers = numbers.shuffled(using: &myGenerator)
385+
/// // shuffledNumbers == [8, 9, 4, 3, 2, 6, 7, 0, 5, 1]
376386
///
377387
/// - Parameter generator: The random number generator to use when shuffling
378388
/// the sequence.
379-
/// - Returns: A shuffled array of this sequence's elements.
389+
/// - Returns: An array of this sequence's elements in a shuffled order.
390+
///
391+
/// - Complexity: O(*n*)
380392
@inlinable
381393
public func shuffled<T: RandomNumberGenerator>(
382-
using generator: T
394+
using generator: inout T
383395
) -> [Element] {
384396
var result = ContiguousArray(self)
385-
result.shuffle(using: generator)
397+
result.shuffle(using: &generator)
386398
return Array(result)
387399
}
388400

389401
/// Returns the elements of the sequence, shuffled.
390402
///
403+
/// For example, you can shuffle the numbers between `0` and `9` by calling
404+
/// the `shuffled()` method on that range:
405+
///
406+
/// let numbers = 0...9
407+
/// let shuffledNumbers = numbers.shuffled()
408+
/// // shuffledNumbers == [1, 7, 6, 2, 8, 9, 4, 3, 5, 0]
409+
///
410+
/// This method uses the default random generator, `Random.default`. The call
411+
/// to `numbers.shuffled()` above is equivalent to calling
412+
/// `numbers.shuffled(using: &Random.default)`.
413+
///
391414
/// - Parameter generator: The random number generator to use when shuffling
392415
/// the sequence.
393416
/// - Returns: A shuffled array of this sequence's elements.
394417
///
395-
/// This uses the standard library's default random number generator.
418+
/// - Complexity: O(*n*)
396419
@inlinable
397420
public func shuffled() -> [Element] {
398-
return shuffled(using: Random.default)
421+
return shuffled(using: &Random.default)
399422
}
400423
}
401424

402425
extension MutableCollection {
403-
/// Shuffles the collection in place.
426+
/// Shuffles the collection in place, using the given generator as a source
427+
/// for randomness.
428+
///
429+
/// You use this method to randomize the elements of a collection when you
430+
/// are using a custom random number generator. For example, you can use the
431+
/// `shuffle(using:)` method to randomly reorder the elements of an array.
432+
///
433+
/// var names = ["Alejandro", "Camila", "Diego", "Luciana", "Luis", "Sofía"]
434+
/// names.shuffle(using: &myGenerator)
435+
/// // names == ["Sofía", "Alejandro", "Camila", "Luis", "Diego", "Luciana"]
404436
///
405437
/// - Parameter generator: The random number generator to use when shuffling
406438
/// the collection.
439+
///
440+
/// - Complexity: O(*n*)
407441
@inlinable
408442
public mutating func shuffle<T: RandomNumberGenerator>(
409-
using generator: T
443+
using generator: inout T
410444
) {
411445
guard count > 1 else { return }
412446
var amount = count
@@ -424,13 +458,21 @@ extension MutableCollection {
424458

425459
/// Shuffles the collection in place.
426460
///
427-
/// - Parameter generator: The random number generator to use when shuffling
428-
/// the collection.
461+
/// Use the `shuffle()` method to randomly reorder the elements of an
462+
/// array.
463+
///
464+
/// var names = ["Alejandro", "Camila", "Diego", "Luciana", "Luis", "Sofía"]
465+
/// names.shuffle(using: myGenerator)
466+
/// // names == ["Luis", "Camila", "Luciana", "Sofía", "Alejandro", "Diego"]
429467
///
430-
/// This uses the standard library's default random number generator.
468+
/// This method uses the default random generator, `Random.default`. The call
469+
/// to `names.shuffle()` above is equivalent to calling
470+
/// `names.shuffle(using: &Random.default)`.
471+
///
472+
/// - Complexity: O(*n*)
431473
@inlinable
432474
public mutating func shuffle() {
433-
shuffle(using: Random.default)
475+
shuffle(using: &Random.default)
434476
}
435477
}
436478

0 commit comments

Comments
 (0)