5

私は、OpenMP を使用して並列化しながら、行列と行列の乗算を計算するためにトリプル ネストされた for ループを実装する C コードに取り組んでいます。for ループの開始から終了までにかかる時間を正確に測定しようとしています。これまで gettimeofday() を使用してきましたが、for ループの実行にかかった時間を正確に記録しているように感じられない場合があることに気付きました。実際よりも時間がかかったと言っているように見えました。

元のコードは次のとおりです。

struct timeval start end;
double elapsed;

gettimeofday(&start, NULL);
#pragma omp parallel for num_threads(threads) private(i, j, k)
for(...)
{
 ...
 for(...)
 {
  ...
  for(...)
  {
   ...
  }
 }
}

gettimeofday(&end, NULL);
elapsed = (end.tv_sec+1E-6*end.tv_usec) - (start.tv_sec+1E-6*start.tv_usec)

そして、これは clock_gettime() を使用した同じコードです:

 struct timespec start1, finish1;
 double elapsed1;

clock_gettime(CLOCK_MONOTONIC, &start1);

  #pragma omp parallel for num_threads(threads) private(i, j, k)
    for(...)
    {
     ...
     for(...)
     {
      ...
      for(...)
      {
       ...
      }
     }
    }

clock_gettime(CLOCK_MONOTONIC, &finish1);
elapsed1 = (finish1.tv_sec - start1.tv_sec);
elapsed1 += (finish1.tv_nsec - start1.tv_nsec)/1000000000.0;

ループが完了するまでに 3 ~ 4 秒かかります。両方の時間測定を同時に使用してみましたが、gettimeofday() を使用した結果はほとんどの場合、clock_gettime() の結果よりも長く、結果よりも 1 秒以上長くなることもありました。 clock_gettime() を使用して取得していました:

struct timespec start1, finish1;
double elapsed1;

struct timeval start end;
double elapsed;

clock_gettime(CLOCK_MONOTONIC, &start1);
gettimeofday(&start, NULL);

  #pragma omp parallel for num_threads(threads) private(i, j, k)
    for(...)
    {
     ...
     for(...)
     {
      ...
      for(...)
      {
       ...
      }
     }
    }

gettimeofday(&end, NULL);
clock_gettime(CLOCK_MONOTONIC, &finish1);

elapsed = (end.tv_sec+1E-6*end.tv_usec) - (start.tv_sec+1E-6*start.tv_usec)

elapsed1 = (finish1.tv_sec - start1.tv_sec);
elapsed1 += (finish1.tv_nsec - start1.tv_nsec)/1000000000.0;

これには理由がありますか?これら 2 つの機能を使用する際に違いが生じる原因は何ですか? これら 2 つの機能の性質をよりよく理解しようとしています。

4

2 に答える 2

5

elapsed = (end.tv_sec+1E-6*end.tv_usec) - (start.tv_sec+1E-6*start.tv_usec)両方とも大きい同様の値を減算すると、精度が失われる傾向があります。

  1. を使用しelapsed = (end.tv_sec - start.tv_sec) - (start.tv_usec- end.tv_usec)/1E6ます。これは OP の 2 番目と 3 番目のコードのようなものですが、1 番目ではありません。

  2. 偏見をなくすために、公平を期して、一貫した順序で時間を取得してください。

    clock_gettime(CLOCK_MONOTONIC, &start1);
    gettimeofday(&start, NULL);
    
    ...
    
    // gettimeofday(&end, NULL);
    // clock_gettime(CLOCK_MONOTONIC, &finish1);
    clock_gettime(CLOCK_MONOTONIC, &finish1);
    gettimeofday(&end, NULL);
    
  3. マイナー: 3 番目の、不一致を少し(0.5 マイクロ秒)減らすのに役立つ非常に微妙な改善は、ティックの変更でテストを開始することです。ただし、代替の改善については、 @Dietrich Epp のコメントに注意してください。

    gettimeofday(&t, NULL);
    do { 
      gettimeofday(&start, NULL);
    } while (start == t);
    

または、ワイド整数演算を使用して精度の問題を回避します

long long elapsed_ns = (1LL*finish1.tv_sec - start1.tv_sec)*1000000000LL + 
    finish1.tv_nsec - start1.tv_nsec;
于 2016-09-26T19:39:14.820 に答える
2

みんな助けてくれてありがとう。問題は時間関数に関係する必要はなく、終了時間と開始時間の差を計算するときに括弧を適切に配置しなかったことが原因であることが判明しました。私は知っています、一種の逆境的で愚かな解決策ですが、それは私の問題を解決しました. gettimeofday() を使用していたとき、結果を計算するためにこれを行っていました。

経過 = end.tv_sec+1E-6*end.tv_usec - start.tv_sec+1E-6*start.tv_usec

私がこれをしていたはずのとき:

経過 = (end.tv_sec+1E-6*end.tv_usec) - (start.tv_sec+1E-6*start.tv_usec)

私が使用しているコードは、他の誰かが gettimeofday() 関数を使用するために作成したもので、次の #define が含まれていました。

#define TIME_GET(time) (time).tv_sec+1E-6*(time).tv_usec
#define TIME_GET_RESULT(start,end) TIME_GET(end)-TIME_GET(start)

括弧を追加して最初の #define を変更すると、問題が修正されました。

#define TIME_GET(time) ((time).tv_sec+1E-6*(time).tv_usec)

clock_gettime() を使い始めたとき、括弧を正しく入れていましたが、コードの作成者が gettimeofday() に対して持っていた #define がそうではないことに気づきませんでした。

于 2016-09-27T11:50:56.327 に答える