[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
a simple rng in scheme
From: |
Andy Wingo |
Subject: |
a simple rng in scheme |
Date: |
Thu, 01 Sep 2016 12:41:31 +0200 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux) |
Hi,
Here is a simple implementation of what I think is xorshift128+, a
simple random number generator. I have verified the first 10 outputs
against
https://github.com/AndreasMadsen/xorshift/blob/master/reference.c.
(define (xorshift128+ s0 s1)
(define (u64 x) (logand #xffffFFFFffffFFFF x))
(define (ulogxor x y) (u64 (logxor x y)))
(define (ulsh x y) (u64 (ash x y)))
(define (ursh x y) (u64 (ash x (- y))))
(define (uadd x y) (u64 (+ x y)))
(let ((state (u64vector s0 s1)))
(lambda ()
(let* ((x (u64vector-ref state 0))
(y (u64vector-ref state 1))
(x* y)
(y* (ulogxor x (ulsh x 23)))
(y* (ulogxor y* (ursh y* 17)))
(y* (ulogxor y* (ulogxor y (ursh y 26)))))
(u64vector-set! state 0 x*)
(u64vector-set! state 1 y*)
(let ((res (uadd y* y)))
(values (logand res #xffffFFFF) (ash res -32)))))))
This function returns 2 32-bit values instead of one 64-bit value, to
avoid allocating bignums. In fact with the latest Guile master, this
function does not allocate at all. It can produce around 20M random
numbers per second on this old desktop.
The disassembly looks like:
----------------------------------------
Disassembly of anonymous procedure at #x7fdb8320f168:
0 (assert-nargs-ee/locals 1 5) ;; 6 slots (0 args) at
/tmp/xorshift128+.scm:8:4
1 (load-u64 4 0 0) at
/tmp/xorshift128+.scm:9:16
4 (free-ref 5 5 0) ;; free var 0
6 (bv-u64-ref 3 5 4)
7 (load-u64 2 0 8) at
/tmp/xorshift128+.scm:10:16
10 (bv-u64-ref 1 5 2)
11 (ulsh/immediate 0 3 23) at
/tmp/xorshift128+.scm:4:26
12 (ulogxor 3 3 0) at
/tmp/xorshift128+.scm:3:29
13 (ursh/immediate 0 3 17) at
/tmp/xorshift128+.scm:5:26
14 (ulogxor 3 3 0) at
/tmp/xorshift128+.scm:3:29
15 (ursh/immediate 0 1 26) at
/tmp/xorshift128+.scm:5:26
16 (ulogxor 0 1 0) at
/tmp/xorshift128+.scm:3:29
17 (ulogxor 3 3 0)
18 (bv-u64-set! 5 4 1) at
/tmp/xorshift128+.scm:15:8
19 (bv-u64-set! 5 2 3) at
/tmp/xorshift128+.scm:16:8
20 (uadd 5 3 1) at
/tmp/xorshift128+.scm:6:26
21 (load-u64 4 0 4294967295) at
/tmp/xorshift128+.scm:18:18
24 (ulogand 4 5 4)
25 (u64->scm 4 4)
26 (ursh/immediate 5 5 32) at
/tmp/xorshift128+.scm:18:42
27 (u64->scm 3 5)
28 (return-values 3) ;; 2 values at
/tmp/xorshift128+.scm:18:10
As you can see, all unboxed operations. To get this, I had to do a few
things to Guile:
* Introduce unboxed logxor.
* Compute the set of bits needed in any integer result, as a backwards
flow problem.
* Use that information to back-propagate constraints from
(logand #xffffffffffffffff EXP) back to EXP, allowing EXP to unbox
even if its output is out of the u64 range.
* Elide (logand #xffffffffffffffff EXP) once EXP has been unboxed.
Regards,
Andy
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- a simple rng in scheme,
Andy Wingo <=