Created
March 18, 2010 01:02
-
-
Save matthewd/335927 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
require 'benchmark' | |
total = (ENV['TOTAL'] || 10_000).to_i | |
unless defined?(Rubinius) | |
module Rubinius | |
Tuple = Array | |
WORDSIZE = 32 | |
end | |
class Fixnum | |
MAX = 0x3fffffff | |
end | |
end | |
class MTRandomizer | |
MAX = Fixnum::MAX | |
def initialize | |
@counter = 0 | |
begin | |
@handle = File.open("/dev/urandom", "r") | |
rescue Errno::ENOENT, Errno::EPERM, Errno::EACCES | |
@handle = nil | |
end | |
@state = Rubinius::Tuple.new(N2) | |
self.seed = generate_seed | |
end | |
def urandom(bytes) | |
@handle && @handle.read(bytes) | |
end | |
attr_reader :seed | |
def seed=(new_seed) | |
set_seed new_seed | |
@seed = new_seed | |
end | |
def swap_seed(new_seed) | |
old_seed, self.seed = self.seed, new_seed | |
old_seed | |
end | |
# Generate a random Integer, in the range 0...limit | |
def random_bignum(limit) | |
random_bits * (limit / (MAX + 1)) | |
end | |
def generate_seed | |
if urand = urandom(Rubinius::WORDSIZE / 2) | |
result = urand.unpack("I4") | |
else | |
result = [0, 0, 0, 0] | |
end | |
now = Time.now | |
result[0] ^= now.tv_usec | |
result[1] ^= now.tv_sec | |
result[2] ^= $$ ^ ((@counter += 1) << 16) | |
result[3] ^= result.object_id | |
(result[3] << (3 * Rubinius::WORDSIZE)) + | |
((result[2] << (2 * Rubinius::WORDSIZE)) + | |
((result[1] << (1 * Rubinius::WORDSIZE)) + result[0])) | |
end | |
N = 624 | |
M = 397 | |
N2 = N * 2 | |
M2 = M * 2 | |
C_89, X_89 = Rubinius::Tuple.new(256), Rubinius::Tuple.new(256) | |
C_65, X_65 = Rubinius::Tuple.new(256), Rubinius::Tuple.new(256) | |
X_6c = Rubinius::Tuple.new(256) | |
C_07, X_07 = Rubinius::Tuple.new(256), Rubinius::Tuple.new(256) | |
256.times do |i| | |
X_89[i] = 256 * C_89[i] = i * 0x89 | |
X_65[i] = 256 * C_65[i] = i * 0x65 | |
X_6c[i] = 256 * i * 0x6c | |
X_07[i] = 256 * C_07[i] = i * 0x07 | |
end | |
def next_seed(i, j) | |
left, right = @state[j], @state[j + 1] | |
right ^= left >> 14 | |
ll, lr = left >> 8, left & 0xff | |
rl, rr = right >> 8, right & 0xff | |
left = (X_65[ll] + X_89[lr] + X_07[rl] + X_6c[rr]) + C_65[lr] + C_07[rr] | |
right = (X_65[rl] + X_89[rr]) + C_65[rr] + i | |
@state[i] = (left + (right >> 16) + C_89[rl]) & 0xffff | |
@state[i + 1] = right & 0xffff | |
end | |
def set_seed(s) | |
s &= 0xffffffff | |
@state[0] = left = (s >> 16) & 0xffff | |
@state[1] = right = s & 0xffff | |
@seeding = true | |
@next = 0 | |
end | |
def next_seed! | |
if @next == 0 | |
@curr, @next = @next, @next + 2 | |
elsif @next < N2 - 2 | |
next_seed @next, @curr | |
@curr, @next = @next, @next + 2 | |
else | |
next_seed @next, @curr | |
@seeding = false | |
next_state! | |
end | |
end | |
def next_state! | |
if @next < N2 - 2 | |
@curr = @next | |
@next = @next + 2 | |
else | |
@curr = @next | |
@next = 0 | |
end | |
i = @curr | |
j = @next | |
if i < N2 - M2 | |
x = M2 + i | |
else | |
x = M2 - N2 + i | |
end | |
jl = @state[j] | |
jr = @state[j + 1] | |
left = ((@state[i] & 0x8000) | (jl & 0x7fff)) >> 1 | |
right = (jl & 1 << 15) | (jr >> 1) | |
if jr & 1 > 0 | |
left ^= 0x9908 | |
right ^= 0xb0df | |
end | |
@state[i] = left ^ @state[x] | |
@state[i + 1] = right ^ @state[x + 1] | |
end | |
def rb_genrand_int32 | |
if @seeding | |
next_seed! | |
else | |
next_state! | |
end | |
# 0x6c078965 == 1812433253 | |
left, right = @state[@curr], @state[@curr + 1] | |
#y ^= (y >> 11) | |
left, right = left ^ (left >> 11), right ^ (((left & 0x1f) << 5) | (right >> 11)) | |
#y ^= (y << 7) & 0x9d2c5680 | |
left, right = (((left & 0x1ff) << 7) | (right >> 9)) & 0x9d2c, ((right & 0x1ff) << 7) & 0x5680 | |
#y ^= (y << 15) & 0xefc60000 | |
left ^= (right >> 1) & 0xefc6 | |
#y ^= (y >> 18) | |
right ^= left >> 2 | |
(left << 16) | right | |
end | |
def rb_genrand_real | |
a = rb_genrand_int32 >> 5 | |
b = rb_genrand_int32 >> 6 | |
(a * 67108864.0 + b) * (1.0 / 9007199254740992.0) | |
end | |
def make_mask(x) | |
x |= x >> 1 | |
x |= x >> 2 | |
x |= x >> 4 | |
x |= x >> 8 | |
x |= x >> 16 | |
x |= x >> 32 if Rubinius::WORDSIZE > 32 | |
x | |
end | |
def limited_rand(limit) | |
mask = make_mask(limit) | |
loop do | |
val = 0 | |
(Rubinius::WORDSIZE / 8 / 4 - 1).downto(0) do |i| | |
if (mask >> i * 32) != 0 | |
val |= rb_genrand_int32 << (i * 32) | |
val &= mask | |
break if limit < val | |
end | |
return val if i == 0 | |
end | |
end | |
end | |
def random_float | |
rb_genrand_real | |
end | |
def random_fixnum(limit) | |
limited_rand(limit - 1) | |
end | |
def random_bits | |
rb_genrand_int32 | |
end | |
end | |
$rng = MTRandomizer.new | |
Benchmark.bmbm do |x| | |
x.report("random_float") do | |
total.times do | |
$rng.random_float | |
end | |
end | |
x.report("random_fixnum") do | |
total.times do | |
$rng.random_fixnum 10 | |
end | |
end | |
x.report("random_bignum") do | |
total.times do | |
$rng.random_bignum 0x12345678901234567890 | |
end | |
end | |
x.report("seed = generate_seed") do | |
total.times do | |
$rng.seed = $rng.generate_seed | |
end | |
end | |
x.report("seed = 0") do | |
total.times do | |
$rng.seed = 0 | |
end | |
end | |
x.report("seed = bignum") do | |
total.times do | |
$rng.seed = 0x12345678901234567890 | |
end | |
end | |
x.report("seed; random_float") do | |
(total / 20).times do | |
$rng.seed = 10 | |
50.times do | |
$rng.random_float | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment