私は現在、オペレーティングシステムの操作のオーバーヘッドに取り組んでいます。システムコールのコスト
私は実際にシステムコールを行うためのコストを勉強しており、それを観察するための簡単なC++プログラムを開発しました。
#include <iostream>
#include <unistd.h>
#include <sys/time.h>
uint64_t
rdtscp(void) {
uint32_t eax, edx;
__asm__ __volatile__("rdtscp" //! rdtscp instruction
: "+a" (eax), "=d" (edx) //! output
: //! input
: "%ecx"); //! registers
return (((uint64_t)edx << 32) | eax);
}
int main(void) {
uint64_t before;
uint64_t after;
struct timeval t;
for (unsigned int i = 0; i < 10; ++i) {
before = rdtscp();
gettimeofday(&t, NULL);
after = rdtscp();
std::cout << after - before << std::endl;
std::cout << t.tv_usec << std::endl;
}
return 0;
}
このプログラムは非常に簡単です。
- rdtscp関数は、RTDSCP命令(64ビットサイクルカウントを2つの32ビットレジスタにロードするプロセッサ命令)を呼び出すための単なるラッパーです。この機能は、タイミングを取るために使用されます。
- 私は10回反復します。それぞれの反復で、私はgettimeofdayを呼び出し、それを実行するのに要した時間を(CPUサイクルの数として)決定します。
結果は全く予想外である:出力
8984
64008
376
64049
164
64053
160
64056
160
64060
160
64063
164
64067
160
64070
160
64073
160
64077
奇数ラインシステムコールを実行するために必要なサイクル数です。偶数行は、t.tv_usec(私が勉強しているシステムコールであるgettimeofdayによって設定される)の値を含んでいます。
私は、それが可能であることを本当に理解していません。サイクル数は、ほぼ10,000から約150に大幅に減少します。しかし、timeval構造体は各呼び出しで更新されます!
私は別のオペレーティングシステム(debianとmacos)で試してみたところ、結果は似ています。
キャッシュを使用しても、どのように可能かはわかりません。システムコールを作成すると、コンテキスト切り替えがユーザーモードからカーネルモードに切り替わる必要があり、時刻を更新するためにはクロックを読み取る必要があります。
誰かがアイデアを持っていますか?
私は答えは分かりませんが、キャッシングがそれを説明しない理由は何故思いますか? – Pemdas
これは、ダイナミックリンカによるシンボル解決の遅延(レイジーバインディング)になります。実行時に 'LD_BIND_NOW = 1'(Linux)または' DYLD_BIND_AT_LAUNCH = 1'(MacOS)でリンカにすべてのシンボルを解決させてください。 'gettimeofday()'に対する最初の呼び出しのサイクル数は大幅に減少するはずです。 – xbug
良い考えですが、それは大幅に低下していますが、最初のコール(〜770サイクルは最初のコールでカウントされ、まだ150〜 –