Элегантная параллельная инициализация потоков openmp для цикла - PullRequest
0 голосов
/ 06 января 2011

У меня есть цикл for, который использует (несколько сложный) объект счетчика sp_ct для инициализации массива. Серийный код выглядит как

sp_ct.depos(0);
for(int p=0;p<size; p++, sp_ct.increment() ) {
  in[p]=sp_ct.parable_at_basis();
}

Мой счетчик поддерживает распараллеливание, потому что его можно инициализировать до состояния с шагом p, что приводит к следующему рабочему фрагменту кода:

  int firstloop=-1;
#pragma omp parallel for \
       default(none) shared(size,in) firstprivate(sp_ct,firstloop)
  for(int p=0;p<size;p++) {
    if( firstloop == -1 ) {
      sp_ct.depos(p); firstloop=0;
    } else { 
      sp_ct.increment();
    }
    in[p]=sp_ct.parable_at_basis();
  } // end omp paralell for

Мне не нравится это из-за беспорядка, который скрывает то, что действительно происходит, и из-за того, что у него есть ненужная ветвь внутри цикла (Да, я знаю, что это, вероятно, не окажет ощутимого влияния на время выполнения, потому что 1009 * так предсказуемо ...).

Я бы предпочел написать что-то вроде

#pragma omp parallel for default(none) shared(size,in) firstprivate(sp_ct,firstloop)
  for(int p=0;p<size;p++) {
#prgma omp initialize // or something
    {  sp_ct.depos(p); }
    in[p]=sp_ct.parable_at_basis();
    sp_ct.increment();
    }
  } // end omp paralell for

Возможно ли это?

Ответы [ 4 ]

1 голос
/ 06 декабря 2013

Из того, что я могу сказать, вы можете сделать это, вручную определив куски.Это похоже на то, что я пытался сделать с индукцией в OpenMP Индукция с OpenMP: получение значений диапазона для парализованного цикла for в OpenMP

Так что вы, вероятно, хотите что-то вроде этого:*

#pragma omp parallel
{
    const int nthreads = omp_get_num_threads();
    const int ithread = omp_get_thread_num();
    const int start = ithread*size/nthreads;
    const int finish = (ithread+1)*size/nthreads;       
    Counter_class_name sp_ct;

    sp_ct.depos(start);   
    for(int p=start; p<finish; p++, sp_ct.increment()) {
        in[p]=sp_ct.parable_at_basis();
    }
}

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

Также вам не нужно объявлять что-либо общее или частное.Все, что объявлено внутри параллельного блока, является частным, а все, что объявлено снаружи, является общим.Вам также не нужен firstprivate.Это делает код чище и понятнее (ИМХО).

1 голос
/ 24 мая 2012

Если я обобщу вашу проблему, вопрос «Как выполнить некоторый код инициализации для каждого потока в параллельном разделе?», Верно?Вы можете использовать свойство предложения firstprivate: «инициализация или создание данной переменной происходит так, как если бы это было сделано один раз для потока перед выполнением потоком конструкции».

struct thread_initializer
{
  explicit thread_initializer(
    int size /*initialization params*/) : size_(size) {}

  //Copy constructor that does the init
  thread_initializer(thread_initializer& _it) : size_(_it.size)
  {
    //Here goes once per thread initialization
    for(int p=0;p<size;p++)
      sp_ct.depos(p);
  }

  int size_;
  scp_type sp_ct;
};

Тогдацикл может быть записан:

thread_initializer init(size);
#pragma omp parallel for \
       default(none) shared(size,in) firstprivate(init)
for(int p=0;p<size;p++) {
  init.sp_ct.increment();
}
in[p]=init.sp_ct.parable_at_basis();

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

0 голосов
/ 06 декабря 2013

Riko, реализуйте sp_ct.depos(), поэтому он будет вызывать .increment() только так часто, как это необходимо для приведения счетчика к переданному параметру. Тогда вы можете использовать этот код:

sp_ct.depos(0);
#pragma omp parallel for \
       default(none) shared(size,in) firstprivate(sp_ct)
for(int p=0;p<size;p++) {
  sp_ct.depos(p);
  in[p]=sp_ct.parable_at_basis();
} // end omp paralell for

Это решение имеет одно дополнительное преимущество: ваша реализация работает, только если каждый поток получает только один блок из 0 - size. Это имеет место при указании schedule(static) без указания размера чанка ( Спецификация OpenMP 4.0 , глава 2.7.1, стр. 57). Но поскольку вы не указали schedule, используемый график будет зависеть от реализации ( Спецификация OpenMP 4.0 , глава 2.3.2). Если реализация решит использовать dynamic или guided, потоки получат несколько чанков с промежутками между ними. Таким образом, один поток может получить чанк 0-20, а затем чанк 70-90, что приведет к рассинхронизации p и sp_ct во втором чанке. Решение выше совместимо со всеми расписаниями.

0 голосов
/ 06 января 2011

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

sp_ct.depos(0);
in[0]=sp_ct.parable_at_basis();
#pragma omp parallel for \
       default(none) shared(size,in) firstprivate(sp_ct,firstloop)
  for(int p = 1; p < size; p++) {
    sp_ct.increment();
    in[p]=sp_ct.parable_at_basis();
  } // end omp paralell for
...