2013-06-14 4 views
1

Я пытался сделать свою программу в C multicore за последние несколько месяцев, но я все время получаю эту странную проблему. Может быть, некоторые из вас могут помочь мне немного?OpenMP разные результаты 1 core vs multiple

Проблема, с которой я сталкиваюсь, заключается в том, что моя программа дает правильные результаты, когда я позволяю ей вычислять, используя только один поток. Однако, когда я выбираю больше потоков, мои значения начинают меняться, хотя выполненные вычисления должны быть точно такими же (кроме генератора случайных чисел, но это не должно быть проблемой, поскольку каждое ядро ​​имеет свою собственную уникальную сеялку и генератор, как известно, работает с многоядерной обработкой openmp).

В любом случае, поскольку сама программа является конфиденциальной, я не могу дать вам весь код (он слишком велик для удобства использования), поэтому я постараюсь дать части кода, которые могут дополнительно объяснить проблема.

Первые библиотеки я включил:

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 
#include <string.h> 
#include <omp.h> /* openmp header */ 
#include <limits.h> 
#include <float.h> 
#include <gsl/gsl_rng.h> /*RNG header*/ 
#include <gsl/gsl_randist.h> /*RNG header*/ 

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

//perform multicore calculations (loop over all photons) 
#pragma omp parallel for default(none) \ 
    num_threads(thread_cnt) \ 
    private(icount,thread_id,i) \ 
    shared(calc,imstr,sum_irefl,leaks) \ 
    copyin(scat,cap,pcap_ini,profile,absmu,ctvar,lib) 
for(icount=0; icount <= cap.ndet; icount++){ 
    thread_id = omp_get_thread_num(); 
    printf("\nthread %d scat:\n",thread_id); 
    for(i=0;i<NDIM;i++) printf("%f\t",scat[i]); 
    do{ 
    do{ 
     start(ctvar, absmu, profile, &pcap_ini, &cap, &icount, &imstr, &lib, calc, &thread_id); 
     do capil(ctvar, absmu, profile, &pcap_ini, &cap, &icount, &imstr, &lib, leaks, scat, 
     calc, &thread_id); 
     while (calc[thread_id].iesc == 0); /* perform capil until iesc not equal to 0 anymore */ 
     } 
    while(calc[thread_id].iesc == -2); /* only do count etc if iesc!=-2, else redo start */ 
    count(absmu, ctvar, &cap, &icount, profile, leaks, &imstr,calc, &thread_id); 
    } 
    while(calc[thread_id].iesc == -3); 
    sum_irefl[thread_id] = sum_irefl[thread_id] + calc[thread_id].i_refl; 
    if(icount%1000 == 0 && thread_id == 0) printf("%d\t%ld\t%f\n",icount,calc[0].i_refl, 
    calc[0].rh[2]); 
    } 

В этих подфункциях (start, capil, count) некоторые переменные записываются с новыми значениями, это случай для calc,imstr,sum_irefl и leaks. Я устанавливаю их как общие, поэтому каждый поток может получить к ним доступ. Тем не менее, я не верю, что есть шанс на гонку памяти, так как, например, calc фактически разделен в массиве, где каждый поток имеет свои собственные переменные (доступ через их уникальный thread_id), а другие общие переменные могут быть замешаны как они ни разу не читаются. Возможно, я ошибаюсь в опасностях памяти, но я не думаю, что это создает проблему здесь ...

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

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

Итак, что мне интересно: возможно ли, что у меня есть вредная гонка памяти? Вы видите что-нибудь еще, что может пойти не так? Знаете ли вы какие-либо (относительно) простые в использовании программы, которые я мог бы использовать для проверки моей многоядерной программы?

+0

Здесь определенно чего-то не хватает. Почему вы получаете и используете идентификатор потока в контексте параллельного цикла? Вы получаете доступ к переменной calc какой-то полностью независимой от цикла, она сливается где-то позже?По крайней мере, это должно быть разделено на параллельную область omp, которая получает идентификатор потока и выполняет конкретную работу по потоку, а также встроенную конструкцию совместной работы, которая распространяется на нить-независимую работу. – njustn

+0

Переменная calc действительно объединена после цикла parallel for. Я также думаю, что понимаю ваше последнее замечание, но «проблема» заключается в том, что во время цикла for выполняется также конкретная работа по потоку (например, calc [thread_id] .iesc получает новое значение и такие вещи). Так что, как я вижу, я не могу сделать поток цикла нить независимым. Но, может быть, я не понимаю вас ... не могли бы вы написать мне пример кода параллельного параллелизма и для региона, используя код, упомянутый в моем первоначальном вопросе? – user2257570

+0

@ user2257570 Не могли бы вы рассказать нам, какой генератор случайных чисел вы используете? Меня интересуют генераторы, которые будут использоваться безопасно с помощью openmp. – altroware

ответ

1

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

#pragma omp parallel default(none) \ 
    num_threads(thread_cnt) \ 
    shared(calc,imstr,sum_irefl,leaks) \ 
    copyin(scat,cap,pcap_ini,profile,absmu,ctvar,lib) 
{ 
int icount, thread_id, i; //note, private by definition in the region 
thread_id = omp_get_thread_num(); 
printf("\nthread %d scat:\n",thread_id); 
//perform multicore calculations (loop over all photons) 
#pragma omp for 
for(icount=0; icount <= cap.ndet; icount++){ 

    for(i=0;i<NDIM;i++) printf("%f\t",scat[i]); 
    do{ 
    do{ 
     start(ctvar, absmu, profile, &pcap_ini, &cap, &icount, &imstr, &lib, calc, &thread_id); 
     do capil(ctvar, absmu, profile, &pcap_ini, &cap, &icount, &imstr, &lib, leaks, scat, 
     calc, &thread_id); 
     while (calc[thread_id].iesc == 0); /* perform capil until iesc not equal to 0 anymore */ 
     } 
    while(calc[thread_id].iesc == -2); /* only do count etc if iesc!=-2, else redo start */ 
    count(absmu, ctvar, &cap, &icount, profile, leaks, &imstr,calc, &thread_id); 
    } 
    while(calc[thread_id].iesc == -3); 
    sum_irefl[thread_id] = sum_irefl[thread_id] + calc[thread_id].i_refl; 
    if(icount%1000 == 0 && thread_id == 0) printf("%d\t%ld\t%f\n",icount,calc[0].i_refl, 
    calc[0].rh[2]); 
    } 
} 
+0

Спасибо за пример кода! Я думал, что вы хотели сделать что-то подобное, но не был уверен;) Вы тоже на хорошей дорожке, я думаю, с добавлением значений ... Все общие переменные действительно суммируются или накапливаются в них во время цикла for. В основном это счетчики, обозначающие наличие определенных событий. Должен ли я что-то сделать для этого? Я читал кое-что о сокращении, которое мне, возможно, придется использовать, но я этого не понимаю. Не могли бы вы посоветовать, njustn? Спасибо за помощь! – user2257570

+0

Извинения за задержку, я возвращался из Германии. Любые накопленные переменные должны быть защищены, проблема в том, что даже такой оператор, как foo ++, фактически является чтением в регистр, добавлением, а затем записью, которая вводит расы данных. Чтобы использовать предложение сокращения, вам, вероятно, придется переместить параллелизм во внутренние функции, которые, я сомневаюсь, вы хотите сделать. Может возникнуть больше смысла создавать частные копии этих файлов, а затем объединить их в конце, поскольку вы уже делаете массив меток. – njustn