]> mj.ucw.cz Git - libucw.git/commitdiff
Random: Removed sub-32bit generators.
authorPavel Charvat <pchar@ucw.cz>
Tue, 22 Mar 2022 15:30:19 +0000 (15:30 +0000)
committerPavel Charvat <pchar@ucw.cz>
Tue, 22 Mar 2022 18:06:59 +0000 (18:06 +0000)
ucw/random-fast.c

index 6c647ab2e4be8ba7e78d0dd1641848df51a2484f..9404abdcdcc5f7cd753261d6178156aef833ebf6 100644 (file)
@@ -22,8 +22,6 @@
 //#define FASTRAND_USE_PCG
 //#define FASTRAND_USE_SPLITMIX
 #define FASTRAND_USE_XOROSHIRO
-//#define FASTRAND_USE_LIBC_ISO
-//#define FASTRAND_USE_LIBC_BSD
 //#define FASTRAND_USE_LIBC_SVID
 
 /**
 // the higher ones only where trivial, for example in 32 bit fastrand_max().
 #define FASTRAND_XOROSHIRO_ONLY_HIGHER
 
-/**
- * FASTRAND_USE_LIBC_ISO
- * -- rand_r()
- * -- libc ISO generator
- * -- usually 31 bit output (RAND_MAX)
- * -- very weak
- **/
-
-/**
- * FASTRAND_USE_LIBC_BSD
- * -- random_r()
- * -- libc BSD generator
- * -- usually 31 bit output (RAND_MAX)
- **/
-
 /**
  * FASTRAND_USE_LIBC_SVID
  * -- mrand48_r()
  * -- libc SVID generator
- * -- 32 bit output
  **/
 
 struct fastrand {
@@ -118,16 +100,6 @@ struct fastrand {
   u64 xoroshiro_state[FASTRAND_XOROSHIRO_SIZE / 64];
 #endif
 
-#ifdef FASTRAND_USE_LIBC_ISO
-  uint iso_state;                      // ISO generator's current seed
-#endif
-
-#ifdef FASTRAND_USE_LIBC_BSD
-  struct random_data bsd_data;         // BSD generator's state structure
-  u64 bsd_state[128 / 8];              // Used "statebuf" in initstate_r(); see manual pages or glibc sources for useful sizes;
-                                       // glibc typecasts the (char *) to (int32_t *), so we rather make sure it's aligned
-#endif
-
 #ifdef FASTRAND_USE_LIBC_SVID
   struct drand48_data svid_data;       // SVID generator's state structure
 #endif
@@ -138,16 +110,6 @@ struct fastrand {
 
 /*** Common helpers ***/
 
-#if defined(FASTRAND_USE_LIBC_ISO) || defined(FASTRAND_USE_LIBC_BSD)
-#  if RAND_MAX == (1U << 30) - 1
-#    define FASTRAND_RAND_MAX_BITS 30
-#  elif RAND_MAX == (1U << 31) - 1
-#    define FASTRAND_RAND_MAX_BITS 31
-#  else
-#    error "Unsupported value of RAND_MAX"
-#  endif
-#endif
-
 // GCC should optimize these to a single instruction
 
 static inline u32 fastrand_ror32(u32 x, uint r)
@@ -286,58 +248,6 @@ fastrand_one(struct fastrand *c)
 
 #endif
 
-/*** Libc ISO generator ***/
-
-#ifdef FASTRAND_USE_LIBC_ISO
-#define FASTRAND_ONE_BITS FASTRAND_RAND_MAX_BITS
-
-static void fastrand_init(struct fastrand *c)
-{
-  c->iso_state = c->seed_value;
-  DBG("RANDOM: Initialized libc ISO random generator, seed=0x%jx, bits=%u", (uintmax_t)c->seed_value, FASTRAND_ONE_BITS);
-}
-
-static inline u32 fastrand_one(struct fastrand *c)
-{
-  return rand_r(&c->iso_state);
-}
-
-#endif
-
-/*** Libc BSD generator ***/
-
-#ifdef FASTRAND_USE_LIBC_BSD
-#define FASTRAND_ONE_BITS FASTRAND_RAND_MAX_BITS
-
-static void fastrand_init(struct fastrand *c)
-{
-  DBG("RANDOM: Initialized libc BSD random generator, seed=0x%jx, statelen=%zu, bits=%u", (uintmax_t)c->seed_value, sizeof(c->bsd_state), FASTRAND_ONE_BITS);
-  c->bsd_data.state = NULL; // Required by initstate_r()
-  if (initstate_r(c->seed_value, (char *)c->bsd_state, sizeof(c->bsd_state), &c->bsd_data) < 0)
-    die("RANDOM: Failed to call initstate_r(): %m");
-}
-
-#ifdef LOCAL_DEBUG
-static NONRET void fastrand_random_r_failed(void)
-{
-  die("RANDOM: Failed to call random_r(): %m");
-}
-#endif
-
-static inline u32 fastrand_one(struct fastrand *c)
-{
-  int32_t r;
-#ifdef LOCAL_DEBUG
-  if (unlikely(random_r(&c->bsd_data, &r) < 0))
-    fastrand_random_r_failed();
-#else
-  (void) random_r(&c->bsd_data, &r);
-#endif
-  return r;
-}
-
-#endif
-
 /*** Libc SVID generator ***/
 
 #ifdef FASTRAND_USE_LIBC_SVID
@@ -441,50 +351,27 @@ static NONRET void fastrand_check_seed_failed(void)
 
 /*** Reading interface ***/
 
-// We assume either 64bit or 24-32bit generator
-COMPILE_ASSERT(fastrand_one_range, (FASTRAND_ONE_BITS >= 24 && FASTRAND_ONE_BITS <= 32) || FASTRAND_ONE_BITS == 64);
+// We assume 32-64 bit output of fastrand_one().
+// For 32 bits fastrand_one() can return u32 type, otherwise it must be u64.
+COMPILE_ASSERT(fastrand_one_range, FASTRAND_ONE_BITS >= 32 && FASTRAND_ONE_BITS <= 64);
 
-#if FASTRAND_ONE_BITS < 64
-#define FASTRAND_ONE_MAX (UINT32_MAX >> (32 - FASTRAND_ONE_BITS))
+#if FASTRAND_ONE_BITS == 32
+#define FASTRAND_ONE_MAX UINT32_MAX
 #else
-#define FASTRAND_ONE_MAX UINT64_MAX
-#endif
-
-#if FASTRAND_ONE_BITS < 64
-#define FASTRAND_TWO_BITS (FASTRAND_ONE_BITS * 2)
-#define FASTRAND_TWO_MAX (UINT64_MAX >> (64 - FASTRAND_TWO_BITS))
-static inline u64 fastrand_two(struct fastrand *c)
-{
-  return ((u64)fastrand_one(c) << FASTRAND_ONE_BITS) | fastrand_one(c);
-}
-#endif
-
-#if FASTRAND_ONE_BITS < 32
-#define FASTRAND_THREE_BITS 64
-#define FASTRAND_THREE_MAX UINT64_MAX
-static inline u64 fastrand_three(struct fastrand *c)
-{
-  return ((u64)fastrand_one(c) << (FASTRAND_ONE_BITS * 2)) | ((u64)fastrand_one(c) << FASTRAND_ONE_BITS) | fastrand_one(c);
-}
+#define FASTRAND_ONE_MAX (UINT64_MAX >> (64 - FASTRAND_ONE_BITS))
 #endif
 
 u32 fastrand_u32(struct fastrand *c)
 {
   FASTRAND_CHECK_SEED(c);
-#if FASTRAND_ONE_BITS < 32
-  return fastrand_two(c);
-#else
   return fastrand_one(c);
-#endif
 }
 
 u64 fastrand_u64(struct fastrand *c)
 {
   FASTRAND_CHECK_SEED(c);
-#if FASTRAND_ONE_BITS < 32
-  return fastrand_three(c);
-#elif FASTRAND_ONE_BITS < 64
-  return fastrand_two(c);
+#if FASTRAND_ONE_BITS < 64
+  return ((u64)fastrand_one(c) << FASTRAND_ONE_BITS) | fastrand_one(c);
 #else
   return fastrand_one(c);
 #endif
@@ -500,21 +387,12 @@ u64 fastrand_bits_u64(struct fastrand *c, uint bits)
 #if FASTRAND_ONE_BITS == 64
   if (!bits)
     return 0;
-  u64 r = fastrand_one(c);
-  return r >> (FASTRAND_ONE_BITS - bits);
+  return fastrand_one(c) >> (FASTRAND_ONE_BITS - bits);
 #else
-  byte n = bits;
   u64 r = fastrand_one(c);
-  if (n <= FASTRAND_ONE_BITS)
-    return r >> (FASTRAND_ONE_BITS - n);
-  while (1)
-    {
-      n -= FASTRAND_ONE_BITS;
-      u64 x = fastrand_one(c);
-      if (n <= FASTRAND_ONE_BITS)
-       return (r << n) | (x >> (FASTRAND_ONE_BITS - n));
-      r = (r << FASTRAND_ONE_BITS) | x;
-    }
+  if (bits <= FASTRAND_ONE_BITS)
+    return r >> (FASTRAND_ONE_BITS - bits);
+  return (r << (bits - FASTRAND_ONE_BITS)) | (fastrand_one(c) >> (2 * FASTRAND_ONE_BITS - bits));
 #endif
 }
 
@@ -561,24 +439,24 @@ u64 fastrand_bits_u64(struct fastrand *c, uint bits)
  * Can be useful for very fast fastrand_one() like xoroshiro128+.
  */
 
-#define FASTRAND_MAX_ONE_U32_ALGORITHM 2
-#define FASTRAND_MAX_MORE_ALGORITHM 3
+#define FASTRAND_MAX_U32_ALGORITHM 2
+#define FASTRAND_MAX_U64_ALGORITHM 3
 
-static inline u32 fastrand_max_one_u32(struct fastrand *c, u32 bound)
+static inline u32 fastrand_max_u32_helper(struct fastrand *c, u32 bound)
 {
-  // Variant for bound <= MIN(UINT32_MAX, FASTRAND_ONE_MAX).
+  // Variant for bound <= UINT32_MAX.
   // This is the most common case -> we optimize it and inline the code to both fastrand_max() and fastrand_max_u64().
 
-#if FASTRAND_MAX_ONE_U32_ALGORITHM == 1
+#if FASTRAND_MAX_U32_ALGORITHM == 1
   while (1)
     {
       u32 r = fastrand_one(c);
       u32 x = r % bound;
-      if (r - x <= (u32)FASTRAND_ONE_MAX + 1 - bound)
+      if (r - x <= (u32)-bound)
        return x;
 #if 1
       // Possible optimization for unlucky cases
-      uint y = r - x;
+      u32 y = r - x;
       do
        r = fastrand_one(c);
       while (r >= y);
@@ -586,15 +464,13 @@ static inline u32 fastrand_max_one_u32(struct fastrand *c, u32 bound)
 #endif
     }
 
-#elif FASTRAND_MAX_ONE_U32_ALGORITHM == 2
-  // Algorithm 2:
-
+#elif FASTRAND_MAX_U32_ALGORITHM == 2
   u32 r = fastrand_one(c);
   u64 m = (u64)r * bound;
-  u32 l = m & (u32)FASTRAND_ONE_MAX;
+  u32 l = (u32)m;
   if (l < bound)
     {
-      u32 t = (u32)FASTRAND_ONE_MAX + 1 - bound;
+      u32 t = (u32)-bound;
 #if 1
       // This condition can decrease the probability of division to <= 50% even in worst case and also optimize bad cases.
       if (t < bound)
@@ -611,12 +487,12 @@ static inline u32 fastrand_max_one_u32(struct fastrand *c, u32 bound)
        {
          r = fastrand_one(c);
          m = (u64)r * bound;
-         l = m & (u32)FASTRAND_ONE_MAX;
+         l = (u32)m;
        }
     }
-  return m >> MIN(FASTRAND_ONE_BITS, 32);
+  return m >> 32;
 
-#elif FASTRAND_MAX_ONE_U32_ALGORITHM == 3
+#elif FASTRAND_MAX_U32_ALGORITHM == 3
   // XXX: Probably could be optimized with __builtin_clz()
   u32 r, y = bound - 1;
   y |= y >> 1;
@@ -630,24 +506,20 @@ static inline u32 fastrand_max_one_u32(struct fastrand *c, u32 bound)
   return r;
 
 #else
-#  error Invalid FASTRAND_MAX_ONE_U32_ALGORITHM
+#  error Invalid FASTRAND_MAX_U32_ALGORITHM
 #endif
 }
 
-static u64 fastrand_max_more(struct fastrand *c, u64 bound)
+static u64 fastrand_max_u64_helper(struct fastrand *c, u64 bound)
 {
   // Less common case -> compromise between speed and size
-#if FASTRAND_MAX_MORE_ALGORITHM == 1
+#if FASTRAND_MAX_U64_ALGORITHM == 1
   while (1)
     {
       u64 r = fastrand_one(c);
       u64 m = FASTRAND_ONE_MAX;
 #if FASTRAND_ONE_BITS < 64
-#if FASTRAND_ONE_BITS < 32
-      while (bound > m + 1)
-#else
       if (bound > m + 1)
-#endif
        {
          r = (r << FASTRAND_ONE_BITS) | fastrand_one(c);
          m = (m << FASTRAND_ONE_BITS) | FASTRAND_ONE_MAX;
@@ -658,7 +530,7 @@ static u64 fastrand_max_more(struct fastrand *c, u64 bound)
        return x;
     }
 
-#elif FASTRAND_MAX_MORE_ALGORITHM == 3
+#elif FASTRAND_MAX_U64_ALGORITHM == 3
   u64 r, y = bound - 1;
   y |= y >> 1;
   y |= y >> 2;
@@ -669,47 +541,39 @@ static u64 fastrand_max_more(struct fastrand *c, u64 bound)
   do
     {
       r = fastrand_one(c);
-      u64 m = FASTRAND_ONE_MAX;
 #if FASTRAND_ONE_BITS < 64
-#if FASTRAND_ONE_BITS < 32
-      while (y > m)
-#else
-      if (y > m)
-#endif
-       {
-         r = (r << FASTRAND_ONE_BITS) | fastrand_one(c);
-         m = (m << FASTRAND_ONE_BITS) | FASTRAND_ONE_MAX;
-       }
+      if (y > FASTRAND_ONE_MAX)
+       r = (r << FASTRAND_ONE_BITS) | fastrand_one(c);
 #endif
       r &= y;
     }
   while (r >= bound);
   return r;
 #else
-#  error Invalid FASTRAND_MAX_MORE_ALGORITHM
+#  error Invalid FASTRAND_MAX_U64_ALGORITHM
 #endif
 }
 
 uint fastrand_max(struct fastrand *c, uint bound)
 {
   FASTRAND_CHECK_SEED(c);
-#if (UINT32_MAX & FASTRAND_ONE_MAX) < UINT_MAX
-  if (bound <= (u32)(FASTRAND_ONE_MAX) + 1)
-    return fastrand_max_one_u32(c, bound);
+#if UINT32_MAX < UINT_MAX
+  if (bound <= UINT32_MAX)
+    return fastrand_max_u32_helper(c, bound);
   else
-    return fastrand_max_more(c, bound);
+    return fastrand_max_u64_helper(c, bound);
 #else
-  return fastrand_max_one_u32(c, bound);
+  return fastrand_max_u32_helper(c, bound);
 #endif
 }
 
 u64 fastrand_max_u64(struct fastrand *c, u64 bound)
 {
   FASTRAND_CHECK_SEED(c);
-  if (bound <= (u64)(u32)(FASTRAND_ONE_MAX) + 1)
-    return fastrand_max_one_u32(c, bound);
+  if (bound <= UINT32_MAX)
+    return fastrand_max_u32_helper(c, bound);
   else
-    return fastrand_max_more(c, bound);
+    return fastrand_max_u64_helper(c, bound);
 }
 
 void fastrand_mem(struct fastrand *c, void *ptr, size_t size)
@@ -737,31 +601,13 @@ void fastrand_mem(struct fastrand *c, void *ptr, size_t size)
          p += 8;
        }
       if (size & 4)
-       {
-         *(u32 *)p = fastrand_one(c);
-         p += 4;
-       }
 #else
-#if FASTRAND_ONE_BITS < 32
-      uint y, y_n = 0;
-#endif
       for (size_t n = size >> 2; n--; )
-       {
-#if FASTRAND_ONE_BITS < 32
-         if (!y_n--)
-           {
-             y_n = FASTRAND_ONE_BITS / (32 - FASTRAND_ONE_BITS) - 1;
-             y = fastrand_one(c);
-           }
-         uint x = fastrand_one(c) | (y << (FASTRAND_ONE_BITS - 24));
-         y >>= 32 - FASTRAND_ONE_BITS;
-#else
-         uint x = fastrand_one(c);
 #endif
-         *(u32 *)p = x;
+       {
+         *(u32 *)p = fastrand_one(c);
          p += 4;
        }
-#endif
       size &= 3;
     }
   if (size)