Skip to content

An implementation of generators from the MWCX family - #171

Merged
mborland merged 2 commits into
boostorg:developfrom
vigna:mwcx
Nov 2, 2026
Merged

mborland merged 2 commits into
boostorg:developfrom
vigna:mwcx

Conversation

@vigna

@vigna vigna commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

This PR adds four scrambled multiply-with-carry generators from "Scrambled Multiply-with-Carry Pseudorandom Number Generators", the same four that are being added to the Rust rngs project (rust-random/rngs#125):

  • mwc192x: 64-bit outputs, 192 bits of state. Recommended for all purposes.
  • mwc128x: 64-bit outputs, 128 bits of state.
  • mwc128x32: 32-bit outputs, 128 bits of state. Recommended when 32-bit outputs are needed.
  • mwc64x32: 32-bit outputs, 64 bits of state, for environments in which registers are scarce.

These generators are really fast. They pass all tests I'm aware of (in particular, PractRand up to 32TB). The code is translated from the reference C implementations at https://github.com/vigna/MWCPRNG, and has the same speed.

AMD EPYC 9R45 (Zen 5, 4.5 GHz), reference C code, ns per 64 bits (details)

Generator GCC 16.2 Clang 23.1
MWC192X 0.52 0.51
xoshiro256++ 0.58 0.58
xoroshiro128++ 0.66 0.69
MWC128X 0.94 0.95
MT19937-64 1.39 1.14

NVIDIA H100, ps per 32 bits for the whole GPU (details)

Generator ps
MWC64X/32 0.23
MWC128X/32 0.24
xoshiro128+ 0.37
xoshiro128++ 0.44
cuRAND XORWOW 0.36
cuRAND Philox4x32-10 0.81

Notes:

  • 128-bit product. detail/mwcx_arithmetic.hpp uses unsigned __int128, _umul128 (MSVC x64), __umulh (MSVC ARM64), or a portable 32-bit schoolbook multiplication (selected automatically on 32-bit targets; BOOST_RANDOM_MWCX_PORTABLE_MUL forces it).
  • Jumps. discard(z) takes logarithmic time, and jump(c, e) advances by c · 2^e steps, for distances beyond 2^64 (e.g., mwc192x::jump(1, 96)), so non-overlapping subsequences for parallel computations are easy to obtain. The arithmetic needs no wide multiplication.
  • Seeding. An integer seed sets the carry to one and fills the digits with SplitMix, as suggested by the reference code; a seed sequence sets the whole state (an invalid state gets carry one).
  • Tests. Validation values, discard() and jump() are checked against the reference C code, also with the portable multiplication, with GCC 15.2 and Clang 21.1, C++11 and C++20, 64-bit and -m32. I could not test the MSVC paths.
  • The speed column of the generator table is left empty: the existing figures could not be reproduced consistently on a single machine.

@codecov

codecov Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.11%. Comparing base (459f9c2) to head (350dfaa).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #171      +/-   ##
===========================================
+ Coverage    95.80%   96.11%   +0.30%     
===========================================
  Files          118      127       +9     
  Lines         7653     8261     +608     
===========================================
+ Hits          7332     7940     +608     
  Misses         321      321              
Files with missing lines Coverage Δ
include/boost/random/detail/mwcx_arithmetic.hpp 100.00% <100.00%> (ø)
include/boost/random/detail/mwcx_base.hpp 100.00% <100.00%> (ø)
include/boost/random/mwcx.hpp 100.00% <100.00%> (ø)
test/test_comp_mwc128x.cpp 100.00% <100.00%> (ø)
test/test_comp_mwc128x32.cpp 100.00% <100.00%> (ø)
test/test_comp_mwc192x.cpp 100.00% <100.00%> (ø)
test/test_comp_mwc64x32.cpp 100.00% <100.00%> (ø)
test/test_comp_mwcx.hpp 100.00% <100.00%> (ø)
test/test_mwcx_mul_add.cpp 100.00% <100.00%> (ø)

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 459f9c2...350dfaa. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mborland mborland left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for the contribution! This is a cool addition to the library. Once this cycles green I will merge it.

@vigna

vigna commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the heads up! One question: I found the speed table very confusing initially, as it times a call, but different generators generate a different amount of random bits. Wouldn't it be more intuitive to report the same data, but using cycles/byte? In that way, one has a fair comparison, and it would be meaningful even if, say, you're filling an array.

@mborland

mborland commented Nov 2, 2026

Copy link
Copy Markdown
Member

Thanks for the heads up! One question: I found the speed table very confusing initially, as it times a call, but different generators generate a different amount of random bits. Wouldn't it be more intuitive to report the same data, but using cycles/byte? In that way, one has a fair comparison, and it would be meaningful even if, say, you're filling an array.

I agree the table could be improved, and I think cycles/byte would be a good way to do so.

@mborland
mborland merged commit 0fa1dd5 into boostorg:develop Nov 2, 2026
64 checks passed
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.

2 participants