Skip to content

Commit 9959b98

Browse files
committed
adaptive termination in rdtsc calibration
1 parent 93fc90a commit 9959b98

File tree

2 files changed

+33
-8
lines changed

2 files changed

+33
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@
208208
- On Linux, setting a long backend thread name now truncates it instead of
209209
failing. ([#691](https://github.com/odygrd/quill/issues/691))
210210
- Fixed BSD builds. ([#688](https://github.com/odygrd/quill/issues/688))
211+
- Added adaptive termination for stable measurements in `rdtsc` calibration during init
211212
- Fixed `QUILL_ATTRIBUTE_HOT` and `QUILL_ATTRIBUTE_COLD` clang detection
212213
- CMake improvements: switched to range syntax for minimum required version and bumped minimum required CMake version to
213214
`3.12`. ([#686](https://github.com/odygrd/quill/issues/686))

include/quill/backend/RdtscClock.h

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <chrono>
1818
#include <cstdint>
1919
#include <cstdio>
20+
#include <vector>
2021

2122
QUILL_BEGIN_NAMESPACE
2223

@@ -52,13 +53,19 @@ class RdtscClock
5253
// Convert rdtsc to wall time.
5354
// 1. Get real time and rdtsc current count
5455
// 2. Calculate how many rdtsc ticks can occur in one
55-
// calculate _ticks_per_ns as the median over a number of observations.
56+
// calculate _ticks_per_ns as the median over a number of observations
57+
// we use always odd number of trials for easy median calc
5658
constexpr std::chrono::milliseconds spin_duration = std::chrono::milliseconds{10};
59+
constexpr size_t max_trials = 15;
60+
constexpr size_t min_trials = 3;
61+
constexpr double convergence_threshold = 0.01; // 1% threshold
5762

58-
constexpr int trials = 13;
59-
std::array<double, trials> rates = {{0}};
63+
std::vector<double> rates;
64+
rates.reserve(max_trials);
6065

61-
for (size_t i = 0; i < trials; ++i)
66+
double previous_median = 0.0;
67+
68+
for (size_t i = 0; i < max_trials; ++i)
6269
{
6370
auto const beg_ts = detail::get_timestamp<std::chrono::steady_clock>();
6471
uint64_t const beg_tsc = rdtsc();
@@ -70,14 +77,31 @@ class RdtscClock
7077
auto const end_ts = detail::get_timestamp<std::chrono::steady_clock>();
7178
end_tsc = rdtsc();
7279
elapsed_ns = end_ts - beg_ts;
73-
} while (elapsed_ns < spin_duration); // busy spin for 10ms
80+
} while (elapsed_ns < spin_duration);
81+
82+
rates.push_back(static_cast<double>(end_tsc - beg_tsc) / static_cast<double>(elapsed_ns.count()));
83+
84+
// Check for convergence after minimum trials and only on an odd count of trials.
85+
if (((i + 1) >= min_trials) && (((i + 1) % 2) != 0))
86+
{
87+
std::nth_element(rates.begin(), rates.begin() + static_cast<ptrdiff_t>((i + 1) / 2), rates.end());
88+
double current_median = rates[(i + 1) / 2];
89+
90+
// If we've converged, break early
91+
if (std::abs(current_median - previous_median) / current_median < convergence_threshold)
92+
{
93+
break;
94+
}
7495

75-
rates[i] = static_cast<double>(end_tsc - beg_tsc) / static_cast<double>(elapsed_ns.count());
96+
previous_median = current_median;
97+
}
7698
}
7799

78-
std::nth_element(rates.begin(), rates.begin() + trials / 2, rates.end());
100+
// Calculate final median.
101+
std::nth_element(rates.begin(), rates.begin() + static_cast<ptrdiff_t>(rates.size() / 2),
102+
rates.end());
79103

80-
double const ticks_per_ns = rates[trials / 2];
104+
double const ticks_per_ns = rates[rates.size() / 2];
81105
_ns_per_tick = 1 / ticks_per_ns;
82106
}
83107

0 commit comments

Comments
 (0)