rdtscp を使う

x86マシンでは、処理時間を計測する時、rdtsc 命令を使うと便利な場合があります。

しかし、rdtsc で取得できるタイムスタンプ値は、コア毎に同期されておらず、マザーボードやBIOSによっては、大きな差が出ることがあります。

例えば、手元のマシンは、GA-X79-UP4というマザーボードなのですが、

https://bitbucket.org/fixstars/blog/src/b51c91c278eb55dbe3e592ea957ea29015779ad8/core-tsc.c?at=master

というようなプログラムを使って、コアごとのtsc値を出力すると、

と、なって、0番のコアだけ大きく値がずれていることがわかります。

小さいプログラムを計測する場合は、それほど問題にはならないかもしれませんが、コンテキストスイッチが入るぐらい大きなプログラムで時間計測する場合、この差が問題になる場合があります。

rdtscp 命令を使うと、この問題に対していくらか対策することができます。

rdtscp 命令は、tsc と同時に、IA32_TSC_AUX MSR に書かれている値を ECX にストアします。Linuxでは、IA32_TSC_AUX MSR には

http://lxr.free-electrons.com/source/arch/x86/kernel/vsyscall_64.c#L290

node<<12 | cpu という値が書かれているようです。

これを使って、

  1. 最初にコア毎のtsc値を取得
  2. tscの値は、この最初に取得した値との差分を使う

とすることで、どのコアでtscを取得しても、大きくずれることのない値を使うことができます。

rdtscpを使用したtsc取得のサンプルを以下に置いておきます。(NUMA環境では動きません)

https://bitbucket.org/fixstars/blog/src/d6883fea41b58666cee3d72f8a9fd82a60794d68/rdtscp.c?at=master

ただし、この方法ではcalibrate時に、OSのスケジューラがうまく動かなかった場合に、適切な値が取得できません。何度も動かせる場合は正しいらしい値が取れるまで繰り返せばよいですが、常に正しい値が必要な場合はこの方法は使えません。次回は、この問題に対応するため、Linux上で、x86のCPU_CLK_UNHALTED を読む方法について紹介するかもしれません。

また、VM上の環境ではrdtscpは使えないことがあります。注意しましょう。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です