9 #include "quill/core/Attributes.h" 10 #include "quill/core/ChronoTimeUtils.h" 11 #include "quill/core/Common.h" 12 #include "quill/core/Rdtsc.h" 46 QUILL_NODISCARD
double ns_per_tick()
const noexcept {
return _ns_per_tick; }
59 constexpr std::chrono::milliseconds spin_duration = std::chrono::milliseconds{10};
60 constexpr
size_t max_trials = 15;
61 constexpr
size_t min_trials = 3;
62 constexpr
double convergence_threshold = 0.01;
64 std::vector<double> rates;
65 rates.reserve(max_trials);
67 double previous_median = 0.0;
69 for (
size_t i = 0; i < max_trials; ++i)
71 auto const beg_ts = detail::get_timestamp<std::chrono::steady_clock>();
72 uint64_t
const beg_tsc =
rdtsc();
74 std::chrono::nanoseconds elapsed_ns;
78 auto const end_ts = detail::get_timestamp<std::chrono::steady_clock>();
80 elapsed_ns = end_ts - beg_ts;
81 }
while (elapsed_ns < spin_duration);
83 rates.push_back(static_cast<double>(end_tsc - beg_tsc) / static_cast<double>(elapsed_ns.count()));
86 if (((i + 1) >= min_trials) && (((i + 1) % 2) != 0))
88 std::nth_element(rates.begin(), rates.begin() +
static_cast<ptrdiff_t
>((i + 1) / 2), rates.end());
89 double current_median = rates[(i + 1) / 2];
92 if (std::abs(current_median - previous_median) / current_median < convergence_threshold)
97 previous_median = current_median;
102 std::nth_element(rates.begin(), rates.begin() +
static_cast<ptrdiff_t
>(rates.size() / 2),
105 double const ticks_per_ns = rates[rates.size() / 2];
106 _ns_per_tick = 1 / ticks_per_ns;
109 double _ns_per_tick{0};
113 explicit RdtscClock(std::chrono::nanoseconds resync_interval)
114 : _ns_per_tick(RdtscTicks::instance().ns_per_tick())
116 double const calc_value =
static_cast<double>(resync_interval.count()) * _ns_per_tick;
119 if (calc_value >= static_cast<double>(std::numeric_limits<int64_t>::max()) || calc_value < 0)
121 _resync_interval_ticks = std::numeric_limits<int64_t>::max();
125 _resync_interval_ticks =
static_cast<int64_t
>(calc_value);
128 _resync_interval_original = _resync_interval_ticks;
135 std::fprintf(stderr,
"Failed to sync RdtscClock. Timestamps will be incorrect\n");
141 uint64_t time_since_epoch(uint64_t rdtsc_value)
const noexcept
146 auto const index = _version.load(std::memory_order_relaxed) & (_base.size() - 1);
149 auto diff =
static_cast<int64_t
>(rdtsc_value - _base[index].base_tsc);
152 if (diff > _resync_interval_ticks)
155 diff =
static_cast<int64_t
>(rdtsc_value - _base[index].base_tsc);
158 return static_cast<uint64_t
>(_base[index].base_time +
159 static_cast<int64_t
>(
static_cast<double>(diff) * _ns_per_tick));
163 uint64_t time_since_epoch_safe(uint64_t rdtsc_value)
const noexcept
172 version = _version.load(std::memory_order_acquire);
173 auto const index = version & (_base.size() - 1);
175 if (QUILL_UNLIKELY((_base[index].base_tsc) == 0 && (_base[index].base_time == 0)))
181 auto const diff =
static_cast<int64_t
>(rdtsc_value - _base[index].base_tsc);
182 wall_ts =
static_cast<uint64_t
>(_base[index].base_time +
183 static_cast<int64_t
>(
static_cast<double>(diff) * _ns_per_tick));
184 }
while (version != _version.load(std::memory_order_acquire));
190 bool resync(uint32_t lag)
const noexcept
193 constexpr uint8_t max_attempts{4};
195 for (uint8_t attempt = 0; attempt < max_attempts; ++attempt)
197 uint64_t
const beg =
rdtsc();
199 auto const wall_time =
static_cast<int64_t
>(detail::get_timestamp_ns<std::chrono::system_clock>());
200 uint64_t
const end =
rdtsc();
202 if (QUILL_LIKELY(end - beg <= lag))
205 auto const index = (_version.load(std::memory_order_relaxed) + 1) & (_base.size() - 1);
206 _base[index].base_time = wall_time;
207 _base[index].base_tsc = _fast_average(beg, end);
208 _version.fetch_add(1, std::memory_order_release);
210 _resync_interval_ticks = _resync_interval_original;
217 constexpr int64_t max_int64_half = std::numeric_limits<int64_t>::max() / 2;
218 if (_resync_interval_ticks <= max_int64_half)
220 _resync_interval_ticks = _resync_interval_ticks * 2;
227 double nanoseconds_per_tick()
const noexcept {
return _ns_per_tick; }
232 BaseTimeTsc() =
default;
233 int64_t base_time{0};
234 uint64_t base_tsc{0};
238 QUILL_NODISCARD
static uint64_t _fast_average(uint64_t x, uint64_t y) noexcept
240 return (x & y) + ((x ^ y) >> 1);
244 mutable int64_t _resync_interval_ticks{0};
245 int64_t _resync_interval_original{0};
246 double _ns_per_tick{0};
248 alignas(QUILL_CACHE_LINE_ALIGNED)
mutable std::atomic<uint32_t> _version{0};
249 mutable std::array<BaseTimeTsc, 2> _base{};
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
Converts tsc ticks to nanoseconds since epoch.
Definition: RdtscClock.h:30
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:36