計算にCPU周波数をCとRDTSCは常に0を返します
-
26-09-2019 - |
質問
次のコードの抜粋を参照されたからインストラクターがつきますのでこの測定アルゴリズムの性能:
#include <stdio.h>
#include <unistd.h>
static unsigned cyc_hi = 0, cyc_lo = 0;
static void access_counter(unsigned *hi, unsigned *lo) {
asm("rdtsc; movl %%edx,%0; movl %%eax,%1"
: "=r" (*hi), "=r" (*lo)
: /* No input */
: "%edx", "%eax");
}
void start_counter() {
access_counter(&cyc_hi, &cyc_lo);
}
double get_counter() {
unsigned ncyc_hi, ncyc_lo, hi, lo, borrow;
double result;
access_counter(&ncyc_hi, &ncyc_lo);
lo = ncyc_lo - cyc_lo;
borrow = lo > ncyc_lo;
hi = ncyc_hi - cyc_hi - borrow;
result = (double) hi * (1 << 30) * 4 + lo;
return result;
}
しかしながらプレーする必要がありますコードする携帯型機械の異なるCPU周波数です。めるようにしている計算にCPU周波数のマシンのコード行われているようになります:
int main(void)
{
double c1, c2;
start_counter();
c1 = get_counter();
sleep(1);
c2 = get_counter();
printf("CPU Frequency: %.1f MHz\n", (c2-c1)/1E6);
printf("CPU Frequency: %.1f GHz\n", (c2-c1)/1E9);
return 0;
}
この結果は常に0できますかはいかがでしょうか。私は走Linux(アーチ)のお客様がごVMware.
友達の機(MacBookできることを期待しています。一;だって、その結果、0よりも大きな変数のCPU周波数が固定されていない(したものであることができないんです。彼は別の機械ではLinux(Ubuntuとしてホストおよび報告をするため0になります。この規則はこの問題を解決の仮想マシンは、と思ったのです。
そのアイデアはなぜことが起きているのは、どうすればよいですか?
解決
さて、他の回答は役に立ちませんでしたから、私はより詳細に説明しよう。問題は、現代のCPUは、順不同で命令を実行することができるということです。
:あなたのコードのようなものとして開始しますrdtsc
push 1
call sleep
rdtsc
現代のCPUが行うのないの必然しかし、元の順序で命令を実行します。
:オリジナルのオーダーにもかかわらず、CPUは(ほとんど)同じようにそれを実行して自由ですrdtsc
rdtsc
push 1
call sleep
2 rdtsc
sの違いはそれを防ぐために(少なくとも非常に近い)は0になり、なぜこの場合、それは明らかだ、あなたは命令を実行する必要があり、そのCPUはのことはありませんの再配置順不同で実行します。そのために使用する最も一般的な指示がCPUID
です。私がリンクされ、他の答えは(メモリが提供する場合)、このタスクのために正しく/効果的CPUID
を使用するために必要な手順については、そこから大体開始する必要があります。
もちろん、それはティム・ポストが正しかったことは可能ですし、あなたしているのものための仮想マシンの問題を見て。それが今立っているとしてそれにもかかわらず、あなたのコードでも、実際のハードウェア上で正しく動作するという保証はありません。
編集:コードのでしょうが、の仕事理由として:説明書がは順不同で実行できることだけでなく、まず、事実を保証するものではありません彼らその< em>のでしょうのこと。第二には、他の人がいない間、それはsleep
(の少なくとも一部の実装)いる可能性があります、その周りに再配置されることからrdtsc
を防ぐシリアル化命令を含む(またはそれらを含むが、唯一の特定の(しかし未指定)の状況下では、それらを実行することもできます)。
あなたは何をして残されているのは、ほぼすべて再コンパイルして、あるいは単に一度の実行と、次の間で変更することができます動作です。これは、行の時間の非常に正確な結果の数十を生成し、いくつかの(ほぼ)完全に原因不明の理由(完全に他のプロセスで起こっ例えば、何か)。
のために失敗する可能性が他のヒント
私は正確にあなたのコードに問題があるが、あなたはこのような単純な命令のために不必要な作業のビットをかなりやっていることを確実に言うことはできません。私はあなたのrdtsc
コードが実質的に簡素化をお勧めします。あなたはあなたの自己を運ぶ64ビット演算を行う必要はありません、あなたは、二重のように、その操作の結果を格納する必要はありません。あなたはEAXとEDX使用するGCCを伝えることができ、あなたのインラインアセンブラで別々の出力を使用する必要はありません。
ここでは、このコードを大幅に簡略化バージョンである
#include <stdint.h>
uint64_t rdtsc() {
uint64_t ret;
# if __WORDSIZE == 64
asm ("rdtsc; shl $32, %%rdx; or %%rdx, %%rax;"
: "=A"(ret)
: /* no input */
: "%edx"
);
#else
asm ("rdtsc"
: "=A"(ret)
);
#endif
return ret;
}
また、あなたは0、または何か他のものを取得しているかどうかを確認することができますので、あなたはしているが、このから抜け出す値をプリントアウトを検討してくださいます。
忘れによる利用 volatile
おasm算書, で伝えるコンパイラの asm
声の出力を毎回のように純粋な機能です。(volatile
は暗黙のための asm
諸表のとします。<br></p><)
そこでいただけます 正確に ゼロ:コンパイラの最適化 end-start
へ 0
コンパイル時に、総合化学院(共subexpression消去).
自分の回答 得CPUサイクルカウント? のための __rdtsc()
本来、@Mysticialの回答がありますが、GNU Cのインラインasm、私の見積りはこちら
// prefer using the __rdtsc() intrinsic instead of inline asm at all. uint64_t rdtsc(){ unsigned int lo,hi; __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); return ((uint64_t)hi << 32) | lo; }
この正常に動作しない問題を効率的に32ビット版および64ビットのコードです。
うーん、私は肯定的ではないが、私はこの問題は、この線の内側のかもしれ疑います:
結果=(ダブル)HI *(1 << 30)* 4 + LO;
あなたが安全に、「符号なし」にこのような巨大な乗算を実行することができれば、私は不審なよ...その多くの場合、32ビットの数値ではないでしょうか? ...あなたは安全に乗算2 ^ 32によると、すでにこの可能性のヒントは...あなたがする必要があるかもしれません最後に2 ^ 30に追加余分な「* 4」としてそれを追加しなければならなかったことができなかったということだけで事実各副成分HIとLOは、(代わりに最後に単一のものの)二重に変換し、2倍
を使用して乗算を行います