Skip to content

Instantly share code, notes, and snippets.

@matthewd
Created March 18, 2010 01:02
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 matthewd/335927 to your computer and use it in GitHub Desktop.
Save matthewd/335927 to your computer and use it in GitHub Desktop.
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