2009-03-12 5 views
1

Cの関数呼び出し中に経過した時間を1ナノ秒の精度で計算したい。C言語の関数呼び出しで経過時間を見つけるタイマー

Cでタイマー機能が利用できますか?

「はい」の場合は、サンプルのコードスニペットを入力してください。

擬似コード

Timer.Start() 
foo(); 
Timer.Stop() 
Display time elapsed in execution of foo() 

環境の詳細: - はどれタイマー機能は特に、プラットフォーム固有でなければならないとしているRHELのマシン上

答えて

2

をGCC 3.4コンパイラを使用してその精度要件を満たしている。

POSIXシステムの標準ソリューションはgettimeofday()ですが、マイクロ秒の精度しかありません。

これがパフォーマンスベンチマークのためのものである場合、標準的な方法は、テスト中のコードに精度要件を厳しくするのに十分な時間がかかるようにすることです。つまり、テストコードを1秒以上実行します。

1

1ナノ秒の精度を保証するタイマーがcにありません。 clock()以上を調べることをお勧めします。POSIX gettimeofday()

+1

ありANSIにはタイマーは、はい、ありませんが、POSIX2001は、指定し 'にclock_gettime()' –

+0

ので、あなたが言って何を私の答えについて不正確なものは何もなかったことが、それはほかに何かを言及している可能性があることですか?それはダウンボトムの非常に薄い理由です。 –

0

システムクロックの解像度に依存する1ナノ秒の解像度を提供するタイマがあるかどうかはわかりませんが、しかしhttp://code.google.com/p/high-resolution-timer/を見たいかもしれません。彼らは、ほとんどのLinuxシステムではマイクロ秒レベル、Sunシステムではナノ秒の解像度を提供できることを示しています。

4

Intelおよび互換プロセッサでは、Cコードのasm()ブロックに簡単にラップすることができるrdtsc命令を使用できます。各サイクルでインクリメントするビルトインプロセッササイクルカウンタの値を返します。あなたは高い解像度を得て、そのようなタイミングは非常に速いです。

このインクリメントがどれほど速いかを知るには、キャリブレーションが必要です。この命令は、5秒間などの一定の時間間隔で2回コールします。周波数を低消費電力にシフトするプロセッサでこれを行うと、較正に問題が生じる可能性があります。

0

このスケールでベンチマークを行うことはお勧めできません。時間を最小限に抑えるためのオーバーヘッドがあるため、ナノ秒で作業すると結果が信頼できなくなる可能性があります。あなたのプラットフォームのシステムコール、またはboost::Date_Timeをより大きいスケール[優先]で使用することができます。

5

使用しているプロセッサの種類を確認できますか? x86プロセッサを使用している場合、タイムスタンプカウンタ(tsc)を見ることができます。このコードスニペット:

#define rdtsc(low,high) \ 
    __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) 

は、CPUがそれぞれlowhighで実行されたサイクル数を入れますが(それは2つのlongのを期待し、あなたがlong long intに結果を格納することができます)、次のように:

inline void getcycles (long long int * cycles) 
{ 
    unsigned long low; 
    long high; 
    rdtsc(low,high); 
    *cycles = high; 
    *cycles <<= 32; 
    *cycles |= low; 
} 

これは、CPUが実行したサイクル数を返します。あなたは、あなたのCPU速度を取得し、nsの数を得るためにnsごとにいくつのサイクルを把握する必要があります。

これを行うには、私は/proc/cpuinfoから "cpu MHz"の文字列を解析し、10進数に変換しました。その後は数学的なもので、1MHz = 1,000,000サイクル/秒、10億ns /秒ということを覚えておいてください。

+0

これは、私がその目的のために働く場所で使用するものです。 –

0

あなたはそれを10^9回実行してストップウォッチできますか?

+0

私はそれがdrive-by downvoteを得ることを知っていました。あまりにも単純です。 –

+0

