Skip to content

[4.2][SE-0202][stdlib] Random unification #16495

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ set(SWIFT_BENCH_MODULES
single-source/Queue
single-source/RC4
single-source/RGBHistogram
single-source/RandomShuffle
single-source/RandomValues
single-source/RangeAssignment
single-source/RangeIteration
single-source/RangeReplaceableCollectionPlusDefault
Expand Down
64 changes: 64 additions & 0 deletions benchmark/single-source/RandomShuffle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//===--- RandomShuffle.swift ----------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import TestsUtils

//
// Benchmark that shuffles arrays of integers. Measures the performance of
// shuffling large arrays.
//

public let RandomShuffle = [
BenchmarkInfo(name: "RandomShuffleDef", runFunction: run_RandomShuffleDef,
tags: [.api], setUpFunction: setup_RandomShuffle),
BenchmarkInfo(name: "RandomShuffleLCG", runFunction: run_RandomShuffleLCG,
tags: [.api], setUpFunction: setup_RandomShuffle),
]

/// A linear congruential PRNG.
struct LCRNG: RandomNumberGenerator {
private var state: UInt64

init(seed: Int) {
state = UInt64(truncatingIfNeeded: seed)
for _ in 0..<10 { _ = next() }
}

mutating func next() -> UInt64 {
state = 2862933555777941757 &* state &+ 3037000493
return state
}
}

var numbers = Array(0...3_000_000)

@inline(never)
func setup_RandomShuffle() {
_ = numbers.count
}

@inline(never)
public func run_RandomShuffleDef(_ N: Int) {
for _ in 0 ..< N {
numbers.shuffle()
blackHole(numbers.first!)
}
}

@inline(never)
public func run_RandomShuffleLCG(_ N: Int) {
var generator = LCRNG(seed: 0)
for _ in 0 ..< N {
numbers.shuffle(using: &generator)
blackHole(numbers.first!)
}
}
87 changes: 87 additions & 0 deletions benchmark/single-source/RandomValues.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//===--- RandomValues.swift -----------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import TestsUtils

//
// Benchmark generating lots of random values. Measures the performance of
// the default random generator and the algorithms for generating integers
// and floating-point values.
//

public let RandomValues = [
BenchmarkInfo(name: "RandomIntegersDef", runFunction: run_RandomIntegersDef, tags: [.api]),
BenchmarkInfo(name: "RandomIntegersLCG", runFunction: run_RandomIntegersLCG, tags: [.api]),
BenchmarkInfo(name: "RandomDoubleDef", runFunction: run_RandomDoubleDef, tags: [.api]),
BenchmarkInfo(name: "RandomDoubleLCG", runFunction: run_RandomDoubleLCG, tags: [.api]),
]

/// A linear congruential PRNG.
struct LCRNG: RandomNumberGenerator {
private var state: UInt64

init(seed: Int) {
state = UInt64(truncatingIfNeeded: seed)
for _ in 0..<10 { _ = next() }
}

mutating func next() -> UInt64 {
state = 2862933555777941757 &* state &+ 3037000493
return state
}
}

@inline(never)
public func run_RandomIntegersDef(_ N: Int) {
for _ in 0 ..< N {
var x = 0
for _ in 0 ..< 100_000 {
x &+= Int.random(in: 0...10_000)
}
blackHole(x)
}
}

@inline(never)
public func run_RandomIntegersLCG(_ N: Int) {
for _ in 0 ..< N {
var x = 0
var generator = LCRNG(seed: 0)
for _ in 0 ..< 100_000 {
x &+= Int.random(in: 0...10_000, using: &generator)
}
CheckResults(x == 498214315)
}
}

@inline(never)
public func run_RandomDoubleDef(_ N: Int) {
for _ in 0 ..< N {
var x = 0.0
for _ in 0 ..< 100_000 {
x += Double.random(in: -1000...1000)
}
blackHole(x)
}
}

@inline(never)
public func run_RandomDoubleLCG(_ N: Int) {
for _ in 0 ..< N {
var x = 0.0
var generator = LCRNG(seed: 0)
for _ in 0 ..< 100_000 {
x += Double.random(in: -1000...1000, using: &generator)
}
blackHole(x)
}
}
6 changes: 5 additions & 1 deletion benchmark/utils/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -110,6 +110,8 @@ import ProtocolDispatch2
import Queue
import RC4
import RGBHistogram
import RandomShuffle
import RandomValues
import RangeAssignment
import RangeIteration
import RangeReplaceableCollectionPlusDefault
Expand Down Expand Up @@ -264,6 +266,8 @@ registerBenchmark(QueueGeneric)
registerBenchmark(QueueConcrete)
registerBenchmark(RC4Test)
registerBenchmark(RGBHistogram)
registerBenchmark(RandomShuffle)
registerBenchmark(RandomValues)
registerBenchmark(RangeAssignment)
registerBenchmark(RangeIteration)
registerBenchmark(RangeReplaceableCollectionPlusDefault)
Expand Down
6 changes: 5 additions & 1 deletion stdlib/public/SwiftShims/LibcShims.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -149,6 +149,10 @@ __swift_uint32_t _stdlib_cxx11_mt19937(void);
SWIFT_RUNTIME_STDLIB_INTERNAL
__swift_uint32_t _stdlib_cxx11_mt19937_uniform(__swift_uint32_t upper_bound);

// Random number for stdlib
SWIFT_RUNTIME_STDLIB_INTERNAL
void _stdlib_random(void *buf, __swift_size_t nbytes);

// Math library functions
static inline SWIFT_ALWAYS_INLINE
float _stdlib_remainderf(float _self, float _other) {
Expand Down
24 changes: 23 additions & 1 deletion stdlib/public/core/Bool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -86,6 +86,28 @@ public struct Bool {
public init(_ value: Bool) {
self = value
}

/// Returns a random Boolean value
///
/// - Parameter generator: The random number generator to use when getting a
/// random Boolean.
/// - Returns: A random Boolean value.
@inlinable
public static func random<T: RandomNumberGenerator>(
using generator: inout T
) -> Bool {
return (generator.next() >> 17) & 1 == 0
}

/// Returns a random Boolean value
///
/// - Returns: A random Boolean value.
///
/// This uses the standard library's default random number generator.
@inlinable
public static func random() -> Bool {
return Bool.random(using: &Random.default)
}
}

extension Bool : _ExpressibleByBuiltinBooleanLiteral, ExpressibleByBooleanLiteral {
Expand Down
3 changes: 2 additions & 1 deletion stdlib/public/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
# Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -97,6 +97,7 @@ set(SWIFTLIB_ESSENTIAL
Policy.swift
PrefixWhile.swift
Print.swift
Random.swift
RandomAccessCollection.swift
Range.swift
RangeReplaceableCollection.swift
Expand Down
69 changes: 68 additions & 1 deletion stdlib/public/core/Collection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -803,6 +803,25 @@ public protocol Collection: Sequence where SubSequence: Collection {
/// `endIndex`.
func formIndex(after i: inout Index)

/// Returns a random element of the collection, using the given generator as
/// a source for randomness.
///
/// You use this method to select a random element from a collection when you
/// are using a custom random number generator. For example, call
/// `randomElement(using:)` to select a random element from an array of names.
///
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
/// let randomName = names.randomElement(using: &myGenerator)!
/// // randomName == "Amani" (maybe)
///
/// - Parameter generator: The random number generator to use when choosing
/// a random element.
/// - Returns: A random element from the collection. If the collection is
/// empty, the method returns `nil`.
func randomElement<T: RandomNumberGenerator>(
using generator: inout T
) -> Element?

@available(*, deprecated, message: "all index distances are now of type Int")
typealias IndexDistance = Int
}
Expand Down Expand Up @@ -1016,6 +1035,54 @@ extension Collection {
return count
}

/// Returns a random element of the collection, using the given generator as
/// a source for randomness.
///
/// You use this method to select a random element from a collection when you
/// are using a custom random number generator. For example, call
/// `randomElement(using:)` to select a random element from an array of names.
///
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
/// let randomName = names.randomElement(using: &myGenerator)!
/// // randomName == "Amani" (maybe)
///
/// - Parameter generator: The random number generator to use when choosing
/// a random element.
/// - Returns: A random element from the collection. If the collection is
/// empty, the method returns `nil`.
@inlinable
public func randomElement<T: RandomNumberGenerator>(
using generator: inout T
) -> Element? {
guard !isEmpty else { return nil }
let random = generator.next(upperBound: UInt(count))
let index = self.index(
startIndex,
offsetBy: numericCast(random)
)
return self[index]
}

/// Returns a random element of the collection.
///
/// For example, call `randomElement()` to select a random element from an
/// array of names.
///
/// let names = ["Zoey", "Chloe", "Amani", "Amaia"]
/// let randomName = names.randomElement()!
/// // randomName == "Amani" (perhaps)
///
/// This method uses the default random generator, `Random.default`. The call
/// to `names.randomElement()` above is equivalent to calling
/// `names.randomElement(using: &Random.default)`.
///
/// - Returns: A random element from the collection. If the collection is
/// empty, the method returns `nil`.
@inlinable
public func randomElement() -> Element? {
return randomElement(using: &Random.default)
}

/// Do not use this method directly; call advanced(by: n) instead.
@inlinable
@inline(__always)
Expand Down
Loading