clock_gettime() Vs. gettimeofday() для измерения времени выполнения OpenMP

Я работаю над кодом C, который реализует тройной вложенный цикл for для вычисления умножения матрицы на матрицу при распараллеливании его с использованием OpenMP. Я пытаюсь точно измерить количество времени, которое требуется от начала цикла 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 (), а иногда был на секунду дольше, чем результат, который я получал использование 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 ответа

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, Это как второй и третий код ОП, но не первый.

  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. Незначительное: третье, хотя и очень тонкое улучшение, которое помогает немного уменьшить несогласованность (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;

Спасибо всем за помощь. Оказывается, проблема не связана с функциями времени, а связана с тем, что при расчете разницы между временем окончания и временем начала неправильно ставятся круглые скобки. Я знаю, что-то вроде антиклиматического и тупого решения, но оно решило мою проблему. Когда я использовал gettimeofday(), я делал это для вычисления результата:

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

Когда я должен был сделать это:

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

Код, который я использую, был написан кем-то другим для использования функции gettimeofday(), и у них были следующие #define's:

#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(), я правильно поставил круглые скобки и просто не заметил, что #define, который был у автора кода для gettimeofday(), не был.

Другие вопросы по тегам