実際には、それはピンチで悪い考えではありません。問題は、毎回キャッシュに物事を再取得する必要がないため、あなたが一度だけ時間を計らせることができれば、あなたの答えがはるかに速くなる可能性があるということです。 –

+0

@ T.E.D:ええ、その問題があります。あらゆる種類のタイミング測定を混乱させます。つまり、ウォーミングアップの前または後に時間を測定したいですか? –

4

clock_gettime(3)を使用してください。詳細については、man 3 clock_gettimeと入力してください。つまり、ナノ秒の精度はめったに必要ありません。

0

CPU時間が100%になることが確実な場合は、gettimeofdayなどの標準システムコールを使用できます。 foo()を実行している間に、他のスレッドやプロセスがCPU時間を奪うことがある多くの状況を考えることができます。

0

あなたはこのようにできないことを求めています。そのレベルの精度に到達するにはHWレベルのサポートが必要であり、さらには非常に注意深く変数を制御する必要があります。コードの実行中に割り込みが発生した場合はどうなりますか? OSがコードの一部を実行することを決定したらどうなるでしょうか?

あなたのコードは何をしていますか? RAMメモリを使用していますか?あなたのコードやデータがキャッシュにあるかどうかはどうでしょうか?

一部の環境では、これらの変数を制御する場合は、このジョブに対してHWレベルカウンタを使用できます。しかし、Linuxのコンテキストスイッチをどうやって防ぐのですか?

たとえば、テキサスインスツルメンツのDSPツール(Code Composer Studio)では、デバッグ環境全体がエミュレータ(たとえばBlackhawk)が実行されるすべての操作に関する情報を受け取るように設定されているため、コードを非常に正確にプロファイルできます。一部のプロセッサでは、チップ内部のHWブロックに直接コーディングされたウォッチポイントを設定することもできます。これは、メモリレーンもこのデバッグブロックにルーティングされるために機能します。

CSL(チップサポートライブラリ)には、タイミングオーバーヘッドが数サイクルかかることが求められる機能があります。しかし、これはそのプロセッサーでのみ使用可能であり、HWレジスターからのタイマー値の読み取りに完全に依存しています。

0

私たちはすべてこのテストサンプルを作り直す時間を無駄にします。準備が整ったものを投稿するのはなぜですか?とにかく、ここに私の結果があります。

CLOCK_PROCESS_CPUTIME_ID resolution: 0 sec 1 nano 
clock_gettime 4194304 iterations : 459.427311 msec 0.110 microsec/call 
CLOCK_MONOTONIC resolution: 0 sec 1 nano 
clock_gettime 4194304 iterations : 64.498347 msec 0.015 microsec/call 
CLOCK_REALTIME resolution: 0 sec 1 nano 
clock_gettime 4194304 iterations : 65.494828 msec 0.016 microsec/call 
CLOCK_THREAD_CPUTIME_ID resolution: 0 sec 1 nano 
clock_gettime 4194304 iterations : 427.133157 msec 0.102 microsec/call 
rdtsc 4194304 iterations : 115.427895 msec 0.028 microsec/call 
Dummy 16110479703957395943 
rdtsc in milliseconds 4194304 iterations : 197.259866 msec 0.047 microsec/call 
Dummy 4.84682e+08 UltraHRTimerMs 197 HRTimerMs 197.26 

#include <time.h> 
#include <cstdio> 
#include <string> 
#include <iostream> 
#include <chrono> 
#include <thread> 

enum { TESTRUNS = 1024*1024*4 }; 

class HRCounter 
{ 
private: 
    timespec start, tmp; 
public: 
    HRCounter(bool init = true) 
    { 
     if(init) 
      SetStart(); 
    } 

    void SetStart() 
    { 
     clock_gettime(CLOCK_MONOTONIC, &start); 
    } 

    double GetElapsedMs() 
    { 
     clock_gettime(CLOCK_MONOTONIC, &tmp); 
     return (double)(tmp.tv_nsec - start.tv_nsec)/1000000 + (tmp.tv_sec - start.tv_sec) * 1000; 
    } 
}; 

