Skip to content

Instantly share code, notes, and snippets.

@natecook1000
Created April 13, 2018 17:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save natecook1000/b8ead5305109b2b095c72b1400e4cc94 to your computer and use it in GitHub Desktop.
Save natecook1000/b8ead5305109b2b095c72b1400e4cc94 to your computer and use it in GitHub Desktop.
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))
}
}
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