Skip to content

Modernize the PRNG used for rand() by using a Configure option#24105

Open
scottchiefbaker wants to merge 2 commits intoPerl:bleadfrom
scottchiefbaker:modern_rand
Open

Modernize the PRNG used for rand() by using a Configure option#24105
scottchiefbaker wants to merge 2 commits intoPerl:bleadfrom
scottchiefbaker:modern_rand

Conversation

@scottchiefbaker
Copy link
Contributor

@scottchiefbaker scottchiefbaker commented Jan 20, 2026

Per past discussion on p5p we know the drand48 we use as a PRNG has limitations. This PR attempts to cleanly upgrade the PRNG used for rand() calls, while leaving the existing drand48 code in place for internal Perl stuff (hash seed, etc). I found randfunc and seedfunc options in Configure that allow us to point at a new PRNG without affecting the existing code.

I created a new prng.h file which is largely self-contained except for a couple of additions to embed.fnc and Configure. I have included two new PRNG implementations: PCG64 (my preference) and xoroshiro128** as options.

Completed items

  • Modern PRNG implementation (PCG64)
  • Detailed instructions for future devs on how to extend and modify
  • Updated unit tests
  • Verify srand() functionality works as expected
  • Verify the new rand() outputs the full 53 bit state capable from a double (drand48 could only do 48 bits)
    • ./perl -I lib -E 'for (1..5) { printf("%064b\n", rand() * 2**64-1); }'

TODO

  • prng.h does not seem to be rebuilt consistently after changes. Do I need to add this new file to build system?
  • Bikeshed on what the best PRNG is in 2026
  • make regen puts the functions prototypes in a weird location "Used in locale.c and perl.c"
  • Add an option to get a random integer? rand64()?

Alternate options

  • We don't do anything. rand() is "good enough"
  • Point users at CPAN. Random::Simple is a drop-in replacement for rand() and srand() already


typedef struct { uint64_t state; uint64_t inc; } pcg64_random_t;
// Global PRNG object
pcg64_random_t prng;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't thread-safe.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this should probably live in the interpreter instead. And do something sensible on thread cloning so different threads don't get the same results.

@scottchiefbaker scottchiefbaker force-pushed the modern_rand branch 3 times, most recently from 40d4a6d to 1b78937 Compare January 26, 2026 23:17
@scottchiefbaker scottchiefbaker force-pushed the modern_rand branch 3 times, most recently from 9002fb7 to b0c2c62 Compare February 2, 2026 16:49
@scottchiefbaker scottchiefbaker changed the title Modernize the PRNG used for rand() by add a Configure option Modernize the PRNG used for rand() by adding a Configure option Feb 3, 2026
@scottchiefbaker scottchiefbaker changed the title Modernize the PRNG used for rand() by adding a Configure option Modernize the PRNG used for rand() by using a Configure option Feb 3, 2026
Comment on lines +67 to +68
const uint64_t word = ((prng.state >> ((prng.state >> 59) + 5)) ^ prng.state) * 12605985483714917081ull;
prng.state = prng.state * 6364136223846793005ull + prng.inc;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these fairly arbitrary numbers, or did they get created by some specific process? Either way, I'd like to see some sort of comment explaining why these numbers in particular.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That algorithm was designed by people WAY smarter than me. Per a previous iteration of this PR I added notes in prng.h with links to various documents talking about PCG64 and how to choose a good PRNG:

// Notes:
// https://zephyrtronium.github.io/articles/randomness.html
// https://docs.oracle.com/en/java/javase/21/core/choosing-prng-algorithm.html
// https://github.com/alvoskov/SmokeRand

TLDR; PCG64 has been tested and vetted by people smarter than me.

// Splitmix64 is defined in util.c
static U64 splitmix64(U64 *state);

typedef struct { uint64_t state; uint64_t inc; } pcg64_random_t;
Copy link
Contributor

@Leont Leont Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this should be U64 instead of uint64_t

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants