1

I have a RNG function xorshift128plus that takes a Xorshift128PlusKey:

/** 
        * \brief Keys for scalar xorshift128. Must be non-zero.
        * These are modified by xorshift128plus.
        */
        struct Xorshift128PlusKey
        {
            uint64_t s1;
            uint64_t s2;
        };

        /** 
        * \brief Return a new 64-bit random number.
        */
        uint64_t xorshift128plus(Xorshift128PlusKey* key);

I would like to seed my RNG using rdtsc (processor time stamp). The problem is that the __rdtsc intrinsic under msvc returns a 64 bit unsigned integer and the seed must be a 32 bit unsigned integer. What is the best way to convert the rdtsc to a seed while preserving randomness. The conversion must be as fast as possible.

I can't use the std lib or boost. (It's for a game engine)

plasmacel
  • 8,183
  • 7
  • 53
  • 101
Coder32
  • 181
  • 1
  • 10
  • I suppose "preserving randomness" is relative; you're going to lose information casting to half the bits. – AndyG Apr 27 '17 at 16:57
  • 3
    What randomness do you expect to find in a time stamp that can be preserved? The only thing vaguely random about it is when the machine was last booted (which might not be random at all, e.g. if your program runs automatically on boot). Since the low 32-bits will be changing far more rapidly than the high ones, why not just take the low 32-bits? – Jonathan Wakely Apr 27 '17 at 16:58

1 Answers1

9

The 64-bit processor timestamp isn't random at all, so there is no need to preserve randomness when narrowing it to 32 bits. You can simply use the least-significant 32 bits as your seed. Randomness is the responsibility of the PRNG, not of the seed.

unsigned __int64 tsc = __rdtsc();
uint32_t seed = static_cast<uint32_t>(tsc & 0xFFFFFFFF);
TypeIA
  • 16,916
  • 1
  • 38
  • 52
  • Or write `uint32_t seed = __rdtsc();` like a normal person, and let the compiler throw away the high bits. (You might have to cast or assign it to `uint64_t` first so you get unsigned overflow instead of signed overflow when narrowing, if you want to be really pedantic. But casting and ANDing seems pointless.) – Peter Cordes Aug 18 '18 at 12:10
  • @PeterCordes Thanks, the code posted here is written the way it is for illustration / educational purposes. It is equivalent to what you suggest and an optimizing compiler most likely generates the same instructions. – TypeIA Aug 18 '18 at 12:52
  • You also may need the cast to avoid a compiler warning. – TypeIA Aug 18 '18 at 12:55
  • Yes, compilers know how to optimize that all away, and in fact do so. (https://godbolt.org/g/t423Ts). You're right, MSVC does warn if you enable `-Wall`, but MSVC's own header files spew warnings with `-Wall`. gcc/clang/icc don't warn even with `-Wall -Wextra -Wpedantic`. I don't usually use MSVC, so I'm only used to gcc/clang `-Wall` warnings. I'd still argue that `& 0xFFFFFFFF` is worse for readability because you have to count the digits to make sure there are 8 or more so it's actually a no-op. Omitting it doesn't cause a warning. – Peter Cordes Aug 18 '18 at 14:07
  • And BTW, it turns out `__rdtsc()` is defined as returning an `unsigned` 64-bit type, not signed, so that was never a problem. `_rdtsc()` with one underscore is documented as [returning signed `__int64` by Intel](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=4067,602,2978,4071,4255&techs=SSE,SSE2,SSE3,SSSE3,SSE4_1,SSE4_2,AVX,AVX2,Other&text=rdtsc). – Peter Cordes Aug 18 '18 at 14:08