Ошибка генератора случайных значений в задачах OpenMP - PullRequest
0 голосов
/ 31 января 2020

Я пытаюсь разработать микрокод на основе вычисления числа Пи методом Монте-Карло при использовании задач OpenMP. Кажется, у меня есть некоторые проблемы в этом коде. Действительно, мое приложение просто падает, и я получил ошибку сегментации, вызванную функцией gomp_barrier ...

Я не знаю, делаю ли я неправильные вещи в своем коде или нет, но я трачу немного времени вовремя попытаться его отладить и я ничего не нашел. Когда я просто удаляю вычисление x и y с генератором случайных значений, код работает хорошо ... Но если я добавлю вызов rand_r или srand48_r, у меня будет этот segfault. Так что, возможно, я использую эти функции неправильно. У кого-нибудь есть идеи, пожалуйста?

Вот мой код:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <inttypes.h>
#include <time.h>
#include <unistd.h>

#include "omp.h"

#define TRIALS_PER_THREAD 10E6

int main(int argc, char** argv)
{
  uint64_t const n_test = TRIALS_PER_THREAD;
  uint64_t i;
  double pi = 0.;

  int nb_threads = 2;
  #pragma omp parallel shared(nb_threads)
  {
    #pragma omp master
      nb_threads = omp_get_num_threads();

  }
  fprintf(stdout, "Nb threads: %d\n", nb_threads);

  uint64_t* result = (uint64_t*)malloc(sizeof(uint64_t) * nb_threads);
  for(i = 0; i < nb_threads; ++i)
  {
    result[i] = 0;
  }

  int nb_test_per_thread = 20;

  #pragma omp parallel\
  shared(result)\
  firstprivate(n_test,nb_test_per_thread)
  {
    unsigned int seed;
    struct drand48_data randBuffer;

    seed = time(NULL) ^ omp_get_thread_num() ^ getpid();
    srand48_r(seed, &randBuffer);

    for(int k = 0; k < nb_test_per_thread; ++k)
    {
      #pragma omp task shared(result, seed, randBuffer)
      {
        uint64_t local_res = 0;
        double x = 0., y = 0.;
        for(i = 0; i < n_test; ++i)
        {
          drand48_r(&randBuffer, &x);// / (double)RAND_MAX;
          drand48_r(&randBuffer, &y);// / (double)RAND_MAX;

          local_res += (((x * x) + (y * y)) <= 1);
        }
        int tid = omp_get_thread_num();
        result[tid] += local_res;
      }
    }
  }

  for(i = 0; i < nb_threads; ++i)
  {
    pi += result[i];
  }

  fprintf(stdout, "%ld of %ld throws are in the circle !\n", (uint64_t)pi, n_test*nb_test_per_thread*nb_threads);
  pi *= 4;
  pi /= (double)(n_test*nb_test_per_thread*nb_threads);
  fprintf(stdout, "Pi ~= %f\n", pi);

  return 0;
}

Заранее спасибо за помощь!

1 Ответ

1 голос
/ 03 февраля 2020

Ваше использование drand48_r(&randBuffer, &x); имеет состояние гонки, потому что каждый поток OpenMP разделяет randBuffer.

на Linux drand48_r() справочную страницу :

СИНОПСИС

   #include <stdlib.h>

   int drand48_r(struct drand48_data *buffer, double *result);

  ...

...

АТРИБУТЫ

Для пояснения терминов, используемых в этот раздел см. атрибуты (7) .

   ┌──────────────────────────┬───────────────┬─────────────────────┐
   │Interface                 │ Attribute     │ Value               │
   ├──────────────────────────┼───────────────┼─────────────────────┤
   │drand48_r(), erand48_r(), │ Thread safety │ MT-Safe race:buffer │
   │lrand48_r(), nrand48_r(), │               │                     │
   │mrand48_r(), jrand48_r(), │               │                     │
   │srand48_r(), seed48_r(),  │               │                     │
   │lcong48_r()               │               │                     │
   └──────────────────────────┴───────────────┴─────────────────────┘

Обратите внимание на MT-Safe race:buffer в таблице ATTRIUBTES .

Per справочная страница attribute (7) :

   `:identifier`

За аннотациями иногда могут следовать идентификаторы, предназначенные для группировки нескольких функций, которые, например, обращаются к структурам данных в небезопасный способ, как в race и const ...

Несколько параллельных вызовов на drand48_r() не могут безопасно использовать один и тот же аргумент buffer из-за условия гонки данных.

...