Почему я должен использовать сокращение, а не атомарную переменную? - PullRequest
0 голосов
/ 14 января 2019

Предположим, мы хотим что-то посчитать в цикле OpenMP. Сравните уменьшение

int counter = 0;
#pragma omp for reduction( + : counter )
for (...) {
    ...
    counter++;
}

с атомным приращением

int counter = 0;
#pragma omp for
for (...) {
    ...
    #pragma omp atomic
    counter++
}

Атомный доступ немедленно предоставляет результат, в то время как сокращение принимает только его правильное значение в конце цикла. Например, сокращения не позволяют этого:

int t = counter;
if (t % 1000 == 0) {
    printf ("%dk iterations\n", t/1000);
}

, что обеспечивает меньшую функциональность.

Зачем мне использовать сокращение вместо атомарного доступа к счетчику?

Ответы [ 2 ]

0 голосов
/ 14 января 2019

Производительность - ключевой момент.

Рассмотрим следующую программу

#include <stdio.h>
#include <omp.h>
#define N 1000000
int a[N], sum;

int main(){
  double begin, end;

  begin=omp_get_wtime();
  for(int i =0; i<N; i++)
    sum+=a[i];
  end=omp_get_wtime();
  printf("serial %g\t",end-begin);

  begin=omp_get_wtime();
# pragma omp parallel for
  for(int i =0; i<N; i++)
# pragma omp atomic
    sum+=a[i];
  end=omp_get_wtime();
  printf("atomic %g\t",end-begin);

  begin=omp_get_wtime();
# pragma omp parallel for reduction(+:sum)
  for(int i =0; i<N; i++)
    sum+=a[i];
  end=omp_get_wtime();
  printf("reduction %g\n",end-begin);
}

При выполнении (gcc -O3 -fopenmp) выдает:

серийный 0,00491182 атомный 0,0786559 сокращение 0,001103

Так что примерно атомный = 20xserial = 80xreduction

«Редукция» должным образом использует параллелизм, и с 4-ядерным компьютером мы можем получить повышение производительности от 3 до 6 по сравнению с «последовательным».

Теперь «атомарный» в 20 раз длиннее «серийного». Как объяснялось в предыдущем ответе, сериализация обращений к памяти отключает параллелизм, но все обращения к памяти выполняются атомарными операциями. Эти операции требуют не менее 20-50 циклов на современных компьютерах и значительно замедляют работу при интенсивном использовании.

0 голосов
/ 14 января 2019

Краткий ответ:

Производительность

Длинный ответ:

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

Сокращение, с другой стороны, является обычной операцией, которая может выполняться параллельно с использованием алгоритмов параллельного сокращения. Прочитайте это и это статьи для получения дополнительной информации об алгоритмах параллельного сокращения.


Приложение: понимание того, как работает параллельное сокращение

Представьте себе сценарий, в котором у вас есть потоки 4, и вы хотите уменьшить массив элементов 8. Что вы можете сделать это за 3 шага (проверьте прилагаемое изображение, чтобы лучше понять, о чем я говорю ):

  • Шаг 0 . Потоки с индексом i<4 позаботятся о результате суммирования A[i]=A[i]+A[i+4].
  • Шаг 1 . Потоки с индексом i<2 позаботятся о результате суммирования A[i]=A[i]+A[i+4/2].
  • Шаг 2 . Потоки с индексом i<4/4 позаботятся о результате суммирования A[i]=A[i]+A[i+4/4]

В конце этого процесса вы получите результат сокращения по первому элементу A, т.е. A[0]

enter image description here

...