-
-
Save natecook1000/b8ead5305109b2b095c72b1400e4cc94 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
let N = 10_000_000 | |
public protocol RandomNumberGenerator { | |
mutating func next() -> UInt64 | |
} | |
/// A struct version of the Xorshift128+ PRNG. | |
public struct Xorshift128PlusStruct : RandomNumberGenerator { | |
private var xS: UInt64 | |
private var yS: UInt64 | |
public init(xSeed: UInt64 = 0, ySeed: UInt64 = UInt64.max) { | |
xS = xSeed == 0 && ySeed == 0 ? UInt64.max : xSeed | |
yS = ySeed | |
for _ in 0..<10 { _ = next() } | |
} | |
public mutating func next() -> UInt64 { | |
var x = xS | |
let y = yS | |
xS = y | |
x ^= x &<< 23 | |
yS = x ^ y ^ (x &>> 17) ^ (y &>> 26) | |
return yS &+ y | |
} | |
} | |
/// A class version of the Xorshift128+ PRNG. | |
public final class Xorshift128Plus : RandomNumberGenerator { | |
private var xS: UInt64 | |
private var yS: UInt64 | |
public init(xSeed: UInt64 = 0, ySeed: UInt64 = UInt64.max) { | |
xS = xSeed == 0 && ySeed == 0 ? UInt64.max : xSeed | |
yS = ySeed | |
for _ in 0..<10 { _ = next() } | |
} | |
public func next() -> UInt64 { | |
var x = xS | |
let y = yS | |
xS = y | |
x ^= x &<< 23 | |
yS = x ^ y ^ (x &>> 17) ^ (y &>> 26) | |
return yS &+ y | |
} | |
} | |
/// A struct version of a linear congruential PRNG. | |
public struct LCRNGStruct: RandomNumberGenerator { | |
private var state: UInt64 | |
public init(seed: Int) { | |
self.state = 0 | |
reseed(seed) | |
} | |
public mutating func reseed(_ seed: Int) { | |
state = UInt64(truncatingIfNeeded: seed) | |
for _ in 0..<10 { _ = next() } | |
} | |
public mutating func next() -> UInt64 { | |
// Values from https://nuclear.llnl.gov/CNP/rng/rngman/node4.html | |
state = 2862933555777941757 &* state &+ 3037000493 | |
return state | |
} | |
} | |
/// A class version of a linear congruential PRNG. | |
public final class LCRNG: RandomNumberGenerator { | |
private var state: UInt64 | |
public init(seed: Int) { | |
self.state = 0 | |
reseed(seed) | |
} | |
public func reseed(_ seed: Int) { | |
state = UInt64(truncatingIfNeeded: seed) | |
for _ in 0..<10 { _ = next() } | |
} | |
public func next() -> UInt64 { | |
// Values from https://nuclear.llnl.gov/CNP/rng/rngman/node4.html | |
state = 2862933555777941757 &* state &+ 3037000493 | |
return state | |
} | |
} | |
/// A wrapper that makes an underlying RNG thread-safe using a mutex lock. | |
public final class ThreadLocking<Base: RandomNumberGenerator & AnyObject> : RandomNumberGenerator { | |
private var base: Base | |
private var mutex = pthread_mutex_t() | |
public init(_ base: Base) { | |
self.base = base | |
pthread_mutex_init(&mutex, nil) | |
} | |
public func next() -> UInt64 { | |
pthread_mutex_lock(&mutex) | |
defer { pthread_mutex_unlock(&mutex) } | |
return base.next() | |
} | |
} | |
/// Run the given test and print the time it took. | |
func test(name: String, body: () -> UInt) { | |
let start = Date() | |
let result = body() | |
let elapsed = Date().timeIntervalSince(start) | |
print(name.padding(toLength: 30, withPad: " ", startingAt: 0), terminator: "") | |
print(String(format: "% 9.4f %15u", elapsed, result)) | |
} | |
/// Create a low-level test - call next() directly on a generator | |
func makeLowLevelTest<T: RandomNumberGenerator>(_ generator: inout T) -> () -> UInt { | |
var gen = generator | |
return { | |
var x = 0 as UInt64 | |
for _ in 0..<N { | |
x = x &+ gen.next() | |
} | |
return UInt(x) | |
} | |
} | |
class ViewController: UIViewController { | |
var xorshift = Xorshift128Plus(xSeed: numericCast(arc4random())) | |
var lcrng = LCRNG(seed: numericCast(arc4random())) | |
var xorshiftTS = ThreadLocking(Xorshift128Plus(xSeed: numericCast(arc4random()))) | |
var lcrngTS = ThreadLocking(LCRNG(seed: numericCast(arc4random()))) | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
var xorshiftStruct = Xorshift128PlusStruct(xSeed: numericCast(arc4random())) | |
var lcrngStruct = LCRNGStruct(seed: numericCast(arc4random())) | |
test(name: "arc4random (2 calls)") { | |
var x = 0 as UInt64 | |
for _ in 0..<N { | |
x = x &+ UInt64(truncatingIfNeeded: arc4random() as UInt32) << 32 | | |
UInt64(truncatingIfNeeded: arc4random() as UInt32) | |
} | |
return UInt(x) | |
} | |
test(name: "arc4random_buf") { | |
var x = 0 as UInt64 | |
for _ in 0..<N { | |
var y = 0 as UInt64 | |
arc4random_buf(&y, 8) | |
x = x &+ y | |
} | |
return UInt(x) | |
} | |
test(name: "Xorshift128Plus (struct)", body: makeLowLevelTest(&xorshiftStruct)) | |
test(name: "Xorshift128Plus", body: makeLowLevelTest(&xorshift)) | |
test(name: "ThreadLocking<Xorshift128Plus>", body: makeLowLevelTest(&xorshiftTS)) | |
test(name: "LinearCongruential (struct)", body: makeLowLevelTest(&lcrngStruct)) | |
test(name: "LinearCongruential", body: makeLowLevelTest(&lcrng)) | |
test(name: "ThreadLocking<LinearCongruential>", body: makeLowLevelTest(&lcrngTS)) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
arc4random (2 calls) 1.1841 4294967295 | |
arc4random_buf 1.9345 390673047 | |
Xorshift128Plus (struct) 0.1827 1904048264 | |
Xorshift128Plus 0.8127 2218632601 | |
ThreadLocking<Xorshift128Plus> 1.3473 2226537391 | |
LinearCongruential (struct) 0.1819 3070407360 | |
LinearCongruential 0.5484 4017712064 | |
ThreadLocking<LinearCongruenti 1.0569 55884352 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment