Сравнение производительности различных конструкций pthread

#c #performance #parallel-processing #pthreads #execution-time

#c #Производительность #параллельная обработка #pthreads #время выполнения

Вопрос:

Мне нужно сравнить производительность различных конструкций pthread, таких как мьютекс, семафоры, блокировки чтения-записи, а также соответствующих последовательных программ, путем разработки некоторых экспериментов. Основная проблема заключается в том, чтобы решить, как измерить время выполнения кода для анализа ?

Я читал о некоторых функциях C, таких как clock (), gettimeofday () и т.д. Из того, что я смог понять — мы можем использовать clock (), чтобы получить фактическое количество циклов процессора, используемых программой (путем вычитания значения, возвращаемого функцией в начале и конце кода, время которого мы хотим измерить), gettimeofday() возвращает время настенных часов для выполнения программы.

Но проблема в том, что общее количество циклов процессора не представляется мне хорошим критерием, поскольку оно суммировало бы процессорное время, затраченное на все параллельно работающие потоки (поэтому clock (), по моему мнению, не подходит). Также время на настенных часах не подходит, поскольку в фоновом режиме могут выполняться другие процессы, поэтому время в конечном итоге зависит от того, как потоки будут запланированы (поэтому gettimeofday () также, по моему мнению, не подходит).

Некоторые другие функции, о которых я знаю, также выполняют, скорее всего, то же самое, что и две из вышеперечисленных. Итак, я хотел узнать, есть ли какая-то функция, которую я могу использовать для своего анализа, или я где-то ошибаюсь в своем заключении выше?

Комментарии:

1. как регистрируется ваше выполнение? какая у вас операционная система? Если вы хотите сравнить моно / многопоточность, сравните реальное время, а не процессорное

2. Я использую Linux

3. и как долго длится время выполнения? сколько у вас cpu / core?

4. Я должен сравнить его для различных входных размеров — скажем, например, я должен суммировать массив, затем я должен изменить размер, который может быть как 10^7, 10^8, 10^9.

5. Вы должны показать код из ваших попыток. Что касается способа измерения времени, вы должны использовать либо clock_getttime() , либо __rdtsc() . Не забудьте отключить изменения частоты процессора. Всегда используйте по крайней мере -O2 в вашем компиляторе. Выполните несколько измерений и используйте статистические методы для удаления выбросов: усеченное среднее или даже минимальное значение, которое проще и приводит к более стабильным результатам.

Ответ №1:

Из Linux clock_gettime:

    CLOCK_PROCESS_CPUTIME_ID (since Linux 2.6.12)
          Per-process CPU-time clock (measures CPU time consumed by all
          threads in the process).

   CLOCK_THREAD_CPUTIME_ID (since Linux 2.6.12)
          Thread-specific CPU-time clock.
  

Я полагаю, что clock() было где-то реализовано как clock_gettime(CLOCK_PROCESS_CPUTIME_ID , но я вижу, что это реализовано с использованием times() в glibc.

Итак, если вы хотите измерить процессорное время, зависящее от потока, вы можете использовать clock_gettimer(CLOCK_THREAD_CPUTIME_ID, ... в системах GNU / Linux.

Никогда не используйте gettimeofday nor clock_gettime(CLOCK_REALTIME для измерения выполнения программы. Даже не думайте об этом. gettimeofday это «настенные часы» — вы можете повесить их на стену в своей комнате. Если вы хотите измерить течение времени, забудьте gettimeofday .

Если вы хотите, вы также можете оставаться полностью совместимым с posixly, используя pthread_getcpuclockid внутри вашего потока и используя его возвращаемое clock_id значение с clock_gettime .

Комментарии:

1. все зависит от того, что вы хотите измерить, для меня это реальное время, потому что я чувствую, что это время, если мне нужно 1 минута для загрузки моей программы, мне все равно, что для выполнения требуется 1 секунда, для меня это время равно 1 минуте, а не 1 секунде 😉

2. Тогда используйте CLOCK_MONOTONIC , а не gettimeofday . gettimeofday это настенные часы, а не «часы с интервалом измерения». Он может прыгать. Если вы используете gettimeofday для измерения выполнения вашей программы, не удивляйтесь, увидев отрицательный временной интервал. Или неправильный интервал. Он может прыгать. gettimeofday предназначено только для приятного просмотра пользовательских часов, синхронизированных с UTC. Потому что однажды произойдет скачок, и ваши измерения будут неверными. Или ntp запускается и синхронизирует систему — и ваши измерения будут неверными.

Ответ №2:

Я не уверен, что суммирование массива является хорошим тестом, вам не нужны никакие мьютексы и т.д. Для суммирования массива в многопоточности, каждому потоку просто нужно суммировать выделенную часть массива, и есть много обращений к памяти для нескольких вычислений процессора. Пример (значения SZ и NTHREADS указаны при компиляции), измеренное время является реальным временем (монотонным) :

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

static int Arr[SZ];

void * thSum(void * a)
{
  int s = 0, i;
  int sup = *((int *) a)   SZ/NTHREADS;

  for (i = *((int *) a); i != sup;   i)
    s  = Arr[i];

  *((int *) a) = s;
}

int main()
{
  int i;

  for (i = 0; i != SZ;   i)
    Arr[i] = rand();

  struct timespec t0, t1;

  clock_gettime(CLOCK_MONOTONIC, amp;t0);

  int s = 0;

  for (i = 0; i != SZ;   i)
    s  = Arr[i];

  clock_gettime(CLOCK_MONOTONIC, amp;t1);
  printf("mono thread : %d %lfn", s,
         (t1.tv_sec - t0.tv_sec)   (t1.tv_nsec - t0.tv_nsec)/1000000000.0);

  clock_gettime(CLOCK_MONOTONIC, amp;t0);

  int n[NTHREADS];
  pthread_t ths[NTHREADS];

  for (i = 0; i != NTHREADS;   i) {
    n[i] = SZ / NTHREADS * i;
    if (pthread_create(amp;ths[i], NULL, thSum, amp;n[i])) {
      printf("cannot create thread %dn", i);
      return -1;
    }
  }

  int s2 = 0;

  for (i = 0; i != NTHREADS;   i) {
    pthread_join(ths[i], NULL);
    s2  = n[i];
  }

  clock_gettime(CLOCK_MONOTONIC, amp;t1);
  printf("%d threads : %d %lfn", NTHREADS, s2,
         (t1.tv_sec - t0.tv_sec)   (t1.tv_nsec - t0.tv_nsec)/1000000000.0);
}
  

Компиляции и исполнения:

(массив из 100.000.000 элементов)

 /tmp % gcc -DSZ=100000000 -DNTHREADS=2 -O3 s.c -lpthread -lrt
/tmp % ./a.out
mono thread : 563608529 0.035217
2 threads : 563608529 0.020407
/tmp % ./a.out
mono thread : 563608529 0.034991
2 threads : 563608529 0.022659
/tmp % gcc -DSZ=100000000 -DNTHREADS=4 -O3 s.c -lpthread -lrt
/tmp % ./a.out
mono thread : 563608529 0.035212
4 threads : 563608529 0.014234
/tmp % ./a.out
mono thread : 563608529 0.035184
4 threads : 563608529 0.014163
/tmp % gcc -DSZ=100000000 -DNTHREADS=8 -O3 s.c -lpthread -lrt
/tmp % ./a.out
mono thread : 563608529 0.035229
8 threads : 563608529 0.014971
/tmp % ./a.out
mono thread : 563608529 0.035142
8 threads : 563608529 0.016248
  

(массив из 1000.000.000 элементов)

 /tmp % gcc -DSZ=1000000000 -DNTHREADS=2 -O3 s.c -lpthread -lrt
/tmp % ./a.out
mono thread : -1471389927 0.343761
2 threads : -1471389927 0.197303
/tmp % ./a.out
mono thread : -1471389927 0.346682
2 threads : -1471389927 0.197669
/tmp % gcc -DSZ=1000000000 -DNTHREADS=4 -O3 s.c -lpthread -lrt
/tmp % ./a.out
mono thread : -1471389927 0.346859
4 threads : -1471389927 0.130639
/tmp % ./a.out
mono thread : -1471389927 0.346506
4 threads : -1471389927 0.130751
/tmp % gcc -DSZ=1000000000 -DNTHREADS=8 -O3 s.c -lpthread -lrt
/tmp % ./a.out
mono thread : -1471389927 0.346954
8 threads : -1471389927 0.123572
/tmp % ./a.out
mono thread : -1471389927 0.349652
8 threads : -1471389927 0.127059
  

Как вы можете видеть, даже время выполнения не делится на количество потоков, узким местом, вероятно, является доступ к памяти

Комментарии:

1. Вы не должны использовать gettimeofday() для измерения производительности. Любая синхронизация ntp разрушит ваши показатели.

2. @AlainMerigot измеренное время выполнения отличается, но, вероятно, не из-за ntp, тактовые частоты достаточно хороши, а время наверстывания упущенного невелико. Для меня должно измеряться реальное время, а не время процессора

3. @AlainMerigot в любом случае, я перешел на монотонное время