[Perl/perl5] d3f8bc: Add support for an ${^RNG} hook, along with suppor...

[email protected] (Yves Orton via perl5-changes)
Newsgroups perl.perl5.changes
Message-ID <Perl/perl5/push/refs/heads/yves/caret_rng_hook/[email protected]>
  Branch: refs/heads/yves/caret_rng_hook
  Home:   https://github.com/Perl/perl5
  Commit: d3f8bc6f8ada2dec5dbc794fe723ce5fb02e3d9c
      https://github.com/Perl/perl5/commit/d3f8bc6f8ada2dec5dbc794fe723ce5fb02e3d9c
  Author: Yves Orton <[email protected]>
  Date:   2026-08-17 (Mon, 17 Aug 2026)

  Changed paths:
    M MANIFEST
    M Makefile.SH
    M Porting/Maintainers.pl
    M Porting/cmpVERSION.pl
    M Porting/config.sh
    A dist/RNG/Changes
    A dist/RNG/MANIFEST
    A dist/RNG/Makefile.PL
    A dist/RNG/README
    A dist/RNG/RNG.xs
    A dist/RNG/lib/RNG.pm
    A dist/RNG/lib/RNG/PCG.pm
    A dist/RNG/lib/RNG/SHA.pm
    A dist/RNG/t/pcg.t
    A dist/RNG/t/sha.t
    M embed.fnc
    M embed.h
    M lib/.gitignore
    M pod/perldelta.pod
    M pod/perldiag.pod
    M pod/perlfunc.pod
    M pod/perlvar.pod
    M pp.c
    M proto.h
    M t/op/srand.t
    M util.h
    M win32/GNUmakefile
    M win32/Makefile

  Log Message:
  -----------
  Add support for an ${^RNG} hook, along with support PCG randomization

Historically the random number generator used by perl has evolved over
time, in the early days it was the system RNG, then later we rolled our
own version drand48 for cross platform stability. But these days
drand48 has a bunch of flaws so it would be nice to be able to use
something better.

However this is not so easy. Most code just calls rand() because it is
the easiest thing to do. Most people dont bother adding support for
their own hook, or burdening their consumers with having to provide an
RNG, especially as there is no standard for doing so. Some code maybe
ends up with support for an RNG hook, like List::Util did, but IMO such
cases are a rarity, and a proliferation of slightly different hooks for
this would not be helpful.

In theory one can override CORE::rand() and CORE::srand() before a
module is loaded, and thus change the RNG, but the reality is there is
no good way to overload srand()/rand() in another packages namespace.
(So when people say "just put your pet RNG on CPAN" it is kind of a cop
out, that RNG is likely to never really be useful as it just wont get
used by most code.)

Similarly we can't really solve this problem with a new builtin function
that implements a new rng. Such a builtin wouldn't be used by anything
and would only be useful to new code that was written to use it. It
would have the same problems as the current rand() and it would not be
surprising if in 10 years when the PCG has been replaced by something
else we will be back to square one, but now with two problems. :-)

At the same time we don't really want to change Perls internal code to
use something other than drand48(). There is lots of code that expects
that doing srand(1234) will produce test results that look a specific
way, so changing cores RNG would cause all kinds of unfortunate drama
through the ecosystem.

This patch takes a different approach. Let the user hook rand() to use
an alternative, so that the user can choose which algorith, and how it
was seeded, etc. In this model ${^RNG} is used as a hook for
rand() and srand(). If ${^RNG} contains an object then it uses the
objects rand_uv() and srand() methods to do its thing. If it
contains an unblessed subref then it calls it with no arguments for
rand() and with arguments for srand() (srand(undef) is defined to be
the same as srand()).

This is wired into the macro Drand01() which despite its name and
appearance is actually a pTHX style macro, but which actually hides the
fact by access what looks like a global var, but is actually an
interpreter var under threads, thus hiding that it does operate on aTHX
just not directly. This allows us to redirect so that modules like
List::Util will still respect the hook even though they bypass pp_rand()
for most of their random calls.

I thought about using ${^HOOKS}{RNG} instead of ${^RNG}, but for the
time I decided to just use ${^RNG} as it feels better to do
somethinglike:

    local ${^RNG} = RNG::PCG->new();

than it does to do:

    local ${^HOOKS}{RNG}

and it allows the rng internals to avoid the hash lookup. Although
currently it is not optimizing the ${^RNG} package var fetch, and the
var is not magic in any way so the ${^RNG} hook is not being stored in
an interpreter var so the current implementation could be sped up we
wished. IMO it is preferable to keep this as simple as possible.

As part of this patch I have added a new dist/RNG which contains XS code
to implement the two dimensional PCG-XSH-RR 64/128, which allows for 128
bit state with only 64 bit or 32 operations. This seems to be the state
of the art RNG these days.

I also implemented an RNG::SHA which uses SHA256 to produce the stream
of random values, most just as a proof of concept.

I chose to make RNG use a UV as its base currency, so rand() calls
${^RNG}->rand_uv() which returns a UV, which is then converted into
whatever the user requested. I did it this way so that any floating
point operations or conversion to floating point operations could be
performed by the C code. It feels like a more flexible foundation
than using a float. /me waves hands.

Modules like List::Util which bind to Drand01() at the C level still
call the callback, so even without using List::Util's hook you can
override it just like any other perl code.

This effectively means that every piece of code that uses rand() to
do randomness now has the same capability as was hand hacked in
List::Util long ago. On callback to rull them all sort of thing. :-)



To unsubscribe from these emails, change your notification settings at https://github.com/Perl/perl5/settings/notifications
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.