summaryrefslogtreecommitdiff
path: root/libc/benchmarks/gpu/Random.h
diff options
context:
space:
mode:
Diffstat (limited to 'libc/benchmarks/gpu/Random.h')
-rw-r--r--libc/benchmarks/gpu/Random.h190
1 files changed, 190 insertions, 0 deletions
diff --git a/libc/benchmarks/gpu/Random.h b/libc/benchmarks/gpu/Random.h
new file mode 100644
index 000000000000..f7d272289a6d
--- /dev/null
+++ b/libc/benchmarks/gpu/Random.h
@@ -0,0 +1,190 @@
+//===-- Pseudo-random number generation utilities ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_BENCHMARKS_GPU_RANDOM_H
+#define LLVM_LIBC_BENCHMARKS_GPU_RANDOM_H
+
+#include "hdr/stdint_proxy.h"
+#include "src/__support/CPP/algorithm.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+#include "src/__support/sign.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace benchmarks {
+
+// Pseudo-random number generator (PRNG) that produces unsigned 64-bit, 32-bit,
+// and 16-bit integers. The implementation is based on the xorshift* generator,
+// seeded using SplitMix64 for robust initialization. For more details, see:
+// https://en.wikipedia.org/wiki/Xorshift
+class RandomGenerator {
+ uint64_t state;
+
+ static LIBC_INLINE uint64_t splitmix64(uint64_t x) noexcept {
+ x += 0x9E3779B97F4A7C15ULL;
+ x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL;
+ x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL;
+ x = (x ^ (x >> 31));
+ return x ? x : 0x9E3779B97F4A7C15ULL;
+ }
+
+public:
+ explicit LIBC_INLINE RandomGenerator(uint64_t seed) noexcept
+ : state(splitmix64(seed)) {}
+
+ LIBC_INLINE uint64_t next64() noexcept {
+ uint64_t x = state;
+ x ^= x >> 12;
+ x ^= x << 25;
+ x ^= x >> 27;
+ state = x;
+ return x * 0x2545F4914F6CDD1DULL;
+ }
+
+ LIBC_INLINE uint32_t next32() noexcept {
+ return static_cast<uint32_t>(next64() >> 32);
+ }
+
+ LIBC_INLINE uint16_t next16() noexcept {
+ return static_cast<uint16_t>(next64() >> 48);
+ }
+};
+
+// Generates random floating-point numbers where the unbiased binary exponent
+// is sampled uniformly in `[min_exp, max_exp]`. The significand bits are
+// always randomized, while the sign is randomized by default but can be fixed.
+// Evenly covers orders of magnitude; never yields Inf/NaN.
+template <typename T> class UniformExponent {
+ static_assert(cpp::is_same_v<T, float16> || cpp::is_same_v<T, float> ||
+ cpp::is_same_v<T, double>,
+ "UniformExponent supports float16, float, and double");
+
+ using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
+ using Storage = typename FPBits::StorageType;
+
+public:
+ explicit UniformExponent(int min_exp = -FPBits::EXP_BIAS,
+ int max_exp = FPBits::EXP_BIAS,
+ cpp::optional<Sign> forced_sign = cpp::nullopt)
+ : min_exp(clamp_exponent(cpp::min(min_exp, max_exp))),
+ max_exp(clamp_exponent(cpp::max(min_exp, max_exp))),
+ forced_sign(forced_sign) {}
+
+ LIBC_INLINE T operator()(RandomGenerator &rng) const noexcept {
+ // Sample unbiased exponent e uniformly in [min_exp, max_exp] without modulo
+ // bias, using rejection sampling
+ auto sample_in_range = [&](uint64_t r) -> int32_t {
+ const uint64_t range = static_cast<uint64_t>(
+ static_cast<int64_t>(max_exp) - static_cast<int64_t>(min_exp) + 1);
+ const uint64_t threshold = (-range) % range;
+ while (r < threshold)
+ r = rng.next64();
+ return static_cast<int32_t>(min_exp + static_cast<int64_t>(r % range));
+ };
+ const int32_t e = sample_in_range(rng.next64());
+
+ // Start from random bits to get random sign and mantissa
+ FPBits xbits([&] {
+ if constexpr (cpp::is_same_v<T, double>)
+ return FPBits(rng.next64());
+ else if constexpr (cpp::is_same_v<T, float>)
+ return FPBits(rng.next32());
+ else
+ return FPBits(rng.next16());
+ }());
+
+ if (e == -FPBits::EXP_BIAS) {
+ // Subnormal: biased exponent must be 0; ensure mantissa != 0 to avoid 0
+ xbits.set_biased_exponent(Storage(0));
+ if (xbits.get_mantissa() == Storage(0))
+ xbits.set_mantissa(Storage(1));
+ } else {
+ // Normal: biased exponent in [1, 2 * FPBits::EXP_BIAS]
+ const int32_t biased = e + FPBits::EXP_BIAS;
+ xbits.set_biased_exponent(static_cast<Storage>(biased));
+ }
+
+ if (forced_sign)
+ xbits.set_sign(*forced_sign);
+
+ return xbits.get_val();
+ }
+
+private:
+ static LIBC_INLINE int clamp_exponent(int val) noexcept {
+ if (val < -FPBits::EXP_BIAS)
+ return -FPBits::EXP_BIAS;
+
+ if (val > FPBits::EXP_BIAS)
+ return FPBits::EXP_BIAS;
+
+ return val;
+ }
+
+ const int min_exp;
+ const int max_exp;
+ const cpp::optional<Sign> forced_sign;
+};
+
+// Generates random floating-point numbers that are uniformly distributed on
+// a linear scale. Values are sampled from `[min_val, max_val)`.
+template <typename T> class UniformLinear {
+ static_assert(cpp::is_same_v<T, float16> || cpp::is_same_v<T, float> ||
+ cpp::is_same_v<T, double>,
+ "UniformLinear supports float16, float, and double");
+
+ using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
+ using Storage = typename FPBits::StorageType;
+
+ static constexpr T MAX_NORMAL = FPBits::max_normal().get_val();
+
+public:
+ explicit UniformLinear(T min_val = -MAX_NORMAL, T max_val = MAX_NORMAL)
+ : min_val(clamp_val(cpp::min(min_val, max_val))),
+ max_val(clamp_val(cpp::max(min_val, max_val))) {}
+
+ LIBC_INLINE T operator()(RandomGenerator &rng) const noexcept {
+ double u = standard_uniform(rng.next64());
+ double a = static_cast<double>(min_val);
+ double b = static_cast<double>(max_val);
+ double y = a + (b - a) * u;
+ return static_cast<T>(y);
+ }
+
+private:
+ static LIBC_INLINE T clamp_val(T val) noexcept {
+ if (val < -MAX_NORMAL)
+ return -MAX_NORMAL;
+
+ if (val > MAX_NORMAL)
+ return MAX_NORMAL;
+
+ return val;
+ }
+
+ static LIBC_INLINE double standard_uniform(uint64_t x) noexcept {
+ constexpr int PREC_BITS =
+ LIBC_NAMESPACE::fputil::FPBits<double>::SIG_LEN + 1;
+ constexpr int SHIFT_BITS = LIBC_NAMESPACE::fputil::FPBits<double>::EXP_LEN;
+ constexpr double INV = 1.0 / static_cast<double>(1ULL << PREC_BITS);
+
+ return static_cast<double>(x >> SHIFT_BITS) * INV;
+ }
+
+ const T min_val;
+ const T max_val;
+};
+
+} // namespace benchmarks
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif