This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] PR fortran/91414: Improved PRNG


On Mon, Aug 12, 2019 at 11:23 PM Steve Kargl
<sgk@troutmask.apl.washington.edu> wrote:
>
> On Sun, Aug 11, 2019 at 12:37:49PM +0300, Janne Blomqvist wrote:
> > Update the PRNG from xorshift1024* to xoshiro256** by the same
> > author. For details see
> >
> > http://prng.di.unimi.it/
> >
> > and the paper at
> >
> > https://arxiv.org/abs/1805.01407
> >
> > Also the seeding is slightly improved, by reading only 8 bytes from
> > the operating system and using the simple splitmix64 PRNG to fill in
> > the rest of the PRNG state (as recommended by the xoshiro author),
> > instead of reading the entire state from the OS.
> >
> > Regtested on x86_64-pc-linux-gnu, Ok for trunk?
> >
>
> Looks good to me.

Thanks, committed to trunk (with a minor bugfix I noticed). I
backported the initialization changes to the gcc9 and gcc8 branches as
well with the attached patch.



-- 
Janne Blomqvist
From 16abae420bcff75950868a30327bf7b242e0388e Mon Sep 17 00:00:00 2001
From: Janne Blomqvist <blomqvist.janne@gmail.com>
Date: Tue, 13 Aug 2019 10:35:05 +0300
Subject: [PATCH] PR fortran/91414 Improve initialization of PRNG

As part of PR 91414 an improved PRNG was contributed to trunk. This is
a partial backport of some related changes to the PRNG. Namely when
seeding the PRNG, it needs only 8 bytes of randomness from the OS, and
uses a simple splitmix64 PRNG to fill in the rest of the state,
instead of getting all the state from the OS. This can be useful for
operating systems that can run out of entropy.

libgfortran/ChangeLog:

2019-08-13  Janne Blomqvist  <jb@gcc.gnu.org>

	Partial backport from trunk
	PR fortran/91414
	* intrinsics/random.c (lcg_parkmiller): Replace with splitmix64.
	(splitmix64): New function.
	(getosrandom): Fix return value, simplify.
	(init_rand_state): Use getosrandom only to get 8 bytes, splitmix64
	to fill rest of state.
---
 libgfortran/intrinsics/random.c | 51 ++++++++++++++-------------------
 1 file changed, 21 insertions(+), 30 deletions(-)

diff --git a/libgfortran/intrinsics/random.c b/libgfortran/intrinsics/random.c
index 7476439647c..9283477482f 100644
--- a/libgfortran/intrinsics/random.c
+++ b/libgfortran/intrinsics/random.c
@@ -275,30 +275,19 @@ jump (xorshift1024star_state* rs)
 }
 
 
-/* Super-simple LCG generator used in getosrandom () if /dev/urandom
-   doesn't exist.  */
+/* Splitmix64 recommended by xorshift author for initializing.  After
+   getting one uint64_t value from the OS, this is used to fill in the
+   rest of the state.  */
 
-#define M 2147483647 /* 2^31 - 1 (A large prime number) */
-#define A 16807      /* Prime root of M, passes statistical tests and produces a full cycle */
-#define Q 127773 /* M / A (To avoid overflow on A * seed) */
-#define R 2836   /* M % A (To avoid overflow on A * seed) */
-
-__attribute__((unused)) static uint32_t
-lcg_parkmiller(uint32_t seed)
+static uint64_t
+splitmix64 (uint64_t x)
 {
-    uint32_t hi = seed / Q;
-    uint32_t lo = seed % Q;
-    int32_t test = A * lo - R * hi;
-    if (test <= 0)
-        test += M;
-    return test;
+  uint64_t z = (x += 0x9e3779b97f4a7c15);
+  z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
+  z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
+  return z ^ (z >> 31);
 }
 
-#undef M
-#undef A
-#undef Q
-#undef R
-
 
 /* Get some random bytes from the operating system in order to seed
    the PRNG.  */
@@ -315,7 +304,7 @@ getosrandom (void *buf, size_t buflen)
 #else
 #ifdef HAVE_GETENTROPY
   if (getentropy (buf, buflen) == 0)
-    return 0;
+    return buflen;
 #endif
   int flags = O_RDONLY;
 #ifdef O_CLOEXEC
@@ -328,7 +317,7 @@ getosrandom (void *buf, size_t buflen)
       close (fd);
       return res;
     }
-  uint32_t seed = 1234567890;
+  uint64_t seed = 0x047f7684e9fc949dULL;
   time_t secs;
   long usecs;
   if (gf_gettime (&secs, &usecs) == 0)
@@ -340,13 +329,9 @@ getosrandom (void *buf, size_t buflen)
   pid_t pid = getpid();
   seed ^= pid;
 #endif
-  uint32_t* ub = buf;
-  for (size_t i = 0; i < buflen / sizeof (uint32_t); i++)
-    {
-      ub[i] = seed;
-      seed = lcg_parkmiller (seed);
-    }
-  return buflen;
+  size_t size = buflen < sizeof (uint64_t) ? buflen : sizeof (uint64_t);
+  memcpy (buf, &seed, size);
+  return size;
 #endif /* __MINGW64_VERSION_MAJOR  */
 }
 
@@ -361,7 +346,13 @@ init_rand_state (xorshift1024star_state* rs, const bool locked)
     __gthread_mutex_lock (&random_lock);
   if (!master_init)
     {
-      getosrandom (master_state, sizeof (master_state));
+      uint64_t os_seed;
+      getosrandom (&os_seed, sizeof (os_seed));
+      for (uint64_t i = 0; i < sizeof (master_state) / sizeof (uint64_t); i++)
+	{
+          os_seed = splitmix64 (os_seed);
+          master_state[i] = os_seed;
+        }
       njumps = 0;
       master_init = true;
     }
-- 
2.17.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]