27

毎秒数百のメッセージを送信する必要があり、各メッセージに時間フィールドが含まれる、非常に遅延の影響を受けやすいアプリケーションを構築しようとする際に、gettimeofday の最適化を検討したいと考えました。最初に考えたのは、rdtsc最適化に基づくものでした。何かご意見は ?他のポインターはありますか?返される時間値の必要な精度はミリ秒ですが、値が受信側と 1 ~ 2 ミリ秒ずれることがある場合は大した問題ではありません。gettimeofday にかかる 62 ナノ秒よりも優れた処理を試みる

4

5 に答える 5

63

POSIX クロック

POSIX クロック ソースのベンチマークを作成しました。

  • 時間 (秒) => 3 サイクル
  • ftime (ミリ秒) => 54 サイクル
  • gettimeofday (us) => 42 サイクル
  • clock_gettime (ns) => 9 サイクル (CLOCK_MONOTONIC_COARSE)
  • clock_gettime (ns) => 9 サイクル (CLOCK_REALTIME_COARSE)
  • clock_gettime (ns) => 42 サイクル (CLOCK_MONOTONIC)
  • clock_gettime (ns) => 42 サイクル (CLOCK_REALTIME)
  • clock_gettime (ns) => 173 サイクル (CLOCK_MONOTONIC_RAW)
  • clock_gettime (ns) => 179 サイクル (CLOCK_BOOTTIME)
  • clock_gettime (ns) => 349 サイクル (CLOCK_THREAD_CPUTIME_ID)
  • clock_gettime (ns) => 370 サイクル (CLOCK_PROCESS_CPUTIME_ID)
  • rdtsc (サイクル) => 24 サイクル

これらの数値は、Linux 4.0 上の Intel Core i7-4771 CPU @ 3.50GHz のものです。これらの測定値は、TSC レジスタを使用して、各クロック メソッドを何千回も実行し、最小コスト値を取得して取得されました。

これらの実装方法はハードウェアとカーネルのバージョンによって異なるため、実行する予定のマシンでテストする必要があります。コードはここにあります。これは、同じリポジトリ ( tsc.h ) にあるサイクル カウント用の TSC レジスタに依存しています。

TSC

TSC (プロセッサ タイムスタンプ カウンター) にアクセスすることは、物事を計る最も正確で安価な方法です。通常、これはカーネル自体が使用しているものです。TSC はコア間で同期され、周波​​数スケーリングの影響を受けないため、最新の Intel チップでも非常に簡単です。そのため、単純なグローバル タイム ソースを提供します。ここでの使用例と、アセンブリ コードのウォークスルーをここで見ることができます。

これに関する主な問題 (移植性以外) は、サイクルからナノ秒に移行する良い方法がないように思われることです。私が見つけることができる限り、インテルのドキュメントでは、TSC は固定周波数で動作することが示されていますが、この周波数はプロセッサが示す周波数とは異なる場合があります。Intel は、TSC 周波数を把握するための信頼できる方法を提供していないようです。Linux カーネルは、2 つのハードウェア タイマー間で発生する TSC サイクルの数をテストすることで、これを解決しているようです (こちらを参照)。

Memcached

Memcached は、わざわざキャッシュ メソッドを実行します。プラットフォーム全体でパフォーマンスをより予測可能にするため、または複数のコアでより適切にスケーリングするためだけかもしれません。また、価値のある最適化ではない場合もあります。

于 2012-10-27T03:14:51.070 に答える
49

gettimeofday実際にベンチマークを行い、許容できないほど遅いことがわかりましたか?

1 秒あたり 100 メッセージのレートでは、メッセージごとに 10 ミリ秒の CPU 時間があります。複数のコアがある場合、完全に並列化できると仮定すると、簡単に 4 ~ 6 倍に増やすことができます。これはメッセージあたり 40 ~ 60 ミリ秒です。gettimeofday のコストが 10 ミリ秒近くになる可能性はほとんどありません。1 ~ 10 マイクロ秒程度になると思われます (私のシステムでは、マイクロベンチマークで呼び出しごとに約 1 マイクロ秒かかります。自分で試してみてください)。最適化への取り組みは、別の場所に費やすほうがよいでしょう。

TSC を使用することは合理的な考えですが、最新の Linux には既にユーザー空間 TSC ベースの gettimeofdayがあります。可能であれば、vdso は gettimeofday の実装を取り込みますrdtsc。したがって、カーネルに入らずに時刻を計算します。ただし、一部の CPU モデルでは、異なるコアまたは異なるパッケージ間で TSC が同期されていないため、これが無効になる可能性があります。高性能のタイミングが必要な場合は、まず、同期された TSC を備えた CPU モデルを見つけることを検討してください。

つまり、かなりの量の解像度を犠牲にしても構わないと思っている場合 (タイミングは最後のティックまでしか正確ではないため、数十ミリ秒ずれている可能性があります)、CLOCK_MONOTONIC_COARSE または CLOCK_REALTIME_COARSEclock_gettimeと共に使用できます。これは vdso でも実装されており、カーネルを呼び出さないことが保証されています (最近のカーネルと glibc の場合)。

于 2011-06-27T21:18:17.157 に答える
4

bdonian が言うように、1 秒あたり数百のメッセージしか送信しない場合は、gettimeofday十分に高速になります。

ただし、1 秒あたり数百万のメッセージを送信している場合は、異なる可能性があります (ただし、それでもボトルネックであることを測定する必要があります)。その場合、次のようなことを検討することをお勧めします。

  • グローバル変数を持ち、現在のタイムスタンプを希望の精度で提供します
  • タイムスタンプを更新する以外は何もしない専用のバックグラウンド スレッドを用意します (タイムスタンプを T 単位時間ごとに更新する必要がある場合は、スレッドを T の一部だけスリープさせてからタイムスタンプを更新します。必要に応じてリアルタイム機能を使用します)。
  • 他のすべてのスレッド (または、他の方法でスレッドを使用しない場合はメイン プロセス) は、グローバル変数を読み取るだけです。

C 言語は、タイムスタンプ値が よりも大きい場合、その値を読み取れることを保証しませんsig_atomic_t。これに対処するためにロックを使用できますが、ロックは重いです。代わりに、volatile sig_atomic_t型付き変数を使用してタイムスタンプの配列にインデックスを付けることができます。バックグラウンド スレッドが配列内の次の要素を更新してから、インデックスを更新します。他のスレッドはインデックスを読み取り、次に配列を読み取ります。わずかに古いタイムスタンプを取得する可能性があります (ただし、次回は正しいタイムスタンプを取得します)。同時に更新され、古い値のいくつかのバイトと新しい値のいくつかを取得します。

しかし、これはすべて、1 秒あたりわずか数百のメッセージに対してはやり過ぎです。

于 2011-06-27T21:43:45.097 に答える
0

ミリ秒の精度が必要ですか?そうでない場合は、単にtime()UNIX タイムスタンプを使用して処理することができます。

于 2011-06-27T21:10:13.620 に答える