guile-user
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]