私はCPUでこのテストを実行していconstant_tsc
ますnonstop_tsc
$ grep -m 1 ^flags /proc/cpuinfo | sed 's/ /\n/g' | egrep "constant_tsc|nonstop_tsc"
constant_tsc
nonstop_tsc
ステップ 1: tsc のティック レートを計算します。
_ticks_per_ns
多数の観測値の中央値として計算します。rdtscp
順序どおりの実行を保証するために使用します。
static const int trials = 13;
std::array<double, trials> rates;
for (int i = 0; i < trials; ++i)
{
timespec beg_ts, end_ts;
uint64_t beg_tsc, end_tsc;
clock_gettime(CLOCK_MONOTONIC, &beg_ts);
beg_tsc = rdtscp();
uint64_t elapsed_ns;
do
{
clock_gettime(CLOCK_MONOTONIC, &end_ts);
end_tsc = rdtscp();
elapsed_ns = to_ns(end_ts - beg_ts); // calculates ns between two timespecs
}
while (elapsed_ns < 10 * 1e6); // busy spin for 10ms
rates[i] = (double)(end_tsc - beg_tsc) / (double)elapsed_ns;
}
std::nth_element(rates.begin(), rates.begin() + trials/2, rates.end());
_ticks_per_ns = rates[trials/2];
ステップ 2:壁時計の開始時刻と tsc を計算する
uint64_t beg, end;
timespec ts;
// loop to ensure we aren't interrupted between the two tsc reads
while (1)
{
beg = rdtscp();
clock_gettime(CLOCK_REALTIME, &ts);
end = rdtscp();
if ((end - beg) <= 2000) // max ticks per clock call
break;
}
_start_tsc = end;
_start_clock_time = to_ns(ts); // converts timespec to ns since epoch
ステップ 3: tsc からウォール クロック時間を返すことができる関数を作成する
uint64_t tsc_to_ns(uint64_t tsc)
{
int64_t diff = tsc - _start_tsc;
return _start_clock_time + (diff / _ticks_per_ns);
}
ステップ 4:ループで実行し、壁時計の時間を表示clock_gettime
しますrdtscp
// lock the test to a single core
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(6, &mask);
sched_setaffinity(0, sizeof(cpu_set_t), &mask);
while (1)
{
timespec utc_now;
clock_gettime(CLOCK_REALTIME, &utc_now);
uint64_t utc_ns = to_ns(utc_now);
uint64_t tsc_ns = tsc_to_ns(rdtscp());
uint64_t ns_diff = tsc_ns - utc_ns;
std::cout << "clock_gettime " << ns_to_str(utc_ns) << '\n';
std::cout << "tsc_time " << ns_to_str(tsc_ns) << " diff=" << ns_diff << "ns\n";
sleep(10);
}
出力:
clock_gettime 11:55:34.824419837 tsc_time 11:55:34.824419840 diff=3ns clock_gettime 11:55:44.826260245 tsc_time 11:55:44.826260736 diff=491ns clock_gettime 11:55:54.826516358 tsc_time 11:55:54.826517248 diff=890ns clock_gettime 11:56:04.826683578 tsc_time 11:56:04.826684672 diff=1094ns clock_gettime 11:56:14.826853056 tsc_time 11:56:14.826854656 diff=1600ns clock_gettime 11:56:24.827013478 tsc_time 11:56:24.827015424 diff=1946ns
質問:
これら 2 つの方法で計算された時間が急速にずれていることはすぐにわかります。
私は、tsc レートが一定であるconstant_tsc
と仮定しています。nonstop_tsc
これは漂流中の船内時計ですか?このままではドリフトしないのではないでしょうか?
このドリフトの原因は何ですか?
それらを同期させるためにできることはありますか (非常に頻繁に再計算すること
_start_tsc
と_start_clock_time
ステップ 2 を除く)。