[libcxx-commits] [libcxx] [libc++] Avoid a guard variable for the random_shuffle engine (PR #234815)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Fri Oct 23 06:47:34 PDT 2026


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/234815

>From 025133e4ef5d9e1df4cab11f5a69bccc9ff53148 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 22 Oct 2026 14:47:10 -0400
Subject: [PATCH 1/2] [libc++] Avoid a guard variable for the random_shuffle
 engine

The default engine used by std::random_shuffle currently uses a function
local static. However, it turns out the thread safe guard variable is
unnecessary since we're already under a lock in this case.

It would be even better to constant initialize mt19937 instead, however
that's a more difficult change since the standard doesn't define it as
constexpr.
---
 libcxx/src/random_shuffle.cpp | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/libcxx/src/random_shuffle.cpp b/libcxx/src/random_shuffle.cpp
index aa0f78fafe06e0..6f5627ecf758a1 100644
--- a/libcxx/src/random_shuffle.cpp
+++ b/libcxx/src/random_shuffle.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include <__utility/no_destroy.h>
 #include <algorithm>
 #include <random>
 
@@ -42,9 +43,18 @@ __rs_default::~__rs_default() {
 #endif
 }
 
+// The engine is only accessed through a __rs_default, whose construction acquires the mutex (held until
+// the last copy is destroyed), so accesses are already serialized. This allows initializing the engine
+// lazily without a function-local static, which requires a guard variable.
+static constinit __no_destroy<mt19937> __global_mersenne_twister(__uninitialized_tag{});
+static constinit bool __initialized = false;
+
 __rs_default::result_type __rs_default::operator()() {
-  static mt19937 __rs_g;
-  return __rs_g();
+  if (!__initialized) {
+    __global_mersenne_twister.__emplace();
+    __initialized = true;
+  }
+  return __global_mersenne_twister.__get()();
 }
 
 __rs_default __rs_get() { return __rs_default(); }

>From 8939528df71e7bda00c0dbfb466abe772e337cea Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 23 Oct 2026 09:47:22 -0400
Subject: [PATCH 2/2] Simplify by using constinit

---
 .../__random/mersenne_twister_engine.h        | 27 ++++++++++++++-----
 libcxx/src/random_shuffle.cpp                 | 17 +++---------
 2 files changed, 23 insertions(+), 21 deletions(-)

diff --git a/libcxx/include/__random/mersenne_twister_engine.h b/libcxx/include/__random/mersenne_twister_engine.h
index 8b940346bcd902..65a4305d088883 100644
--- a/libcxx/include/__random/mersenne_twister_engine.h
+++ b/libcxx/include/__random/mersenne_twister_engine.h
@@ -102,6 +102,15 @@ _LIBCPP_HIDE_FROM_ABI basic_istream<_CharT, _Traits>&
 operator>>(basic_istream<_CharT, _Traits>& __is,
            mersenne_twister_engine<_UInt, _Wp, _Np, _Mp, _Rp, _Ap, _Up, _Dp, _Sp, _Bp, _Tp, _Cp, _Lp, _Fp>& __x);
 
+// When building the library, we mark some constructors and functions of mersenne_twister as constexpr
+// to allow constant initialization. This will no longer be necessary once this is constexpr in the
+// standard.
+#ifdef _LIBCPP_BUILDING_LIBRARY
+#  define _LIBCPP_MERSENNE_TWISTER_CONSTEXPR constexpr
+#else
+#  define _LIBCPP_MERSENNE_TWISTER_CONSTEXPR
+#endif
+
 template <class _UIntType,
           size_t __w,
           size_t __n,
@@ -167,8 +176,11 @@ class mersenne_twister_engine {
 
   // constructors and seeding functions
 #ifndef _LIBCPP_CXX03_LANG
-  _LIBCPP_HIDE_FROM_ABI mersenne_twister_engine() : mersenne_twister_engine(default_seed) {}
-  _LIBCPP_HIDE_FROM_ABI explicit mersenne_twister_engine(result_type __sd) { seed(__sd); }
+  _LIBCPP_MERSENNE_TWISTER_CONSTEXPR _LIBCPP_HIDE_FROM_ABI mersenne_twister_engine()
+      : mersenne_twister_engine(default_seed) {}
+  _LIBCPP_MERSENNE_TWISTER_CONSTEXPR _LIBCPP_HIDE_FROM_ABI explicit mersenne_twister_engine(result_type __sd) {
+    seed(__sd);
+  }
 #else
   _LIBCPP_HIDE_FROM_ABI explicit mersenne_twister_engine(result_type __sd = default_seed) { seed(__sd); }
 #endif
@@ -176,7 +188,8 @@ class mersenne_twister_engine {
   _LIBCPP_HIDE_FROM_ABI explicit mersenne_twister_engine(_Sseq& __q) {
     seed(__q);
   }
-  _LIBCPP_HIDE_FROM_ABI void seed(result_type __sd = default_seed) _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK {
+  _LIBCPP_MERSENNE_TWISTER_CONSTEXPR _LIBCPP_HIDE_FROM_ABI void
+  seed(result_type __sd = default_seed) _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK {
     __x_[0] = __sd & _Max;
     for (size_t __i = 1; __i < __n; ++__i)
       __x_[__i] = (__f * (__x_[__i - 1] ^ __rshift<__w - 2>(__x_[__i - 1])) + __i) & _Max;
@@ -209,16 +222,16 @@ class mersenne_twister_engine {
     __update_all_states();
   }
 
-  void __update_state(size_t __i, size_t __k) {
+  _LIBCPP_MERSENNE_TWISTER_CONSTEXPR void __update_state(size_t __i, size_t __k) {
     const size_t __j         = (__i + 1) % __n;
     const result_type __mask = __r == _Dt ? result_type(~0) : (result_type(1) << __r) - result_type(1);
     const result_type __yp   = (__x_[__i] & ~__mask) | (__x_[__j] & __mask);
     __x_[__i]                = __x_[__k] ^ __rshift<1>(__yp) ^ (__a * (__yp & 1));
   }
 
-  void __update_state(size_t __i) { __update_state(__i, (__i + __m) % __n); }
+  _LIBCPP_MERSENNE_TWISTER_CONSTEXPR void __update_state(size_t __i) { __update_state(__i, (__i + __m) % __n); }
 
-  void __update_all_states() {
+  _LIBCPP_MERSENNE_TWISTER_CONSTEXPR void __update_all_states() {
     size_t __i = 0;
     // This is split into two loops to help the compiler vectorize the code
     for (; __i != (__n - __m); ++__i)
@@ -314,7 +327,7 @@ class mersenne_twister_engine {
   }
 
   template <size_t __count>
-  _LIBCPP_HIDE_FROM_ABI static result_type __rshift(result_type __x) {
+  _LIBCPP_MERSENNE_TWISTER_CONSTEXPR _LIBCPP_HIDE_FROM_ABI static result_type __rshift(result_type __x) {
     if _LIBCPP_CONSTEXPR (__count < _Dt) {
       return __x >> __count;
     } else {
diff --git a/libcxx/src/random_shuffle.cpp b/libcxx/src/random_shuffle.cpp
index 6f5627ecf758a1..2084ffb77df11f 100644
--- a/libcxx/src/random_shuffle.cpp
+++ b/libcxx/src/random_shuffle.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include <__utility/no_destroy.h>
 #include <algorithm>
 #include <random>
 
@@ -43,19 +42,9 @@ __rs_default::~__rs_default() {
 #endif
 }
 
-// The engine is only accessed through a __rs_default, whose construction acquires the mutex (held until
-// the last copy is destroyed), so accesses are already serialized. This allows initializing the engine
-// lazily without a function-local static, which requires a guard variable.
-static constinit __no_destroy<mt19937> __global_mersenne_twister(__uninitialized_tag{});
-static constinit bool __initialized = false;
-
-__rs_default::result_type __rs_default::operator()() {
-  if (!__initialized) {
-    __global_mersenne_twister.__emplace();
-    __initialized = true;
-  }
-  return __global_mersenne_twister.__get()();
-}
+static constinit mt19937 __global_mersenne_twister;
+
+__rs_default::result_type __rs_default::operator()() { return __global_mersenne_twister(); }
 
 __rs_default __rs_get() { return __rs_default(); }
 



More information about the libcxx-commits mailing list