コードブロックのN倍の実行時間を測定する問題
-
26-09-2019 - |
質問
編集: あらゆる詳細を説明するこの長い投稿を書いた後、問題を発見しました...誰かが私が間違っていることと、実行時間を秒単位で取得する方法(小数点以下 5 桁程度の浮動小数点数を使用)について適切な答えをくれたら、それを承認済みとしてマークします。ヒント:問題は、クロック_getttime() のマニュアル ページをどのように解釈したかにありました。
こんにちは、
という名前の関数があるとします。 myOperation
実行時間を測定する必要があります。それを測定するために私が使用しているのは、 clock_gettime()
勧められたので ここ コメントの1つで。
先生は私たちにそれを測ることを勧めます N
最終レポートの平均、標準偏差、中央値を取得できるようにします。彼はまた、私たちに実行することを勧めています myOperation
M
1 回だけではなく、何度も。もし myOperation
非常に高速な操作であり、それを測定します M
時間により、所要時間の「リアルタイム」を把握できます。使用されているクロックがそのような動作を測定するのに必要な精度を備えていない可能性があります。それで、実行 myOperation
1回だけ、または M
実際の時間は、使用しているクロック精度に対して操作自体に十分な時間がかかるかどうかによって異なります。
それに対処するのに困っています M
実行回数。増加中 M
最終的な平均値を(大幅に)減少させます。それは私には意味がありません。こんな感じで、A地点からB地点まで移動するのに平均3~5秒かかります。しかし、A から B へ、そして A へ戻ることを 5 回行い (A から B は B から A と同じであるため、10 回になります)、それを測定します。10 で割ると、得られる平均は、地点 A から B までの移動にかかる平均 (3 ~ 5 秒) と同じになるはずです。
これは私のコードで実行したいことですが、機能しません。A から B に移動し、A に戻る回数を増やし続けると、そのたびに平均がどんどん低くなり、意味がありません。
理論は十分です。これが私のコードです。
#include <stdio.h>
#include <time.h>
#define MEASUREMENTS 1
#define OPERATIONS 1
typedef struct timespec TimeClock;
TimeClock diffTimeClock(TimeClock start, TimeClock end) {
TimeClock aux;
if((end.tv_nsec - start.tv_nsec) < 0) {
aux.tv_sec = end.tv_sec - start.tv_sec - 1;
aux.tv_nsec = 1E9 + end.tv_nsec - start.tv_nsec;
} else {
aux.tv_sec = end.tv_sec - start.tv_sec;
aux.tv_nsec = end.tv_nsec - start.tv_nsec;
}
return aux;
}
int main(void) {
TimeClock sTime, eTime, dTime;
int i, j;
for(i = 0; i < MEASUREMENTS; i++) {
printf(" » MEASURE %02d\n", i+1);
clock_gettime(CLOCK_REALTIME, &sTime);
for(j = 0; j < OPERATIONS; j++) {
myOperation();
}
clock_gettime(CLOCK_REALTIME, &eTime);
dTime = diffTimeClock(sTime, eTime);
printf(" - NSEC (TOTAL): %ld\n", dTime.tv_nsec);
printf(" - NSEC (OP): %ld\n\n", dTime.tv_nsec / OPERATIONS);
}
return 0;
}
ノート: 上記 diffTimeClock
機能はこちらから ブログ投稿. 。実際の操作を次のように置き換えました myOperation()
長いコードブロックをポストする必要があるため、実際の関数をポストするのは意味がありません。簡単にコーディングできます。 myOperation()
必要に応じて、好きなコードをコンパイルしてください。
ご覧のように、 OPERATIONS = 1
結果は次のとおりです。
» MEASURE 01
- NSEC (TOTAL): 27456580
- NSEC (OP): 27456580
のために OPERATIONS = 100
結果は次のとおりです。
» MEASURE 01
- NSEC (TOTAL): 218929736
- NSEC (OP): 2189297
のために OPERATIONS = 1000
結果は次のとおりです。
» MEASURE 01
- NSEC (TOTAL): 862834890
- NSEC (OP): 862834
のために OPERATIONS = 10000
結果は次のとおりです。
» MEASURE 01
- NSEC (TOTAL): 574133641
- NSEC (OP): 57413
さて、私は数学の達人ではないし、実際には数学の達人ではありませんが、これは私にとってまったく意味がありません。この件については、一緒にこのプロジェクトに参加している友人とすでに話しましたが、彼も違いが理解できません。増やすと値がどんどん下がっていく理由がわかりません OPERATIONS
. 。操作自体には、何度実行しても同じ時間がかかるはずです (もちろん、まったく同じ時間ではなく平均して)。
それは実際には操作自体、読み取られるデータに依存し、一部のデータはすでにキャッシュに存在している可能性がある、と言う人もいるかもしれませんが、私はそれが問題だとは思いません。私の場合、 myOperation
CSV ファイルから 5000 行のテキストを読み取り、値を次のように区切ります。 ;
そしてそれらの値をデータ構造に挿入します。反復ごとに、データ構造を破棄し、再度初期化します。
そういえば私も時間の測り方に問題があると思う clock_gettime()
, 、もしかしたら私の使い方が間違っているかもしれません。つまり、最後の例を見てください。 OPERATIONS = 10000
. 。かかった合計時間は 574133641ns で、およそ 0.5 秒になります。それは不可能です。待っている間画面を見ることに耐えられず、何か食べに行ったので数分かかりました。
解決
ただ変更する必要があるのは、 diffTimeClock()
差の秒数を返す関数 double
:
double diffTimeClock(TimeClock start, TimeClock end) {
double diff;
diff = (end.tv_nsec - start.tv_nsec) / 1E9;
diff += (end.tv_sec - start.tv_sec);
return diff;
}
そしてメインルーチンの変更では dTime
に double
, 、およびそれに適合する printfs:
printf(" - SEC (TOTAL): %f\n", dTime);
printf(" - SEC (OP): %f\n\n", dTime / OPERATIONS);
他のヒント
TimeClock タイプには 2 つのフィールドがあり、1 つは秒、もう 1 つはナノ秒になっているようです。nanosec フィールドを操作の数で単純に割ることは意味がありません。合計時間を分割する必要があります。
gettimeofday() 関数がある POSIX システムを使用している場合は、次のようなものを使用して現在時刻をマイクロ秒単位で取得できます。
long long timeInMicroseconds(void) {
struct timeval tv;
gettimeofday(&tv,NULL);
return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
}
これが非常に便利な理由は、関数にかかった時間を計算するには、次のことを行う必要があるためです。
long long start = timeInMicroseconds();
... do your task N times ...
printf("Total microseconds: %lld", timeInMicroseconds()-start);
したがって、秒とマイクロ秒の 2 つの整数を扱う必要はありません。時間の加算と減算は明らかな方法で機能します。
通常、これには time() 関数を使用します。壁時計の時間が表示されますが、最終的に私が気にするのはそれです。
パフォーマンス テストに関する問題の 1 つは、オペレーティング システムがファイル システム関連の操作をキャッシュする可能性があることです。したがって、2 回目 (以降) の実行は、最初の実行よりもはるかに高速になる可能性があります。通常、加えた変更の結果をよく理解するには、5 つの操作をテストし、結果を平均する必要があります。非常に多くの変数があるため、ノイズを除去するのに役立ちます。