Как избежать конфликтов между потоками в openmp - PullRequest
0 голосов
/ 03 сентября 2018

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

  if (id == 0) nthreads = nthrds; //Only one thread should copy the number of threads to the global value to make sure multiple threads writing to the same address don’t conflict.

Весь код:

#include<omp.h>
#include<stdio.h>
#define NUM_THREADS 2

static long num_steps = 100000;
double step;

int main ()
{
   int i, nthreads;
   double pi, sum[NUM_THREADS];
   step = 1.0/(double) num_steps;

   omp_set_num_threads(NUM_THREADS);
   double time1 = omp_get_wtime();
   #pragma omp parallel
   {
       int i, id,nthrds;
       double x;
       id = omp_get_thread_num();
       nthrds = omp_get_num_threads();
       if (id == 0) nthreads = nthrds; //Only one thread should copy 
       the number of threads to the global value to make sure multiple 
       threads writing to the same address don’t conflict.

       for (i=id, sum[id]=0.0;i< num_steps; i=i+nthrds){
             x = (i+0.5)*step;
             sum[id] += 4.0/(1.0+x*x);
       }
   }
   double time2 = omp_get_wtime();

   for(i=0, pi=0.0;i<nthreads;i++)pi += sum[i] * step;
   printf("%lf\n",pi);
   printf("%lf\n",(time2-time1));

}

Я пытался запустить без оператора if, но он дал значение pi 0, но работал правильно в противном случае (дал 3.141593). Когда я попытался назначить nthreads равным общему количеству потоков (то есть 2) снаружи, это все равно дало правильное значение pi. Кто-нибудь может объяснить мне, как есть разница в выходе?

Спасибо !!

Ответы [ 2 ]

0 голосов
/ 08 сентября 2018

Когда вы пытались назначить nthread, равное общему количеству потоков (то есть 2) вне глобально, это все равно дало правильное значение pi, потому что количество потоков, которые вы запросили с компьютера, было передано вам (т.е. в вашем в случае 2), но если вы запросили 1 миллион потоков, компьютер может не дать вам столько потоков. Чтобы узнать, сколько потоков выделено вам, вам нужно написать этот фрагмент кода.

    if (id == 0) nthreads = nthrds;
0 голосов
/ 03 сентября 2018

Для шага суммирования в последнем цикле необходимо установить переменную nthreads

for(i=0, pi=0.0;i<nthreads;i++)pi += sum[i] * step;

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

nthreads = nthrds;

Если вы пишете в общую память из нескольких потоков без какой-либо защиты, это значение может быть неправильным. Тем не менее, как правило, обычно используется atomic в качестве защиты. В этом случае #pragma omp single nowait будет гораздо более подходящим. Я догадываюсь идея динамической записи этой переменной вместо простого использования NUM_THREADS заключается в том, что вы не всегда можете гарантировать ее.

Во всяком случае. Этот урок очень проблематичен. Он пытается научить OpenMP с использованием сырых примитивов, а не с помощью идиоматических инструментов высокого уровня. Это вызывает много из путаницы . Я считаю, что это плохой подход к обучению OpenMP, особенно если вы не будете следовать учебнику до самого конца.

Правильный способ сделать это на самом деле дан позже в уроке (с некоторой модернизацией мной):

double sum = 0.0;
int step = 1.0/(double) num_steps;
omp_set_num_threads(NUM_THREADS);
#pragma omp parallel for reduction(+:sum)
for (int i=0; i < num_steps; i++) {
    double x = (i+0.5)*step;
    sum = sum + 4.0/(1.0+x*x);
}
double pi = step * sum;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...