2011-01-05 3 views
0

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

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 

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

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

#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 

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

+0

Есть ли причина, по которой вы не можете выполнить инициализацию вне цикла? –

+1

Да, инициализация зависит от первого значения 'p', которое назначено потоку. Это неизвестно вне цикла. –

ответ

0

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

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 
+0

Вам нужно будет отредактировать оператор #pragma, я уверен. (Я действительно не знаю openmp, извините) –

+0

Да, это как раз мой вопрос: Можете ли вы как-то поместить инициализацию в '# прагма'? –

+0

Не знаю, извините, я не могу ответить на ваш вопрос. Могу я спросить, зачем вам это нужно? –

1

Если я обобщать вам проблему, вопрос «Как выполнить некоторый intialization код для каждого потока параллельной секции?», Верно? Вы можете использовать свойство предложения 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

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 Specification, глава 2.7.1, стр. 57). Но так как вы не указали schedule, используемое расписание будет зависящим от реализации (OpenMP 4.0 Specification, глава 2.3.2). Если реализация решит использовать dynamic или guided, потоки получат несколько кусков с промежутками между ними. Таким образом, один поток может получить кусок 0-20, а затем кусок 70-90, который сделает p и sp_ct не синхронизируется со вторым куском. Решение выше совместимо со всеми расписаниями.

1

Из того, что я могу сказать, вы можете сделать это, вручную определяя куски.Это выглядит как-то я пытался сделать с индукцией в OpenMP Induction with OpenMP: getting range values for a parallized for loop in 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(); 
    } 
} 

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

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

Смежные вопросы