From: Pavel Charvat Date: Tue, 22 Mar 2022 15:30:19 +0000 (+0000) Subject: Random: Removed sub-32bit generators. X-Git-Tag: v6.5.14~9 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=250df2e5a9faa50b85e4b3e1adf953db120c7006;p=libucw.git Random: Removed sub-32bit generators. --- diff --git a/ucw/random-fast.c b/ucw/random-fast.c index 6c647ab2..9404abdc 100644 --- a/ucw/random-fast.c +++ b/ucw/random-fast.c @@ -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 /** @@ -83,26 +81,10 @@ // 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)