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) 52 QUILL_NODISCARD
double ns_per_tick()
const noexcept {
return _ns_per_tick; }
65 constexpr std::chrono::milliseconds spin_duration = std::chrono::milliseconds{10};
66 constexpr
size_t max_trials = 15;
67 constexpr
size_t min_trials = 3;
68 constexpr
double convergence_threshold = 0.01;
70 std::vector<double> rates;
71 rates.reserve(max_trials);
73 double previous_median = 0.0;
75 for (
size_t i = 0; i < max_trials; ++i)
77 auto const beg_ts = detail::get_timestamp<std::chrono::steady_clock>();
78 uint64_t
const beg_tsc =
rdtsc();
80 std::chrono::nanoseconds elapsed_ns;
84 auto const end_ts = detail::get_timestamp<std::chrono::steady_clock>();
86 elapsed_ns = end_ts - beg_ts;
87 }
while (elapsed_ns < spin_duration);
89 rates.push_back(static_cast<double>(end_tsc - beg_tsc) / static_cast<double>(elapsed_ns.count()));
92 if (((i + 1) >= min_trials) && (((i + 1) % 2) != 0))
94 std::nth_element(rates.begin(), rates.begin() +
static_cast<ptrdiff_t
>((i + 1) / 2), rates.end());
95 double current_median = rates[(i + 1) / 2];
98 if (std::abs(current_median - previous_median) / current_median < convergence_threshold)
103 previous_median = current_median;
108 std::nth_element(rates.begin(), rates.begin() +
static_cast<ptrdiff_t
>(rates.size() / 2),
111 double const ticks_per_ns = rates[rates.size() / 2];
112 _ns_per_tick = 1 / ticks_per_ns;
115 double _ns_per_tick{0};
119 explicit RdtscClock(std::chrono::nanoseconds resync_interval)
120 : _ns_per_tick(RdtscTicks::instance().ns_per_tick())
122 double const calc_value =
static_cast<double>(resync_interval.count()) * _ns_per_tick;
125 if (calc_value >= static_cast<double>(std::numeric_limits<int64_t>::max()) || calc_value < 0)
127 _resync_interval_ticks = std::numeric_limits<int64_t>::max();
131 _resync_interval_ticks =
static_cast<int64_t
>(calc_value);
134 _resync_interval_original = _resync_interval_ticks;
136 if (!resync(resync_lag_cycles))
139 if (!resync(resync_lag_cycles * 2u))
141 std::fprintf(stderr,
"Failed to sync RdtscClock. Timestamps will be incorrect\n");
147 uint64_t time_since_epoch(uint64_t rdtsc_value)
const noexcept
152 auto const index = _version.load(std::memory_order_relaxed) & (_base.size() - 1);
155 auto diff =
static_cast<int64_t
>(rdtsc_value - _base[index].base_tsc);
158 if (diff > _resync_interval_ticks)
160 resync(resync_lag_cycles);
161 diff =
static_cast<int64_t
>(rdtsc_value - _base[index].base_tsc);
164 return static_cast<uint64_t
>(_base[index].base_time +
165 static_cast<int64_t
>(
static_cast<double>(diff) * _ns_per_tick));
169 uint64_t time_since_epoch_safe(uint64_t rdtsc_value)
const noexcept
178 version = _version.load(std::memory_order_acquire);
179 auto const index = version & (_base.size() - 1);
181 if (QUILL_UNLIKELY((_base[index].base_tsc) == 0 && (_base[index].base_time == 0)))
187 auto const diff =
static_cast<int64_t
>(rdtsc_value - _base[index].base_tsc);
188 wall_ts =
static_cast<uint64_t
>(_base[index].base_time +
189 static_cast<int64_t
>(
static_cast<double>(diff) * _ns_per_tick));
190 }
while (version != _version.load(std::memory_order_acquire));
196 bool resync(uint32_t lag)
const noexcept
199 constexpr uint8_t max_attempts{4};
201 for (uint8_t attempt = 0; attempt < max_attempts; ++attempt)
203 uint64_t
const beg =
rdtsc();
205 auto const wall_time =
static_cast<int64_t
>(detail::get_timestamp_ns<std::chrono::system_clock>());
206 uint64_t
const end =
rdtsc();
208 if (QUILL_LIKELY(end - beg <= lag))
211 auto const index = (_version.load(std::memory_order_relaxed) + 1) & (_base.size() - 1);
212 _base[index].base_time = wall_time;
213 _base[index].base_tsc = _fast_average(beg, end);
214 _version.fetch_add(1, std::memory_order_release);
216 _resync_interval_ticks = _resync_interval_original;
223 constexpr int64_t max_int64_half = std::numeric_limits<int64_t>::max() / 2;
224 if (_resync_interval_ticks <= max_int64_half)
226 _resync_interval_ticks = _resync_interval_ticks * 2;
233 double nanoseconds_per_tick()
const noexcept {
return _ns_per_tick; }
238 BaseTimeTsc() =
default;
239 int64_t base_time{0};
240 uint64_t base_tsc{0};
244 QUILL_NODISCARD
static uint64_t _fast_average(uint64_t x, uint64_t y) noexcept
246 return (x & y) + ((x ^ y) >> 1);
250 static constexpr uint32_t resync_lag_cycles {50
'000}; 251 mutable int64_t _resync_interval_ticks{0}; 252 int64_t _resync_interval_original{0}; /**< stores the initial interval value as as if we fail to resync we increase the timer */ 253 double _ns_per_tick{0}; 255 alignas(QUILL_CACHE_LINE_ALIGNED) mutable std::atomic<uint32_t> _version{0}; 256 mutable std::array<BaseTimeTsc, 2> _base{}; 259 #if defined(_WIN32) && defined(_MSC_VER) && !defined(__GNUC__) 263 } // namespace detail Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
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:109
A static class that calculates the rdtsc ticks per second.
Definition: RdtscClock.h:42