diff --git a/stl/inc/chrono b/stl/inc/chrono index 9c430dcc4e7..1eb85bd4beb 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -12,6 +12,7 @@ #include #include #include +#include #include #pragma pack(push, _CRT_PACKING) @@ -609,8 +610,40 @@ namespace chrono { static constexpr bool is_steady = true; _NODISCARD static time_point now() noexcept { // get current time +#if (defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64)) && !defined(_M_CEE_PURE) + // Implement atomics avoiding header dependency + static volatile long long _Cached_freq = LLONG_MAX; + static volatile long long _Cached_ctr_base = LLONG_MAX; + static volatile long long _Cached_result_base = LLONG_MAX; + + const long long _Freq_from_cache = _Atomic_load_ll_relaxed(&_Cached_freq); + const long long _Ctr_base = _Atomic_load_ll_relaxed(&_Cached_ctr_base); + const long long _Result_base = _Atomic_load_ll_relaxed(&_Cached_result_base); + if (_Freq_from_cache != LLONG_MAX && _Ctr_base != LLONG_MAX && _Result_base != LLONG_MAX) { + // Fast path + const long long _Ctr = _Query_perf_counter(); + return time_point(duration(_Result_base + (_Ctr - _Ctr_base) * period::den / _Freq_from_cache)); + } + // Calculate with two divisions to prevent overflow + const long long _Freq = _Query_perf_frequency(); + const long long _Ctr = _Query_perf_counter(); + const long long _Result = _Scale_large_counter(_Ctr, _Freq); + if (_Atomic_compare_exchange_strong_ll_seq_cst(&_Cached_freq, _Freq, LLONG_MAX)) { + // This is the first result, save current result as base for fast path + _Atomic_compare_exchange_strong_ll_seq_cst(&_Cached_ctr_base, _Ctr, LLONG_MAX); + _Atomic_compare_exchange_strong_ll_seq_cst(&_Cached_result_base, _Result, LLONG_MAX); + } + // if _Result is not saved as first, it is still compatible with fast result + return time_point(duration(_Result)); +#else // ^^^ known hardware && !defined(_M_CEE_PURE) / unknown hardware || defined(_M_CEE_PURE) vvv const long long _Freq = _Query_perf_frequency(); // doesn't change after system boot const long long _Ctr = _Query_perf_counter(); + return time_point(duration(_Scale_large_counter(_Ctr, _Freq))); +#endif // (defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64)) && !defined(_M_CEE_PURE) + } + + private: + _NODISCARD static long long _Scale_large_counter(const long long _Ctr, const long long _Freq) noexcept { static_assert(period::num == 1, "This assumes period::num == 1."); // Instead of just having "(_Ctr * period::den) / _Freq", // the algorithm below prevents overflow when _Ctr is sufficiently large. @@ -619,7 +652,7 @@ namespace chrono { // but the initial value of _Ctr could be large. const long long _Whole = (_Ctr / _Freq) * period::den; const long long _Part = (_Ctr % _Freq) * period::den / _Freq; - return time_point(duration(_Whole + _Part)); + return _Whole + _Part; } }; diff --git a/stl/inc/xatomic.h b/stl/inc/xatomic.h index 6af55a96696..970d2fd4340 100644 --- a/stl/inc/xatomic.h +++ b/stl/inc/xatomic.h @@ -104,6 +104,27 @@ _NODISCARD volatile _Integral* _Atomic_address_as(_Ty& _Source) noexcept { return &reinterpret_cast(_Source); } +// FUNCTION TEMPLATE _Atomic_load_ll_relaxed +#if (defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64)) && !defined(_M_CEE_PURE) +_NODISCARD inline long long _Atomic_load_ll_relaxed(volatile long long* _Mem) noexcept { + // Copy from _Atomic_storage<_Ty, 8>::load +#if defined(_M_IX86) || defined(_M_ARM64) + return __iso_volatile_load64(_Mem); +#elif defined(_M_X64) + return *_Mem; +#else // _M_ARM + return __ldrexd(_Mem); +#endif // hardware +} + +// FUNCTION TEMPLATE _Atomic_compare_exchange_strong_ll_seq_cst +inline bool _Atomic_compare_exchange_strong_ll_seq_cst( + volatile long long* _Mem, long long _Value, long long _Comparand) noexcept { + // Copy from _Atomic_storage<_Ty, 8>::store + return _InterlockedCompareExchange64(_Mem, _Value, _Comparand) == _Comparand; +} +#endif // (defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64)) && !defined(_M_CEE_PURE) + _STD_END #pragma pop_macro("new")