From 0156b8d5dc35b008b4f10ee45161c691335502a7 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 30 Jun 2022 19:21:51 -0700 Subject: [PATCH 1/8] Lemire's fast integer generation --- stl/inc/__msvc_int128.hpp | 166 ++++++++++-------- stl/inc/random | 6 +- stl/inc/xutility | 116 ++++++++++++ tests/std/test.lst | 1 + tests/std/tests/GH_000178_uniform_int/env.lst | 4 + .../std/tests/GH_000178_uniform_int/test.cpp | 80 +++++++++ 6 files changed, 300 insertions(+), 73 deletions(-) create mode 100644 tests/std/tests/GH_000178_uniform_int/env.lst create mode 100644 tests/std/tests/GH_000178_uniform_int/test.cpp diff --git a/stl/inc/__msvc_int128.hpp b/stl/inc/__msvc_int128.hpp index e26d7ead234..d7129fc6da6 100644 --- a/stl/inc/__msvc_int128.hpp +++ b/stl/inc/__msvc_int128.hpp @@ -9,12 +9,22 @@ #include #if _STL_COMPILER_PREPROCESSOR -#ifdef __cpp_lib_concepts +#include +#include // TRANSITION, GH-2520 +#include +#include + +#if _HAS_CXX20 #include #include +#endif // _HAS_CXX20 + +#ifdef __cpp_lib_concepts #include -#include -#include // TRANSITION, GH-2520 +#define _TEMPLATE_CLASS_INTEGRAL(type) template +#else +#define _TEMPLATE_CLASS_INTEGRAL(type) template , int> = 0> +#endif // __cpp_lib_concepts #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -57,7 +67,7 @@ struct } #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { _Word[1] = __shiftleft128(_Word[0], _Word[1], _Count); } else #endif // _STL_128_INTRINSICS @@ -81,7 +91,7 @@ struct } #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { _Word[0] = __shiftright128(_Word[0], _Word[1], _Count); } else #endif // _STL_128_INTRINSICS @@ -96,7 +106,7 @@ struct unsigned char _Carry, uint64_t _Left, uint64_t _Right, uint64_t& _Result) noexcept { // _STL_INTERNAL_CHECK(_Carry < 2); #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { return _addcarry_u64(_Carry, _Left, _Right, &_Result); } #endif // _STL_128_INTRINSICS @@ -110,7 +120,7 @@ struct unsigned char _Carry, uint64_t _Left, uint64_t _Right, uint64_t& _Result) noexcept { // _STL_INTERNAL_CHECK(_Carry < 2); #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { return _subborrow_u64(_Carry, _Left, _Right, &_Result); } #endif // _STL_128_INTRINSICS @@ -148,7 +158,7 @@ struct _NODISCARD static constexpr uint64_t _UMul128( const uint64_t _Left, const uint64_t _Right, uint64_t& _High_result) noexcept { #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { return _umul128(_Left, _Right, &_High_result); } #endif // _STL_128_INTRINSICS @@ -161,7 +171,7 @@ struct static_cast(_Right), static_cast(_Right >> 32), }; - uint32_t __w[4]; + uint32_t __w[4] = {0}; // multiply 2-digit numbers with 4-digit result in base 2^32 _Knuth_4_3_1_M(__u, __v, __w); @@ -197,7 +207,7 @@ struct } int64_t __k = 0; - int64_t __t; + int64_t __t = 0; for (int __i = 0; __i < static_cast(__n); ++__i) { const auto _Prod = static_cast(__qhat) * static_cast(__v[__i]); __t = __u[__i + __j] - __k - static_cast(_Prod); @@ -228,12 +238,12 @@ struct // _STL_INTERNAL_CHECK(_High < _Div); #if _STL_128_DIV_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { return _udiv128(_High, _Low, _Div, &_Remainder); } #endif // _STL_128_DIV_INTRINSICS - const auto __d = _STD countl_zero(static_cast(_Div >> 32)); + const auto __d = _Countl_zero_fallback(static_cast(_Div >> 32)); if (__d >= 32) { // _Div < 2^32 auto _Rem = (_High << 32) | (_Low >> 32); auto _Result = _Rem / static_cast(_Div); @@ -259,7 +269,7 @@ struct static_cast(_Div << __d), static_cast(_Div >> (32 - __d)), }; - uint32_t __q[3]; + uint32_t __q[3] = {0}; _Knuth_4_3_1_D(__u, 5, __v, 2, __q); // _STL_INTERNAL_CHECK(__u[4] == 0); @@ -273,9 +283,14 @@ struct constexpr _Base128() noexcept : _Word{} {} - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Base128(const _Ty _Val) noexcept : _Word{static_cast(_Val)} { - if constexpr (signed_integral<_Ty>) { +#ifdef __cpp_lib_concepts + if constexpr (signed_integral<_Ty>) +#else + if constexpr (is_integral_v<_Ty> && is_signed_v<_Ty>) +#endif + { if (_Val < 0) { _Word[1] = ~0ull; } @@ -284,7 +299,7 @@ struct constexpr explicit _Base128(const uint64_t _Low, const uint64_t _High) noexcept : _Word{_Low, _High} {} - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD constexpr explicit operator _Ty() const noexcept { return static_cast<_Ty>(_Word[0]); } @@ -293,7 +308,9 @@ struct return (_Word[0] | _Word[1]) != 0; } +#if _HAS_CXX20 _NODISCARD_FRIEND constexpr bool operator==(const _Base128&, const _Base128&) noexcept = default; +#endif _NODISCARD_FRIEND constexpr bool operator<(const _Base128& _Left, const _Base128& _Right) noexcept { if (_Left._Word[1] < _Right._Word[1]) { @@ -315,35 +332,35 @@ struct return !(_Left < _Right); } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD_FRIEND constexpr _Ty operator<<(const _Ty _Left, const _Base128& _Right) noexcept { return _Left << _Right._Word[0]; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD_FRIEND constexpr _Ty operator>>(const _Ty _Left, const _Base128& _Right) noexcept { return _Left >> _Right._Word[0]; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Base128& operator<<=(const _Ty _Count) noexcept { _Left_shift(static_cast(_Count)); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator<<=(_Ty& _Left, const _Base128& _Right) noexcept { _Left <<= _Right._Word[0]; return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Base128& operator>>=(const _Ty _Count) noexcept { _Unsigned_right_shift(static_cast(_Count)); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator>>=(_Ty& _Left, const _Base128& _Right) noexcept { _Left >>= _Right._Word[0]; return _Left; @@ -426,14 +443,14 @@ struct // _STL_INTERNAL_CHECK(_Den._Word[1] != 0); // _STL_INTERNAL_CHECK(_Num._Word[1] > _Den._Word[1]); // Normalize by shifting both left until _Den's high bit is set (So _Den's high digit is >= b / 2) - const auto __d = _STD countl_zero(_Den._Word[1]); + const auto __d = _Countl_zero_fallback(_Den._Word[1]); _Den <<= __d; auto _High_digit = __d == 0 ? 0 : _Num._Word[1] >> (64 - __d); // This creates a third digit for _Num _Num <<= __d; _Base128 __qhat; __qhat._Word[1] = _High_digit >= _Den._Word[1]; - uint64_t __rhat; + uint64_t __rhat = 0; __qhat._Word[0] = _UDiv128(_High_digit >= _Den._Word[1] ? _High_digit - _Den._Word[1] : _High_digit, _Num._Word[1], _Den._Word[1], __rhat); @@ -458,11 +475,11 @@ struct // _STL_INTERNAL_CHECK(__qhat._Word[1] == 0); // [_High_digit | _Num] -= __qhat * _Den [Since __qhat < b, this is 3-digit - 1-digit * 2-digit] - uint64_t _Prod0_hi; - uint64_t _Prod_lo = _UMul128(__qhat._Word[0], _Den._Word[0], _Prod0_hi); - auto _Borrow = _SubBorrow64(0, _Num._Word[0], _Prod_lo, _Num._Word[0]); - uint64_t _Prod1_hi; - _Prod_lo = _UMul128(__qhat._Word[0], _Den._Word[1], _Prod1_hi); + uint64_t _Prod0_hi = 0; + uint64_t _Prod_lo = _UMul128(__qhat._Word[0], _Den._Word[0], _Prod0_hi); + auto _Borrow = _SubBorrow64(0, _Num._Word[0], _Prod_lo, _Num._Word[0]); + uint64_t _Prod1_hi = 0; + _Prod_lo = _UMul128(__qhat._Word[0], _Den._Word[1], _Prod1_hi); _Prod1_hi += _AddCarry64(0, _Prod_lo, _Prod0_hi, _Prod_lo); _Borrow = _SubBorrow64(_Borrow, _Num._Word[1], _Prod_lo, _Num._Word[1]); _Borrow = _SubBorrow64(_Borrow, _High_digit, _Prod1_hi, _High_digit); @@ -471,7 +488,7 @@ struct } return __qhat; #else // ^^^ 128-bit intrinsics / no such intrinsics vvv - auto __d = _STD countl_zero(_Den._Word[1]); + auto __d = _Countl_zero_fallback(_Den._Word[1]); const bool _Three_word_den = __d >= 32; __d &= 31; uint32_t __u[5]{ @@ -516,7 +533,7 @@ struct } #endif // !_STL_128_DIV_INTRINSICS _NODISCARD static constexpr _Base128 _Modulo(const _Base128& _Num, const uint64_t _Den) noexcept { - uint64_t _Rem; + uint64_t _Rem = 0; (void) _UDiv128(_Num._Word[1] % _Den, _Num._Word[0], _Den, _Rem); return _Rem; } @@ -551,14 +568,14 @@ struct // _STL_INTERNAL_CHECK(_Den._Word[1] != 0); // _STL_INTERNAL_CHECK(_Num._Word[1] > _Den._Word[1]); // Normalize by shifting both left until _Den's high bit is set (So _Den's high digit is >= b / 2) - const auto __d = _STD countl_zero(_Den._Word[1]); + const auto __d = _Countl_zero_fallback(_Den._Word[1]); _Den <<= __d; auto _High_digit = __d == 0 ? 0 : _Num._Word[1] >> (64 - __d); // This creates a third digit for _Num _Num <<= __d; uint64_t __qhat_high = _High_digit >= _Den._Word[1]; - uint64_t __rhat; - uint64_t __qhat = _UDiv128(_High_digit >= _Den._Word[1] ? _High_digit - _Den._Word[1] : _High_digit, + uint64_t __rhat = 0; + uint64_t __qhat = _UDiv128(_High_digit >= _Den._Word[1] ? _High_digit - _Den._Word[1] : _High_digit, _Num._Word[1], _Den._Word[1], __rhat); for (;;) { @@ -585,11 +602,11 @@ struct // _STL_INTERNAL_CHECK(__qhat_high == 0); // [_High_digit | _Num] -= __qhat * _Den [3-digit - 1-digit * 2-digit] - uint64_t _Prod0_hi; - uint64_t _Prod_lo = _UMul128(__qhat, _Den._Word[0], _Prod0_hi); - auto _Borrow = _SubBorrow64(0, _Num._Word[0], _Prod_lo, _Num._Word[0]); - uint64_t _Prod1_hi; - _Prod_lo = _UMul128(__qhat, _Den._Word[1], _Prod1_hi); + uint64_t _Prod0_hi = 0; + uint64_t _Prod_lo = _UMul128(__qhat, _Den._Word[0], _Prod0_hi); + auto _Borrow = _SubBorrow64(0, _Num._Word[0], _Prod_lo, _Num._Word[0]); + uint64_t _Prod1_hi = 0; + _Prod_lo = _UMul128(__qhat, _Den._Word[1], _Prod1_hi); _Prod1_hi += _AddCarry64(0, _Prod_lo, _Prod0_hi, _Prod_lo); _Borrow = _SubBorrow64(_Borrow, _Num._Word[1], _Prod_lo, _Num._Word[1]); _Borrow = _SubBorrow64(_Borrow, _High_digit, _Prod1_hi, _High_digit); @@ -598,7 +615,7 @@ struct (void) _AddCarry64(_Carry, _Num._Word[1], _Den._Word[1], _Num._Word[1]); } #else // ^^^ 128-bit intrinsics / no such intrinsics vvv - auto __d = _STD countl_zero(_Den._Word[1]); + auto __d = _Countl_zero_fallback(_Den._Word[1]); const bool _Three_word_den = __d >= 32; __d &= 31; uint32_t __u[5]{ @@ -638,19 +655,19 @@ struct return _Num; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator&=(_Ty& _Left, const _Base128& _Right) noexcept { _Left &= _Right._Word[0]; return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator^=(_Ty& _Left, const _Base128& _Right) noexcept { _Left ^= _Right._Word[0]; return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator|=(_Ty& _Left, const _Base128& _Right) noexcept { _Left |= _Right._Word[0]; return _Left; @@ -663,6 +680,8 @@ struct _Unsigned128 : _Base128 { using _Signed_type = _Signed128; using _Unsigned_type = _Unsigned128; + constexpr _Unsigned128() : _Base128() {} + using _Base128::_Base128; constexpr explicit _Unsigned128(const _Base128& _That) noexcept : _Base128{_That} {} @@ -671,6 +690,7 @@ struct _Unsigned128 : _Base128 { return *this; } +#ifdef __cpp_lib_three_way_comparison _NODISCARD_FRIEND constexpr strong_ordering operator<=>( const _Unsigned128& _Left, const _Unsigned128& _Right) noexcept { strong_ordering _Ord = _Left._Word[1] <=> _Right._Word[1]; @@ -679,6 +699,7 @@ struct _Unsigned128 : _Base128 { } return _Ord; } +#endif _NODISCARD_FRIEND constexpr _Unsigned128 operator<<(const _Unsigned128& _Left, const _Base128& _Right) noexcept { auto _Tmp{_Left}; @@ -686,7 +707,7 @@ struct _Unsigned128 : _Base128 { return _Tmp; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Unsigned128& operator<<=(const _Ty _Count) noexcept { _Left_shift(static_cast(_Count)); return *this; @@ -702,7 +723,7 @@ struct _Unsigned128 : _Base128 { return _Tmp; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Unsigned128& operator>>=(const _Ty _Count) noexcept { _Unsigned_right_shift(static_cast(_Count)); return *this; @@ -760,7 +781,7 @@ struct _Unsigned128 : _Base128 { _AddCarry64(_Carry, _Word[1], _That._Word[1], _Word[1]); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator+=(_Ty& _Left, const _Unsigned128& _Right) noexcept { _Left += _Right._Word[0]; return _Left; @@ -778,7 +799,7 @@ struct _Unsigned128 : _Base128 { _SubBorrow64(_Borrow, _Word[1], _That._Word[1], _Word[1]); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator-=(_Ty& _Left, const _Unsigned128& _Right) noexcept { _Left -= _Right._Word[0]; return _Left; @@ -792,13 +813,13 @@ struct _Unsigned128 : _Base128 { *this = *this * _That; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator*=(_Ty& _Left, const _Unsigned128& _Right) noexcept { _Left *= _Right._Word[0]; return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD_FRIEND constexpr _Unsigned128 operator/(const _Unsigned128& _Num, const _Ty _Den) noexcept { #if !_STL_128_DIV_INTRINSICS if constexpr (sizeof(_Ty) <= 4) { @@ -813,7 +834,7 @@ struct _Unsigned128 : _Base128 { return _Unsigned128{_Base128::_Divide(_Num, _Den)}; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Unsigned128& operator/=(const _Ty _That) noexcept { #if !_STL_128_DIV_INTRINSICS if constexpr (sizeof(_Ty) <= 4) { @@ -829,7 +850,7 @@ struct _Unsigned128 : _Base128 { *this = _Unsigned128{_Base128::_Divide(*this, _That)}; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator/=(_Ty& _Left, const _Unsigned128& _Right) noexcept { if (_Right._Word[1] != 0) { _Left = 0; @@ -839,7 +860,7 @@ struct _Unsigned128 : _Base128 { return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD_FRIEND constexpr _Unsigned128 operator%(const _Base128& _Num, const _Ty _Den) noexcept { #if !_STL_128_DIV_INTRINSICS if constexpr (sizeof(_Ty) <= 4) { @@ -854,7 +875,7 @@ struct _Unsigned128 : _Base128 { return _Unsigned128{_Base128::_Modulo(_Num, _Den)}; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Unsigned128& operator%=(const _Ty _Den) noexcept { *this = *this % _Den; return *this; @@ -863,7 +884,7 @@ struct _Unsigned128 : _Base128 { *this = *this % _Den; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator%=(_Ty& _Left, const _Unsigned128& _Right) noexcept { if (_Right._Word[1] == 0) { _Left %= _Right._Word[0]; @@ -946,11 +967,11 @@ class numeric_limits<_Unsigned128> : public _Num_int_base { static constexpr int digits10 = 38; }; -template +template struct common_type<_Ty, _Unsigned128> { using type = _Unsigned128; }; -template +template struct common_type<_Unsigned128, _Ty> { using type = _Unsigned128; }; @@ -959,6 +980,8 @@ struct _Signed128 : _Base128 { using _Signed_type = _Signed128; using _Unsigned_type = _Unsigned128; + constexpr _Signed128() : _Base128() {} + using _Base128::_Base128; constexpr explicit _Signed128(const _Base128& _That) noexcept : _Base128{_That} {} @@ -967,6 +990,7 @@ struct _Signed128 : _Base128 { return *this; } +#ifdef __cpp_lib_three_way_comparison _NODISCARD_FRIEND constexpr strong_ordering operator<=>( const _Signed128& _Left, const _Signed128& _Right) noexcept { strong_ordering _Ord = static_cast(_Left._Word[1]) <=> static_cast(_Right._Word[1]); @@ -975,6 +999,7 @@ struct _Signed128 : _Base128 { } return _Ord; } +#endif _NODISCARD_FRIEND constexpr _Signed128 operator<<(const _Signed128& _Left, const _Base128& _Right) noexcept { auto _Tmp{_Left}; @@ -982,7 +1007,7 @@ struct _Signed128 : _Base128 { return _Tmp; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Signed128& operator<<=(const _Ty _Count) noexcept { _Left_shift(static_cast(_Count)); return *this; @@ -1004,7 +1029,7 @@ struct _Signed128 : _Base128 { } #if _STL_128_INTRINSICS - if (!_STD is_constant_evaluated()) { + if (!_Is_constant_evaluated()) { _Word[0] = __shiftright128(_Word[0], _Word[1], _Count); } else #endif // _STL_128_INTRINSICS @@ -1021,7 +1046,7 @@ struct _Signed128 : _Base128 { return _Tmp; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Signed128& operator>>=(const _Ty _Count) noexcept { _Signed_right_shift(static_cast(_Count)); return *this; @@ -1079,7 +1104,7 @@ struct _Signed128 : _Base128 { _AddCarry64(_Carry, _Word[1], _That._Word[1], _Word[1]); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator+=(_Ty& _Left, const _Signed128& _Right) noexcept { _Left = static_cast<_Ty>(_Signed128{_Left} + _Right); return _Left; @@ -1097,7 +1122,7 @@ struct _Signed128 : _Base128 { _SubBorrow64(_Borrow, _Word[1], _That._Word[1], _Word[1]); return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator-=(_Ty& _Left, const _Signed128& _Right) noexcept { _Left = static_cast<_Ty>(_Signed128{_Left} - _Right); return _Left; @@ -1121,7 +1146,7 @@ struct _Signed128 : _Base128 { return _Result; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Signed128& operator*=(const _Ty _That) noexcept { *this = *this * _That; return *this; @@ -1134,13 +1159,13 @@ struct _Signed128 : _Base128 { *this = _Signed128{static_cast(*this) * _That}; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator*=(_Ty& _Left, const _Signed128& _Right) noexcept { _Left = static_cast<_Ty>(_Signed128{_Left} * _Right); return _Left; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) _NODISCARD_FRIEND constexpr _Signed128 operator/(_Signed128 _Num, _Ty _Den) noexcept { bool _Negative = false; _Num._Strip_negative(_Negative); @@ -1177,7 +1202,7 @@ struct _Signed128 : _Base128 { return _Result; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Signed128& operator/=(const _Ty _That) noexcept { *this = *this / _That; return *this; @@ -1190,7 +1215,7 @@ struct _Signed128 : _Base128 { *this = _Signed128{static_cast<_Base128&>(*this) / _That}; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator/=(_Ty& _Left, const _Signed128& _Right) noexcept { _Left = static_cast<_Ty>(_Signed128{_Left} / _Right); return _Left; @@ -1212,7 +1237,7 @@ struct _Signed128 : _Base128 { return _Signed128{_Result}; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) constexpr _Signed128& operator%=(const _Ty _That) noexcept { *this = *this % _That; return *this; @@ -1225,7 +1250,7 @@ struct _Signed128 : _Base128 { *this = static_cast(*this) % _That; return *this; } - template + _TEMPLATE_CLASS_INTEGRAL(_Ty) friend constexpr _Ty& operator%=(_Ty& _Left, const _Signed128& _Right) noexcept { _Left = static_cast<_Ty>(_Signed128{_Left} % _Right); return _Left; @@ -1305,11 +1330,11 @@ class numeric_limits<_Signed128> : public _Num_int_base { static constexpr int digits10 = 38; }; -template +template struct common_type<_Ty, _Signed128> { using type = _Signed128; }; -template +template struct common_type<_Signed128, _Ty> { using type = _Signed128; }; @@ -1332,6 +1357,5 @@ _STD_END _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) #pragma pack(pop) -#endif // __cpp_lib_concepts #endif // _STL_COMPILER_PREPROCESSOR #endif // __MSVC_INT128_HPP diff --git a/stl/inc/random b/stl/inc/random index 5c87135054d..b751ef30c39 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -1864,7 +1864,9 @@ private: template result_type _Eval(_Engine& _Eng, _Ty _Min, _Ty _Max) const { // compute next value in range [_Min, _Max] - _Rng_from_urng<_Uty, _Engine> _Generator(_Eng); + conditional_t<_Has_static_min_max<_Engine>::value, _Rng_from_urng_v2<_Uty, _Engine>, + _Rng_from_urng<_Uty, _Engine>> + _Generator(_Eng); const _Uty _Umin = _Adjust(static_cast<_Uty>(_Min)); const _Uty _Umax = _Adjust(static_cast<_Uty>(_Max)); @@ -1882,7 +1884,7 @@ private: static _Uty _Adjust(_Uty _Uval) { // convert signed ranges to unsigned ranges and vice versa if constexpr (is_signed_v<_Ty>) { - const _Uty _Adjuster = (static_cast<_Uty>(-1) >> 1) + 1; // 2^(N-1) + constexpr _Uty _Adjuster = (static_cast<_Uty>(-1) >> 1) + 1; // 2^(N-1) if (_Uval < _Adjuster) { return static_cast<_Uty>(_Uval + _Adjuster); diff --git a/stl/inc/xutility b/stl/inc/xutility index 30bcfb7b623..90ae90fbda0 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -9,6 +9,7 @@ #include #if _STL_COMPILER_PREPROCESSOR +#include <__msvc_int128.hpp> #include <__msvc_iter_core.hpp> #include #include @@ -5767,6 +5768,121 @@ private: _Udiff _Bmask; // 2^_Bits - 1 }; +template +class _Rng_from_urng_v2 { // wrap a URNG as an RNG +public: + using _Ty0 = make_unsigned_t<_Diff>; + using _Ty1 = typename _Urng::result_type; + + using _Udiff = conditional_t; + constexpr static unsigned int _Udiff_bits = sizeof(_Udiff) * CHAR_BIT; + using _Uprod = conditional_t<_Udiff_bits <= 16, uint32_t, conditional_t<_Udiff_bits <= 32, uint64_t, _Unsigned128>>; + + explicit _Rng_from_urng_v2(_Urng& _Func) : _Ref(_Func) {} + + _Diff operator()(_Diff _Index) { // adapt _Urng closed range to [0, _Index) + // From Daniel Lemire, "Fast Random Integer Generation in an Interval", ACM Trans. Model. Comput. Simul. 29 (1), + // 2019. + _Udiff _Mask; + unsigned int _Niter; + + if constexpr (_Bits < _Udiff_bits) { + _Mask = 0; + _Niter = 0; + do { + _Mask <<= _Bits; + _Mask |= _Bmask; + ++_Niter; + } while (_Mask < _Udiff(_Index - 1)); + } else { + _Mask = _Bmask; + _Niter = 1; + } + + auto _Product = _Get_random_product(_Index, _Niter); + auto _Rem = static_cast<_Udiff>(_Product) & _Mask; + + if (_Rem < _Index) { + const auto _Threshold = (_Mask - (_Index - 1)) % _Index; + while (_Rem < _Threshold) { + _Product = _Get_random_product(_Index, _Niter); + _Rem = static_cast<_Udiff>(_Product) & _Mask; + } + } + + unsigned int _Generated_bits; + if constexpr (_Bits < _Udiff_bits) { + _Generated_bits = static_cast(_Popcount(_Mask)); + } else { + _Generated_bits = _Udiff_bits; + } + + return static_cast<_Diff>(_Product >> _Generated_bits); + } + + _Udiff _Get_all_bits() { + _Udiff _Ret = _Get_bits(); + + if constexpr (_Bits < _Udiff_bits) { + for (unsigned int _Num = _Bits; _Num < _Udiff_bits; _Num += _Bits) { // don't mask away any bits + _Ret <<= _Bits; + _Ret |= _Get_bits(); + } + } + + return _Ret; + } + + _Rng_from_urng_v2(const _Rng_from_urng_v2&) = delete; + _Rng_from_urng_v2& operator=(const _Rng_from_urng_v2&) = delete; + +private: + _Udiff _Get_bits() { // return a random value within [0, _Bmask] + for (;;) { // repeat until random value is in range + _Udiff _Val = _Ref() - (_Urng::min)(); + + if (_Val <= _Bmask) { + return _Val; + } + } + } + + static constexpr size_t _Calc_bits() { + auto _Bits_local = _Udiff_bits; + auto _Bmask_local = _Udiff(-1); + for (; (_Urng::max)() - (_Urng::min)() < _Bmask_local; _Bmask_local >>= 1) { + --_Bits_local; + } + + return _Bits_local; + } + + _Uprod _Get_random_product(const _Diff _Index, unsigned int _Niter) { + _Udiff _Ret; + if constexpr (_Bits < _Udiff_bits) { + _Ret = 0; + do { + _Ret <<= _Bits; + _Ret |= _Get_bits(); + } while (--_Niter > 0); + } else { + _Ret = _Get_bits(); + } + + if constexpr (is_same_v<_Udiff, uint64_t>) { + uint64_t _High; + const auto _Low = _Base128::_UMul128(_Ret, static_cast<_Udiff>(_Index), _High); + return _Uprod{_Low, _High}; + } else { + return _Uprod{_Ret} * _Uprod{_Index}; + } + } + + _Urng& _Ref; // reference to URNG + constexpr static size_t _Bits = _Calc_bits(); // number of random bits generated by _Get_bits() + constexpr static _Udiff _Bmask = _Udiff(-1) >> (_Udiff_bits - _Bits); // 2^_Bits - 1 +}; + [[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Xbad_alloc(); [[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Xinvalid_argument(_In_z_ const char*); [[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Xlength_error(_In_z_ const char*); diff --git a/tests/std/test.lst b/tests/std/test.lst index 15b0eb8f766..98e91c7a9b8 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -155,6 +155,7 @@ tests\Dev11_1140665_unique_ptr_array_conversions tests\Dev11_1150223_shared_mutex tests\Dev11_1158803_regex_thread_safety tests\Dev11_1180290_filesystem_error_code +tests\GH_000178_uniform_int tests\GH_000342_filebuf_close tests\GH_000431_copy_move_family tests\GH_000431_equal_family diff --git a/tests/std/tests/GH_000178_uniform_int/env.lst b/tests/std/tests/GH_000178_uniform_int/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000178_uniform_int/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000178_uniform_int/test.cpp b/tests/std/tests/GH_000178_uniform_int/test.cpp new file mode 100644 index 00000000000..7a377113006 --- /dev/null +++ b/tests/std/tests/GH_000178_uniform_int/test.cpp @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; + +template +bool basic_test() { + constexpr auto max = (1ull << 60); + constexpr auto num_bins = 20ull; // Don't change this without looking up a new threshold below. + constexpr auto bin_width = max / num_bins; // except possibly the last bin + constexpr auto bin_freq = static_cast(bin_width) / max; + constexpr auto freq_rem = static_cast(max % bin_width) / max; + constexpr double threshold = 31.410; // chi-squared critical value for d.f. = 20 and p = 0.05 + + Generator gen; + std::uniform_int_distribution dist(0, max - 1u); + + + const int N = 20'000; + int frequency[num_bins] = {0}; + for (int i = 0; i < N; ++i) { + ++frequency[std::min(dist(gen) / bin_width, num_bins - 1u)]; + } + + double chi_squared = 0.0; + for (unsigned i = 0; i < num_bins; ++i) { + const auto expected = (bin_freq + (i == num_bins - 1u ? freq_rem : 0.0)) * N; + const auto delta = static_cast(frequency[i] - expected); + chi_squared += (delta * delta) / expected; + } + + return chi_squared <= threshold; +} + +bool test_modulus_bias() { + // This test is designed to detect modulus bias. When generating random intergers in [0,s) with a URBG having range + // [0,R), then R mod s values must be rejected to ensure uniformity. By making (R mod s)/R large, we can introduce a + // large bias if the rejection is incorrect. + + constexpr int max = 5; // Don't change this without looking up a new threshold below. + constexpr double threshold = 11.07; // chi-squared critical value for d.f. = 5 and p = 0.05 + std::uniform_int_distribution<> rng(0, max - 1); + std::independent_bits_engine gen; + + const int N = 1'000; + int frequency[max] = {0}; + for (int i = 0; i < N; ++i) { + ++frequency[rng(gen)]; + } + + double chi_squared = 0.0; + for (int i = 0; i < max; ++i) { + const double expected = static_cast(N) / max; + const double delta = frequency[i] - expected; + chi_squared += delta * delta / expected; + } + + return chi_squared <= threshold; +} + +int main() { + // Four cases tested below: + // (1) URBG provides enough bits to completely fill the underlying type + // (2) URBG provides enough bits for our upper bound, but not enough to fill the type + // (3) URBG is called multiple times, but doesn't fill the type + // (4) URBG is called multiple times and overflow the number of bits in the type + assert((basic_test())); + assert((basic_test>())); + assert((basic_test>())); + assert((basic_test>())); + + assert(test_modulus_bias()); + + return 0; +} From 3c8de09e6a1ae9538781e539d93aba801edd395a Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Thu, 18 Aug 2022 18:47:35 -0700 Subject: [PATCH 2/8] review feedback --- stl/inc/xutility | 44 ++++++++----------- .../std/tests/GH_000178_uniform_int/test.cpp | 40 ++++++++--------- 2 files changed, 39 insertions(+), 45 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 90ae90fbda0..e96f6ab57ac 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5710,7 +5710,8 @@ public: using _Udiff = conditional_t; - explicit _Rng_from_urng(_Urng& _Func) : _Ref(_Func), _Bits(CHAR_BIT * sizeof(_Udiff)), _Bmask(_Udiff(-1)) { + explicit _Rng_from_urng(_Urng& _Func) + : _Ref(_Func), _Bits(CHAR_BIT * sizeof(_Udiff)), _Bmask(static_cast<_Udiff>(-1)) { for (; (_Urng::max)() - (_Urng::min)() < _Bmask; _Bmask >>= 1) { --_Bits; } @@ -5721,7 +5722,7 @@ public: _Udiff _Ret = 0; // random bits _Udiff _Mask = 0; // 2^N - 1, _Ret is within [0, _Mask] - while (_Mask < _Udiff(_Index - 1)) { // need more random bits + while (_Mask < static_cast<_Udiff>(_Index - 1)) { // need more random bits _Ret <<= _Bits - 1; // avoid full shift _Ret <<= 1; _Ret |= _Get_bits(); @@ -5731,7 +5732,7 @@ public: } // _Ret is [0, _Mask], _Index - 1 <= _Mask, return if unbiased - if (_Ret / _Index < _Mask / _Index || _Mask % _Index == _Udiff(_Index - 1)) { + if (_Ret / _Index < _Mask / _Index || _Mask % _Index == static_cast<_Udiff>(_Index - 1)) { return static_cast<_Diff>(_Ret % _Index); } } @@ -5755,7 +5756,7 @@ public: private: _Udiff _Get_bits() { // return a random value within [0, _Bmask] for (;;) { // repeat until random value is in range - _Udiff _Val = _Ref() - (_Urng::min)(); + const _Udiff _Val = _Ref() - (_Urng::min)(); if (_Val <= _Bmask) { return _Val; @@ -5772,10 +5773,10 @@ template class _Rng_from_urng_v2 { // wrap a URNG as an RNG public: using _Ty0 = make_unsigned_t<_Diff>; - using _Ty1 = typename _Urng::result_type; + using _Ty1 = _Invoke_result_t<_Urng&>; using _Udiff = conditional_t; - constexpr static unsigned int _Udiff_bits = sizeof(_Udiff) * CHAR_BIT; + static constexpr unsigned int _Udiff_bits = sizeof(_Udiff) * CHAR_BIT; using _Uprod = conditional_t<_Udiff_bits <= 16, uint32_t, conditional_t<_Udiff_bits <= 32, uint64_t, _Unsigned128>>; explicit _Rng_from_urng_v2(_Urng& _Func) : _Ref(_Func) {} @@ -5783,20 +5784,15 @@ public: _Diff operator()(_Diff _Index) { // adapt _Urng closed range to [0, _Index) // From Daniel Lemire, "Fast Random Integer Generation in an Interval", ACM Trans. Model. Comput. Simul. 29 (1), // 2019. - _Udiff _Mask; - unsigned int _Niter; + _Udiff _Mask = _Bmask; + unsigned int _Niter = 1; if constexpr (_Bits < _Udiff_bits) { - _Mask = 0; - _Niter = 0; - do { + while (_Mask < static_cast<_Udiff>(_Index - 1)) { _Mask <<= _Bits; _Mask |= _Bmask; ++_Niter; - } while (_Mask < _Udiff(_Index - 1)); - } else { - _Mask = _Bmask; - _Niter = 1; + } } auto _Product = _Get_random_product(_Index, _Niter); @@ -5838,8 +5834,9 @@ public: private: _Udiff _Get_bits() { // return a random value within [0, _Bmask] + static constexpr auto _Urng_min = (_Urng::min)(); for (;;) { // repeat until random value is in range - _Udiff _Val = _Ref() - (_Urng::min)(); + const _Udiff _Val = _Ref() - _Urng_min; if (_Val <= _Bmask) { return _Val; @@ -5849,7 +5846,7 @@ private: static constexpr size_t _Calc_bits() { auto _Bits_local = _Udiff_bits; - auto _Bmask_local = _Udiff(-1); + auto _Bmask_local = static_cast<_Udiff>(-1); for (; (_Urng::max)() - (_Urng::min)() < _Bmask_local; _Bmask_local >>= 1) { --_Bits_local; } @@ -5858,15 +5855,12 @@ private: } _Uprod _Get_random_product(const _Diff _Index, unsigned int _Niter) { - _Udiff _Ret; + _Udiff _Ret = _Get_bits(); if constexpr (_Bits < _Udiff_bits) { - _Ret = 0; - do { + while (--_Niter > 0) { _Ret <<= _Bits; _Ret |= _Get_bits(); - } while (--_Niter > 0); - } else { - _Ret = _Get_bits(); + } } if constexpr (is_same_v<_Udiff, uint64_t>) { @@ -5879,8 +5873,8 @@ private: } _Urng& _Ref; // reference to URNG - constexpr static size_t _Bits = _Calc_bits(); // number of random bits generated by _Get_bits() - constexpr static _Udiff _Bmask = _Udiff(-1) >> (_Udiff_bits - _Bits); // 2^_Bits - 1 + static constexpr size_t _Bits = _Calc_bits(); // number of random bits generated by _Get_bits() + static constexpr _Udiff _Bmask = static_cast<_Udiff>(-1) >> (_Udiff_bits - _Bits); // 2^_Bits - 1 }; [[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Xbad_alloc(); diff --git a/tests/std/tests/GH_000178_uniform_int/test.cpp b/tests/std/tests/GH_000178_uniform_int/test.cpp index 7a377113006..4375a4e0949 100644 --- a/tests/std/tests/GH_000178_uniform_int/test.cpp +++ b/tests/std/tests/GH_000178_uniform_int/test.cpp @@ -10,28 +10,28 @@ using namespace std; template bool basic_test() { - constexpr auto max = (1ull << 60); + constexpr auto maximum = 1ull << 60; constexpr auto num_bins = 20ull; // Don't change this without looking up a new threshold below. - constexpr auto bin_width = max / num_bins; // except possibly the last bin - constexpr auto bin_freq = static_cast(bin_width) / max; - constexpr auto freq_rem = static_cast(max % bin_width) / max; + constexpr auto bin_width = maximum / num_bins; // except possibly the last bin + constexpr auto bin_freq = static_cast(bin_width) / maximum; + constexpr auto freq_rem = static_cast(maximum % bin_width) / maximum; constexpr double threshold = 31.410; // chi-squared critical value for d.f. = 20 and p = 0.05 Generator gen; - std::uniform_int_distribution dist(0, max - 1u); + uniform_int_distribution dist(0, maximum - 1u); const int N = 20'000; - int frequency[num_bins] = {0}; + int frequency[num_bins] = {}; for (int i = 0; i < N; ++i) { - ++frequency[std::min(dist(gen) / bin_width, num_bins - 1u)]; + ++frequency[min(dist(gen) / bin_width, num_bins - 1u)]; } double chi_squared = 0.0; - for (unsigned i = 0; i < num_bins; ++i) { + for (unsigned int i = 0; i < num_bins; ++i) { const auto expected = (bin_freq + (i == num_bins - 1u ? freq_rem : 0.0)) * N; const auto delta = static_cast(frequency[i] - expected); - chi_squared += (delta * delta) / expected; + chi_squared += delta * delta / expected; } return chi_squared <= threshold; @@ -42,20 +42,20 @@ bool test_modulus_bias() { // [0,R), then R mod s values must be rejected to ensure uniformity. By making (R mod s)/R large, we can introduce a // large bias if the rejection is incorrect. - constexpr int max = 5; // Don't change this without looking up a new threshold below. + constexpr int maximum = 5; // Don't change this without looking up a new threshold below. constexpr double threshold = 11.07; // chi-squared critical value for d.f. = 5 and p = 0.05 - std::uniform_int_distribution<> rng(0, max - 1); - std::independent_bits_engine gen; + uniform_int_distribution<> rng(0, maximum - 1); + independent_bits_engine gen; - const int N = 1'000; - int frequency[max] = {0}; + const int N = 1'000; + int frequency[maximum] = {}; for (int i = 0; i < N; ++i) { ++frequency[rng(gen)]; } double chi_squared = 0.0; - for (int i = 0; i < max; ++i) { - const double expected = static_cast(N) / max; + for (int i = 0; i < maximum; ++i) { + const double expected = static_cast(N) / maximum; const double delta = frequency[i] - expected; chi_squared += delta * delta / expected; } @@ -68,11 +68,11 @@ int main() { // (1) URBG provides enough bits to completely fill the underlying type // (2) URBG provides enough bits for our upper bound, but not enough to fill the type // (3) URBG is called multiple times, but doesn't fill the type - // (4) URBG is called multiple times and overflow the number of bits in the type + // (4) URBG is called multiple times and overflows the number of bits in the type assert((basic_test())); - assert((basic_test>())); - assert((basic_test>())); - assert((basic_test>())); + assert((basic_test>())); + assert((basic_test>())); + assert((basic_test>())); assert(test_modulus_bias()); From 771edfdf66ce56c1b75b7b1f6729b164e3d7f32a Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Mon, 12 Sep 2022 15:09:33 -0700 Subject: [PATCH 3/8] add benchmark code to the repo --- benchmarks/CMakeLists.txt | 6 +- benchmarks/src/random_integer_generation.cpp | 98 ++++++++++++++++++++ 2 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 benchmarks/src/random_integer_generation.cpp diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 4c684445087..45f3d622589 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -71,7 +71,5 @@ function(add_benchmark name) target_link_libraries(benchmark-${name} PRIVATE benchmark::benchmark) endfunction() -add_benchmark(std_copy - src/std_copy.cpp - CXX_STANDARD 23 -) +add_benchmark(std_copy src/std_copy.cpp) +add_benchmark(random_integer_generation src/random_integer_generation.cpp) diff --git a/benchmarks/src/random_integer_generation.cpp b/benchmarks/src/random_integer_generation.cpp new file mode 100644 index 00000000000..cbad3f57fa5 --- /dev/null +++ b/benchmarks/src/random_integer_generation.cpp @@ -0,0 +1,98 @@ +#include +#include + +/// Test URBGs alone + +static void BM_mt19937(benchmark::State& state) { + std::mt19937 gen; + for (auto _ : state) { + benchmark::DoNotOptimize(gen()); + } +} +BENCHMARK(BM_mt19937); + +static void BM_mt19937_64(benchmark::State& state) { + std::mt19937_64 gen; + for (auto _ : state) { + benchmark::DoNotOptimize(gen()); + } +} +BENCHMARK(BM_mt19937_64); + +static void BM_lcg(benchmark::State& state) { + std::minstd_rand gen; + for (auto _ : state) { + benchmark::DoNotOptimize(gen()); + } +} +BENCHMARK(BM_lcg); + +uint32_t GetMax() { + std::random_device gen; + std::uniform_int_distribution dist(10'000'000, 20'000'000); + return dist(gen); +} + +static const uint32_t max = GetMax(); // random divisor to prevent strength reduction + +/// Test mt19937 + +static void BM_raw_mt19937_old(benchmark::State& state) { + std::mt19937 gen; + std::_Rng_from_urng rng(gen); + for (auto _ : state) { + benchmark::DoNotOptimize(rng(max)); + } +} +BENCHMARK(BM_raw_mt19937_old); + +static void BM_raw_mt19937_new(benchmark::State& state) { + std::mt19937 gen; + std::_Rng_from_urng_v2 rng(gen); + for (auto _ : state) { + benchmark::DoNotOptimize(rng(max)); + } +} +BENCHMARK(BM_raw_mt19937_new); + +/// Test mt19937_64 + +static void BM_raw_mt19937_64_old(benchmark::State& state) { + std::mt19937_64 gen; + std::_Rng_from_urng rng(gen); + for (auto _ : state) { + benchmark::DoNotOptimize(rng(max)); + } +} +BENCHMARK(BM_raw_mt19937_64_old); + +static void BM_raw_mt19937_64_new(benchmark::State& state) { + std::mt19937_64 gen; + std::_Rng_from_urng_v2 rng(gen); + for (auto _ : state) { + benchmark::DoNotOptimize(rng(max)); + } +} +BENCHMARK(BM_raw_mt19937_64_new); + +/// Test minstd_rand + +static void BM_raw_lcg_old(benchmark::State& state) { + std::minstd_rand gen; + std::_Rng_from_urng rng(gen); + for (auto _ : state) { + benchmark::DoNotOptimize(rng(max)); + } +} +BENCHMARK(BM_raw_lcg_old); + +static void BM_raw_lcg_new(benchmark::State& state) { + std::minstd_rand gen; + std::_Rng_from_urng_v2 rng(gen); + for (auto _ : state) { + benchmark::DoNotOptimize(rng(max)); + } +} +BENCHMARK(BM_raw_lcg_new); + +BENCHMARK_MAIN(); From 6c5657b7549cac15aef4726c36430f700ff6ab19 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 12 Sep 2022 16:05:19 -0700 Subject: [PATCH 4/8] Add banner. --- benchmarks/src/random_integer_generation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/benchmarks/src/random_integer_generation.cpp b/benchmarks/src/random_integer_generation.cpp index cbad3f57fa5..999c1db4d1d 100644 --- a/benchmarks/src/random_integer_generation.cpp +++ b/benchmarks/src/random_integer_generation.cpp @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + #include #include From 7e025782d9f99ebfe0a629269dfb55e2e984eca7 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 12 Sep 2022 16:06:18 -0700 Subject: [PATCH 5/8] Include , qualify typedefs. --- benchmarks/src/random_integer_generation.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/benchmarks/src/random_integer_generation.cpp b/benchmarks/src/random_integer_generation.cpp index 999c1db4d1d..0deb9059cc8 100644 --- a/benchmarks/src/random_integer_generation.cpp +++ b/benchmarks/src/random_integer_generation.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include +#include #include /// Test URBGs alone @@ -30,19 +31,19 @@ static void BM_lcg(benchmark::State& state) { } BENCHMARK(BM_lcg); -uint32_t GetMax() { +std::uint32_t GetMax() { std::random_device gen; - std::uniform_int_distribution dist(10'000'000, 20'000'000); + std::uniform_int_distribution dist(10'000'000, 20'000'000); return dist(gen); } -static const uint32_t max = GetMax(); // random divisor to prevent strength reduction +static const std::uint32_t max = GetMax(); // random divisor to prevent strength reduction /// Test mt19937 static void BM_raw_mt19937_old(benchmark::State& state) { std::mt19937 gen; - std::_Rng_from_urng rng(gen); + std::_Rng_from_urng rng(gen); for (auto _ : state) { benchmark::DoNotOptimize(rng(max)); } @@ -51,7 +52,7 @@ BENCHMARK(BM_raw_mt19937_old); static void BM_raw_mt19937_new(benchmark::State& state) { std::mt19937 gen; - std::_Rng_from_urng_v2 rng(gen); + std::_Rng_from_urng_v2 rng(gen); for (auto _ : state) { benchmark::DoNotOptimize(rng(max)); } @@ -62,7 +63,7 @@ BENCHMARK(BM_raw_mt19937_new); static void BM_raw_mt19937_64_old(benchmark::State& state) { std::mt19937_64 gen; - std::_Rng_from_urng rng(gen); + std::_Rng_from_urng rng(gen); for (auto _ : state) { benchmark::DoNotOptimize(rng(max)); } @@ -71,7 +72,7 @@ BENCHMARK(BM_raw_mt19937_64_old); static void BM_raw_mt19937_64_new(benchmark::State& state) { std::mt19937_64 gen; - std::_Rng_from_urng_v2 rng(gen); + std::_Rng_from_urng_v2 rng(gen); for (auto _ : state) { benchmark::DoNotOptimize(rng(max)); } @@ -82,7 +83,7 @@ BENCHMARK(BM_raw_mt19937_64_new); static void BM_raw_lcg_old(benchmark::State& state) { std::minstd_rand gen; - std::_Rng_from_urng rng(gen); + std::_Rng_from_urng rng(gen); for (auto _ : state) { benchmark::DoNotOptimize(rng(max)); } @@ -91,7 +92,7 @@ BENCHMARK(BM_raw_lcg_old); static void BM_raw_lcg_new(benchmark::State& state) { std::minstd_rand gen; - std::_Rng_from_urng_v2 rng(gen); + std::_Rng_from_urng_v2 rng(gen); for (auto _ : state) { benchmark::DoNotOptimize(rng(max)); } From 2f00b9381cd4e7188a6f969284a121d3f567d177 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 12 Sep 2022 16:07:37 -0700 Subject: [PATCH 6/8] Rename max to maximum. --- benchmarks/src/random_integer_generation.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/benchmarks/src/random_integer_generation.cpp b/benchmarks/src/random_integer_generation.cpp index 0deb9059cc8..ebe5d668738 100644 --- a/benchmarks/src/random_integer_generation.cpp +++ b/benchmarks/src/random_integer_generation.cpp @@ -37,7 +37,7 @@ std::uint32_t GetMax() { return dist(gen); } -static const std::uint32_t max = GetMax(); // random divisor to prevent strength reduction +static const std::uint32_t maximum = GetMax(); // random divisor to prevent strength reduction /// Test mt19937 @@ -45,7 +45,7 @@ static void BM_raw_mt19937_old(benchmark::State& state) { std::mt19937 gen; std::_Rng_from_urng rng(gen); for (auto _ : state) { - benchmark::DoNotOptimize(rng(max)); + benchmark::DoNotOptimize(rng(maximum)); } } BENCHMARK(BM_raw_mt19937_old); @@ -54,7 +54,7 @@ static void BM_raw_mt19937_new(benchmark::State& state) { std::mt19937 gen; std::_Rng_from_urng_v2 rng(gen); for (auto _ : state) { - benchmark::DoNotOptimize(rng(max)); + benchmark::DoNotOptimize(rng(maximum)); } } BENCHMARK(BM_raw_mt19937_new); @@ -65,7 +65,7 @@ static void BM_raw_mt19937_64_old(benchmark::State& state) { std::mt19937_64 gen; std::_Rng_from_urng rng(gen); for (auto _ : state) { - benchmark::DoNotOptimize(rng(max)); + benchmark::DoNotOptimize(rng(maximum)); } } BENCHMARK(BM_raw_mt19937_64_old); @@ -74,7 +74,7 @@ static void BM_raw_mt19937_64_new(benchmark::State& state) { std::mt19937_64 gen; std::_Rng_from_urng_v2 rng(gen); for (auto _ : state) { - benchmark::DoNotOptimize(rng(max)); + benchmark::DoNotOptimize(rng(maximum)); } } BENCHMARK(BM_raw_mt19937_64_new); @@ -85,7 +85,7 @@ static void BM_raw_lcg_old(benchmark::State& state) { std::minstd_rand gen; std::_Rng_from_urng rng(gen); for (auto _ : state) { - benchmark::DoNotOptimize(rng(max)); + benchmark::DoNotOptimize(rng(maximum)); } } BENCHMARK(BM_raw_lcg_old); @@ -94,7 +94,7 @@ static void BM_raw_lcg_new(benchmark::State& state) { std::minstd_rand gen; std::_Rng_from_urng_v2 rng(gen); for (auto _ : state) { - benchmark::DoNotOptimize(rng(max)); + benchmark::DoNotOptimize(rng(maximum)); } } BENCHMARK(BM_raw_lcg_new); From ff90fe89fd0ea052fd9cdb9d524168970921ab95 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Mon, 12 Sep 2022 23:02:28 -0700 Subject: [PATCH 7/8] review feedback --- stl/inc/xutility | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index b4184ca62ef..bd9a4d8934d 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -6087,6 +6087,14 @@ public: _Diff operator()(_Diff _Index) { // adapt _Urng closed range to [0, _Index) // From Daniel Lemire, "Fast Random Integer Generation in an Interval", ACM Trans. Model. Comput. Simul. 29 (1), // 2019. + // + // Algorithm 5 <-> This Code: + // m <-> _Product + // l <-> _Rem + // s <-> _Index + // t <-> _Threshold + // L <-> _Generated_bits + _Udiff _Mask = _Bmask; unsigned int _Niter = 1; @@ -6098,10 +6106,14 @@ public: } } + // x <- random integer in [0, 2^L) + // m <- x * s auto _Product = _Get_random_product(_Index, _Niter); - auto _Rem = static_cast<_Udiff>(_Product) & _Mask; + // l <- m mod 2^L + auto _Rem = static_cast<_Udiff>(_Product) & _Mask; if (_Rem < _Index) { + // t <- (2^L - s) mod s const auto _Threshold = (_Mask - (_Index - 1)) % _Index; while (_Rem < _Threshold) { _Product = _Get_random_product(_Index, _Niter); @@ -6116,6 +6128,7 @@ public: _Generated_bits = _Udiff_bits; } + // m / 2^L return static_cast<_Diff>(_Product >> _Generated_bits); } From cc4dcea51f127511470051e00124503cdbcad3f9 Mon Sep 17 00:00:00 2001 From: Matt Stephanson Date: Fri, 16 Sep 2022 19:59:00 -0700 Subject: [PATCH 8/8] Episode V: The Review Feedback Strikes Back --- stl/inc/xutility | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 4a94d6e5ace..c4d4bf3700b 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -6110,6 +6110,7 @@ public: // s <-> _Index // t <-> _Threshold // L <-> _Generated_bits + // 2^L - 1 <-> _Mask _Udiff _Mask = _Bmask; unsigned int _Niter = 1; @@ -6130,7 +6131,7 @@ public: if (_Rem < _Index) { // t <- (2^L - s) mod s - const auto _Threshold = (_Mask - (_Index - 1)) % _Index; + const auto _Threshold = (_Mask - _Index + 1) % _Index; while (_Rem < _Threshold) { _Product = _Get_random_product(_Index, _Niter); _Rem = static_cast<_Udiff>(_Product) & _Mask;