Я пытался сделать свою программу в 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 или более ядрами.
Я знаю, что это не так много, но я надеюсь, что у некоторых из вас есть идея. Если вам нужна дополнительная информация, не стесняйтесь спрашивать меня, так как я могу предоставить больше.
Итак, что мне интересно: возможно ли, что у меня есть вредная гонка памяти? Вы видите что-нибудь еще, что может пойти не так? Знаете ли вы какие-либо (относительно) простые в использовании программы, которые я мог бы использовать для проверки моей многоядерной программы?
Здесь определенно чего-то не хватает. Почему вы получаете и используете идентификатор потока в контексте параллельного цикла? Вы получаете доступ к переменной calc какой-то полностью независимой от цикла, она сливается где-то позже?По крайней мере, это должно быть разделено на параллельную область omp, которая получает идентификатор потока и выполняет конкретную работу по потоку, а также встроенную конструкцию совместной работы, которая распространяется на нить-независимую работу. – njustn
Переменная calc действительно объединена после цикла parallel for. Я также думаю, что понимаю ваше последнее замечание, но «проблема» заключается в том, что во время цикла for выполняется также конкретная работа по потоку (например, calc [thread_id] .iesc получает новое значение и такие вещи). Так что, как я вижу, я не могу сделать поток цикла нить независимым. Но, может быть, я не понимаю вас ... не могли бы вы написать мне пример кода параллельного параллелизма и для региона, используя код, упомянутый в моем первоначальном вопросе? – user2257570
@ user2257570 Не могли бы вы рассказать нам, какой генератор случайных чисел вы используете? Меня интересуют генераторы, которые будут использоваться безопасно с помощью openmp. – altroware