TraceMaker
C++ API of the engine
Loading...
Searching...
No Matches
rng.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2#pragma once
3// Deterministic, counter-based random numbers (design doc 02 ยง6.2).
4//
5// Every random decision is a pure function of (job seed, stage, item, counter), so results never depend
6// on thread timing, and CPU reference code and CUDA kernels produce identical streams.
7//
8// Philox4x32-10: Salmon, Moraes, Dror, Shaw, "Parallel random numbers: as easy as 1, 2, 3", SC 2011.
9// SplitMix64: Steele, Lea, Flood, "Fast splittable pseudorandom number generators", OOPSLA 2014.
10#include <cstdint>
11
12#include "core/hd.hpp"
13
14namespace tmk {
15
16struct U32x4 {
17 std::uint32_t v[4];
18 TM_HD friend constexpr bool operator==(const U32x4& a, const U32x4& b) {
19 return a.v[0] == b.v[0] && a.v[1] == b.v[1] && a.v[2] == b.v[2] && a.v[3] == b.v[3];
20 }
21};
22
23struct U32x2 {
24 std::uint32_t v[2];
25};
26
27namespace detail {
28TM_HD_INLINE constexpr void mul_hi_lo(std::uint32_t a, std::uint32_t b, std::uint32_t& hi, std::uint32_t& lo) {
29 const std::uint64_t p = static_cast<std::uint64_t>(a) * static_cast<std::uint64_t>(b);
30 hi = static_cast<std::uint32_t>(p >> 32);
31 lo = static_cast<std::uint32_t>(p);
32}
33} // namespace detail
34
35// One Philox4x32-10 block: 128 random bits for a 128-bit counter under a 64-bit key.
37 constexpr std::uint32_t kM0 = 0xD2511F53u, kM1 = 0xCD9E8D57u;
38 constexpr std::uint32_t kW0 = 0x9E3779B9u, kW1 = 0xBB67AE85u;
39 for (int round = 0; round < 10; ++round) {
40 if (round > 0) {
41 key.v[0] += kW0;
42 key.v[1] += kW1;
43 }
44 std::uint32_t hi0 = 0, lo0 = 0, hi1 = 0, lo1 = 0;
45 detail::mul_hi_lo(kM0, ctr.v[0], hi0, lo0);
46 detail::mul_hi_lo(kM1, ctr.v[2], hi1, lo1);
47 ctr = U32x4{{hi1 ^ ctr.v[1] ^ key.v[0], lo1, hi0 ^ ctr.v[3] ^ key.v[1], lo0}};
48 }
49 return ctr;
50}
51
52// SplitMix64 finaliser: a fast, well-mixed 64-bit hash, used to derive keys and for hashing ids.
53TM_HD_INLINE constexpr std::uint64_t splitmix64(std::uint64_t x) {
54 x += 0x9E3779B97F4A7C15ull;
55 x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ull;
56 x = (x ^ (x >> 27)) * 0x94D049BB133111EBull;
57 return x ^ (x >> 31);
58}
59
60// A random stream identified by (seed, stage, item). Draw number `n` is a pure function of the stream
61// and `n`, so any thread or GPU lane can jump to any draw without coordination.
62class RngStream {
63 public:
64 TM_HD constexpr RngStream(std::uint64_t seed, std::uint32_t stage, std::uint64_t item)
65 : key_{static_cast<std::uint32_t>(splitmix64(seed ^ (static_cast<std::uint64_t>(stage) << 32))),
66 static_cast<std::uint32_t>(splitmix64(seed + stage) >> 32)},
67 item_(item) {}
68
69 // Four 32-bit values for block `n` of this stream.
70 TM_HD constexpr U32x4 block(std::uint64_t n) const {
71 const U32x4 ctr{{static_cast<std::uint32_t>(n), static_cast<std::uint32_t>(n >> 32),
72 static_cast<std::uint32_t>(item_), static_cast<std::uint32_t>(item_ >> 32)}};
73 return philox4x32_10(ctr, U32x2{{key_[0], key_[1]}});
74 }
75
76 // Draw `n` as a 64-bit value.
77 TM_HD constexpr std::uint64_t u64(std::uint64_t n) const {
78 const U32x4 b = block(n);
79 return (static_cast<std::uint64_t>(b.v[0]) << 32) | b.v[1];
80 }
81
82 // Draw `n` as a double in [0, 1) with 53 random bits.
83 TM_HD constexpr double uniform(std::uint64_t n) const {
84 return static_cast<double>(u64(n) >> 11) * 0x1.0p-53;
85 }
86
87 private:
88 std::uint32_t key_[2];
89 std::uint64_t item_;
90};
91
92} // namespace tmk
TM_HD constexpr U32x4 block(std::uint64_t n) const
Four 32-bit values for block n of this stream.
Definition rng.hpp:70
TM_HD constexpr std::uint64_t u64(std::uint64_t n) const
Draw n as a 64-bit value.
Definition rng.hpp:77
TM_HD constexpr double uniform(std::uint64_t n) const
Draw n as a double in [0, 1) with 53 random bits.
Definition rng.hpp:83
TM_HD constexpr RngStream(std::uint64_t seed, std::uint32_t stage, std::uint64_t item)
Definition rng.hpp:64
TM_HD marks functions that must compile identically for host and device.
#define TM_HD
Definition hd.hpp:9
#define TM_HD_INLINE
Definition hd.hpp:10
TM_HD_INLINE constexpr void mul_hi_lo(std::uint32_t a, std::uint32_t b, std::uint32_t &hi, std::uint32_t &lo)
Definition rng.hpp:28
Definition advice.hpp:44
TM_HD_INLINE constexpr U32x4 philox4x32_10(U32x4 ctr, U32x2 key)
One Philox4x32-10 block: 128 random bits for a 128-bit counter under a 64-bit key.
Definition rng.hpp:36
TM_HD_INLINE constexpr std::uint64_t splitmix64(std::uint64_t x)
SplitMix64 finaliser: a fast, well-mixed 64-bit hash, used to derive keys and for hashing ids.
Definition rng.hpp:53
std::uint32_t v[2]
Definition rng.hpp:24
std::uint32_t v[4]
Definition rng.hpp:17
TM_HD friend constexpr bool operator==(const U32x4 &a, const U32x4 &b)
Definition rng.hpp:18