9 #include "quill/core/Attributes.h" 10 #include "quill/core/ChronoTimeUtils.h" 11 #include "quill/core/Common.h" 12 #include "quill/core/Rdtsc.h" 28 #if defined(_WIN32) && defined(_MSC_VER) && !defined(__GNUC__) 30 #pragma warning(disable : 4324) 45 QUILL_NODISCARD QUILL_EXPORT
static RdtscTicks& instance()
52 QUILL_NODISCARD
double ns_per_tick()
const noexcept {
return _ns_per_tick; }
60 #if defined(__aarch64__) 63 __asm__
volatile(
"mrs %0, cntfrq_el0" :
"=r"(freq));
65 _ns_per_tick = 1e9 /
static_cast<double>(freq);
72 constexpr uint64_t spin_duration_ns = 10ull * 1
'000'000ull;
73 constexpr
size_t max_trials = 15;
74 constexpr
size_t min_trials = 3;
75 constexpr
double convergence_threshold = 0.01;
77 std::vector<double> rates;
78 rates.reserve(max_trials);
80 double previous_median = 0.0;
82 for (
size_t i = 0; i < max_trials; ++i)
85 uint64_t
const beg_tsc =
rdtsc();
93 elapsed_ns = end_ts - beg_ts;
94 }
while (elapsed_ns < spin_duration_ns);
96 rates.push_back(static_cast<double>(end_tsc - beg_tsc) / static_cast<double>(elapsed_ns));
99 if (((i + 1) >= min_trials) && (((i + 1) % 2) != 0))
101 std::nth_element(rates.begin(), rates.begin() +
static_cast<ptrdiff_t
>((i + 1) / 2), rates.end());
102 double current_median = rates[(i + 1) / 2];
105 if (std::abs(current_median - previous_median) / current_median < convergence_threshold)
110 previous_median = current_median;
115 std::nth_element(rates.begin(), rates.begin() +
static_cast<ptrdiff_t
>(rates.size() / 2),
118 double const ticks_per_ns = rates[rates.size() / 2];
119 _ns_per_tick = 1 / ticks_per_ns;
123 double _ns_per_tick{0};
127 explicit RdtscClock(std::chrono::nanoseconds resync_interval)
128 : _ns_per_tick(RdtscTicks::instance().ns_per_tick())
130 double const calc_value =
static_cast<double>(resync_interval.count()) / _ns_per_tick;
133 if (calc_value >= static_cast<double>(std::numeric_limits<int64_t>::max()) || calc_value < 0)
135 _resync_interval_ticks = std::numeric_limits<int64_t>::max();
139 _resync_interval_ticks =
static_cast<int64_t
>(calc_value);
144 if (!resync(resync_lag_cycles))
147 if (!resync(resync_lag_cycles * 2u))
149 std::fprintf(stderr,
"Failed to sync RdtscClock. Timestamps will be incorrect\n");
155 uint64_t time_since_epoch(uint64_t rdtsc_value)
const noexcept
160 auto const index = _version.load(std::memory_order_relaxed) & (_base.size() - 1);
164 auto diff =
static_cast<int64_t
>(rdtsc_value - _base[index].base_tsc);
167 if (diff > _resync_interval_ticks)
169 resync(resync_lag_cycles);
170 auto const resynced_index = _version.load(std::memory_order_relaxed) & (_base.size() - 1);
171 diff =
static_cast<int64_t
>(rdtsc_value - _base[resynced_index].base_tsc);
172 return static_cast<uint64_t
>(_base[resynced_index].base_time +
173 static_cast<int64_t
>(
static_cast<double>(diff) * _ns_per_tick));
176 return static_cast<uint64_t
>(_base[index].base_time +
177 static_cast<int64_t
>(
static_cast<double>(diff) * _ns_per_tick));
181 uint64_t time_since_epoch_safe(uint64_t rdtsc_value)
const noexcept
190 version = _version.load(std::memory_order_acquire);
191 auto const index = version & (_base.size() - 1);
193 if (QUILL_UNLIKELY((_base[index].base_tsc) == 0 && (_base[index].base_time == 0)))
199 auto const diff =
static_cast<int64_t
>(rdtsc_value - _base[index].base_tsc);
200 wall_ts =
static_cast<uint64_t
>(_base[index].base_time +
201 static_cast<int64_t
>(
static_cast<double>(diff) * _ns_per_tick));
202 }
while (version != _version.load(std::memory_order_acquire));
208 bool resync(uint32_t lag)
const noexcept
211 constexpr uint8_t max_attempts{4};
213 for (uint8_t attempt = 0; attempt < max_attempts; ++attempt)
215 uint64_t
const beg =
rdtsc();
218 uint64_t
const end =
rdtsc();
220 if (QUILL_LIKELY(end - beg <= lag))
223 auto const index = (_version.load(std::memory_order_relaxed) + 1) & (_base.size() - 1);
224 _base[index].base_time = wall_time;
225 _base[index].base_tsc = _fast_average(beg, end);
226 _version.fetch_add(1, std::memory_order_release);
235 constexpr int64_t max_int64_half = std::numeric_limits<int64_t>::max() / 2;
236 if (_resync_interval_ticks <= max_int64_half)
238 _resync_interval_ticks = _resync_interval_ticks * 2;
245 double nanoseconds_per_tick()
const noexcept {
return _ns_per_tick; }
251 int64_t base_time{0};
252 uint64_t base_tsc{0};
256 QUILL_NODISCARD
static uint64_t _fast_average(uint64_t x, uint64_t y) noexcept
258 return (x & y) + ((x ^ y) >> 1);
262 static constexpr uint32_t resync_lag_cycles{50
'000}; 263 mutable int64_t _resync_interval_ticks{0}; 264 int64_t _resync_interval_original{0}; /**< stores the initial interval value as as if we fail to resync we increase the timer */ 265 double _ns_per_tick{0}; 267 alignas(QUILL_CACHE_LINE_ALIGNED) mutable std::atomic<uint32_t> _version{0}; 268 mutable std::array<BaseTimeTsc, 2> _base{}; 271 #if defined(_WIN32) && defined(_MSC_VER) && !defined(__GNUC__) 275 } // namespace detail Definition: RdtscClock.h:248
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:28
Converts tsc ticks to nanoseconds since epoch.
Definition: RdtscClock.h:36
QUILL_NODISCARD QUILL_ATTRIBUTE_HOT uint64_t rdtsc() noexcept
Get the TSC counter.
Definition: Rdtsc.h:105
QUILL_NODISCARD QUILL_ATTRIBUTE_HOT uint64_t get_steady_time_ns() noexcept
Mirrors std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::steady_clock::now().time_since_epoch()).count().
Definition: ChronoTimeUtils.h:90
QUILL_NODISCARD QUILL_ATTRIBUTE_HOT uint64_t get_system_time_ns() noexcept
Mirrors std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::system_clock::now().time_since_epoch()).count().
Definition: ChronoTimeUtils.h:57
A static class that calculates the rdtsc ticks per second.
Definition: RdtscClock.h:42
int64_t _resync_interval_original
stores the initial interval value as as if we fail to resync we increase the timer ...
Definition: RdtscClock.h:264