__inline__ uint64_t rdtsc(void) { 
    uint32_t lo, hi; 
    __asm__ __volatile__ (  // serialize 
    "xorl %%eax,%%eax \n  cpuid" 
    ::: "%rax", "%rbx", "%rcx", "%rdx"); 
    /* We cannot use "=A", since this would use %rax on x86_64 and return only the lower 32bits of the TSC */ 
    __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); 
    return (uint64_t)hi << 32 | lo; 
} 

inline uint64_t GetCyclesPerMillisecondImpl() 
{ 
    uint64_t start_cyles = rdtsc(); 
    HRCounter counter; 
    std::this_thread::sleep_for (std::chrono::seconds(3)); 
    uint64_t end_cyles = rdtsc(); 
    double elapsed_ms = counter.GetElapsedMs(); 
    return (end_cyles - start_cyles)/elapsed_ms; 
} 

inline uint64_t GetCyclesPerMillisecond() 
{ 
    static uint64_t cycles_in_millisecond = GetCyclesPerMillisecondImpl(); 
    return cycles_in_millisecond; 
} 

class UltraHRCounter 
{ 
private: 
    uint64_t start_cyles; 
public: 
    UltraHRCounter(bool init = true) 
    { 
     GetCyclesPerMillisecond(); 
     if(init) 
      SetStart(); 
    } 

    void SetStart() { start_cyles = rdtsc(); } 

    double GetElapsedMs() 
    { 
     uint64_t end_cyles = rdtsc(); 
     return (end_cyles - start_cyles)/GetCyclesPerMillisecond(); 
    } 
}; 

int main() 
{ 
    auto Run = [](std::string const& clock_name, clockid_t clock_id) 
    { 
     HRCounter counter(false); 
     timespec spec; 
     clock_getres(clock_id, &spec); 
     printf("%s resolution: %ld sec %ld nano\n", clock_name.c_str(), spec.tv_sec, spec.tv_nsec); 
     counter.SetStart(); 
     for (int i = 0 ; i < TESTRUNS ; ++ i) 
     { 
      clock_gettime(clock_id, &spec); 
     } 
     double fb = counter.GetElapsedMs(); 
     printf("clock_gettime %d iterations : %.6f msec %.3f microsec/call\n", TESTRUNS, (fb), ((fb) * 1000)/TESTRUNS); 
    }; 

    Run("CLOCK_PROCESS_CPUTIME_ID",CLOCK_PROCESS_CPUTIME_ID); 
    Run("CLOCK_MONOTONIC",CLOCK_MONOTONIC); 
    Run("CLOCK_REALTIME",CLOCK_REALTIME); 
    Run("CLOCK_THREAD_CPUTIME_ID",CLOCK_THREAD_CPUTIME_ID); 

    { 
     HRCounter counter(false); 
     uint64_t dummy; 
     counter.SetStart(); 
     for (int i = 0 ; i < TESTRUNS ; ++ i) 
     { 
      dummy += rdtsc(); 
     } 
     double fb = counter.GetElapsedMs(); 
     printf("rdtsc %d iterations : %.6f msec %.3f microsec/call\n", TESTRUNS, (fb), ((fb) * 1000)/TESTRUNS); 
     std::cout << "Dummy " << dummy << std::endl; 
    } 

    { 
     double dummy; 
     UltraHRCounter ultra_hr_counter; 
     HRCounter counter; 
     for (int i = 0 ; i < TESTRUNS ; ++ i) 
     { 
      dummy += ultra_hr_counter.GetElapsedMs(); 
     } 
     double fb = counter.GetElapsedMs(); 
     double final = ultra_hr_counter.GetElapsedMs(); 
     printf("rdtsc in milliseconds %d iterations : %.6f msec %.3f microsec/call\n", TESTRUNS, (fb), ((fb) * 1000)/TESTRUNS); 
     std::cout << "Dummy " << dummy << " UltraHRTimerMs " << final << " HRTimerMs " << fb << std::endl; 
    } 



    return 0; 
} 
関連する問題