2016-10-08 28 views
7

コードブロックの実行時間を決定しようとするコードがあります。C言語でのclock()関数の精度

#include <time.h> 
#include <stdio.h> 

int main() 
{ 
    clock_t start_t, end_t, total_t; 
    int i; 

    start_t = clock(); //clock start 
    printf("Starting of the program, start_t = %ld\n", start_t); 

    printf("Going to scan a big loop, start_t = %ld\n", start_t); 
    for(i=0; i< 10000000; i++) //trying to determine execution time of this block 
    { 
    } 
    end_t = clock(); //clock stopped 
    printf("End of the big loop, end_t = %ld\n", end_t); 

    total_t = (long int)(end_t - start_t); 
    printf("Total time taken by CPU: %lu\n", total_t ); 

    return(0); 
} 

私のマシン上のコードスニペットの出力は

Starting of the program, start_t = 8965 
Going to scan a big loop, start_t = 8965 
End of the big loop, end_t = 27259 
Total time taken by CPU: 18294 

である私のCPUは21 MHzで動作し、これが実行さばかりだけのものだったと仮定したのであれば、各マシンサイクルは約になります47ナノ秒に等しいので(18294 * 47)= 859818ナノ秒。

これはコード内のforループの実行時間ですか?ここで間違った仮定をしていますか?

+0

時間を秒単位で取得するには、数値を分割する必要があります。 'total_t'を' CLOCKS_PER_SEC'で置き換えてください。 'total_t'を浮動小数点値にキャストして動作させる必要があることに注意してください。 –

+1

また、名前付けスキームの小さなニックピッキング: '_t'という接尾辞で終わるシンボルは、通常、(' typdef'で作成された)型別名に使用されます。たとえば 'size_t'や' time_t'や 'clock_t'などです。 –

+0

@JoachimPileborg clock()関数のドキュメントを見直し、CLOCK_PER_SECは1/100th秒まで正確な時刻を返し、10マイクロ秒までの分解能を探しています。 CLOCKS_PER_SECはアーキテクチャによって変更されるため、異なるプラットフォームやアーキテクチャで動作するようにしたいので、違いを計算してクロックスピードに乗算する方が良い方法です。 – user2808264

答えて

4

clock関数で使用される時間の単位は任意です。ほとんどのプラットフォームでは、プロセッサの速度とは無関係です。より一般的には、ソフトウェアで設定可能な外部タイマー割り込みの頻度や、何年ものプロセッサの進化によって互換性を維持していた履歴値に関連しています。リアルタイムに変換するには、マクロCLOCKS_PER_SECを使用する必要があります。

printf("Total time taken by CPU: %fs\n", (double)total_t/CLOCKS_PER_SEC); 

C標準ライブラリは内部タイマーを持っていると時間を伝えるために、外部周辺機器に依存していないプロセッサを含む、ハードウェアの広い範囲で実施可能なように設計されました。多くのプラットフォームでは、timeよりも正確に壁時計の時間を測定する方法と、正確な方法でCPU消費を測定する方法がclockよりも多くなっています。たとえばPOSIXシステム(Linuxや他のUnixライクなシステム)では、マイクロ秒の精度を持つgetrusageを使用することができます。利用可能な

struct timeval start, end; 
struct rusage usage; 
getrusage(RUSAGE_SELF, &usage); 
start = usage.ru_utime; 
… 
getrusage(RUSAGE_SELF, &usage); 
end = usage.ru_utime; 
printf("Total time taken by CPU: %fs\n", (double)(end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec)/1e-6); 

場合、clock_gettime(CLOCK_THREAD_CPUTIME_ID)またはclock_gettime(CLOCK_PROCESS_CPUTIME_ID)より良い精度を与えることができます。ナノ秒の精度を持ちます。

精度と精度の違いに注意してください。精度は、値が報告される単位です。正確さは、報告された値が実際の値にどのくらい近いかを示します。 real-time systemで作業している場合を除き、測定機能自体の呼び出しを含めて、コードの長さに関する厳しい保証はありません。

一部のプロセッサでは、サイクルのクロックがあり、壁時計の時刻ではなくプロセッササイクルをカウントしますが、これはシステム固有のものです。

ベンチマークを作成するときは、この特定の状況でこの特定のCPU上でこの特定の実行可能ファイルを実行することが重要であることに注意してください。結果は他の状況で一般化される場合もあります。たとえば、あなたの質問の空のループは、最適化をオフにしない限り、ほとんどのコンパイラによって最適化されます。最適化されていないコードの速度を測定するのは通常無意味です。実際の作業をループに追加しても、おもちゃのベンチマークには注意してください。実際のコードと同じパフォーマンス特性を持っていないことがよくあります。 PCやスマートフォンなどの最新のハイエンドCPUでは、CPUを大量に使用するコードのベンチマークはしばしばキャッシュの影響を受けやすく、その結果はシステム上で実行されているもの、正確なCPUモデル(異なるキャッシュのサイズとレイアウトなど)、コードがロードされるアドレスなどに依存します。

+0

@ Gilesこれは私が必要としていたものです。 100 msの分解能を持つクロック機能と比較して、1 usまでの分解能を備えています。しかし、このコードが移植可能かどうかは分かりますか?私はこれをARM M0システム上で実行する必要があります。このコードを移植可能にする方法はありますか? – user2808264

+0

@ user2808264 'clock'を超えて何かが必要な場合は、移植性がありません。オペレーティングシステムまたはCPU、またはその両方に依存関係を作成します。お使いのOSが提供するものを確認してください。ベアメタルで動作させている場合は、精度が1μs近く必要な場合は、サイクル精度のカウンタが必要です。CPUに搭載されているデバッグ機能を確認してください(オプション機能だと思います)。それほど正確でない場合は、オプションであるが広く普及している[システムタイマー](http://sushihangover.github.io/cortex-m-systick-polling-vs-interrupts/)を使用することができます。 – Gilles