2013-06-14 2 views
1

지난 몇 달간 C 멀티 코어에서 프로그램을 만들려고 노력했지만 아직 이상한 문제가 있습니다. 어쩌면 당신 중 일부가 좀 도와 줄 수 있을까요?OpenMP 다른 결과 1 코어 vs 복수

내가 겪고있는 문제는 하나의 스레드 만 사용하여 계산할 때 내 프로그램이 적절한 결과를 제공한다는 것입니다. 그러나 더 많은 스레드를 선택하면 수행 한 계산이 정확히 같아야하지만 (임의의 숫자 생성기를 제외하고는) 값이 변경되기 시작합니다.하지만 각 코어가 고유 한 시드를 가지고 있으므로 문제가되어서는 안됩니다. 생성기는 openmp 멀티 코어 처리와 함께 작동하는 것으로 알려져 있음).

어쨌든 프로그램 자체는 기밀 사항이므로 전체 코드를 제공 할 수는 없습니다 (어쨌든 편리한 용도로는 너무 큽니다). 따라서 코드의 일부를 설명하기 위해 최선을 다할 것이며, 문제.

먼저 라이브러리는 I 포함 :

#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_ireflleaks의 경우입니다. 공유로 설정하여 각 스레드가 액세스 할 수 있도록합니다. 그러나, 예를 들어 calc 같은 메모리 경주에 대한 기회가 있다고 생각하지 않는다. 각 스레드는 (고유 한 thread_id을 통해 액세스되는) 자체 변수를 가지며 다른 공유 변수는 다음과 같이 경쟁 할 수있다. 그들은 어느 시점에서 읽히지 않습니다. 아마도 나는 메모리 경주의 위험을 오해하고 있습니다. 그러나 여기에 문제가 있다고 생각하지 않습니다 ...

변수는 이전에 threadsafe로 만들어졌지만 문제가 아닌 것으로 가정) 병렬 루프 중에 만 읽혀 지므로 다시 문제가 무엇인지 알 수 없습니다. 또한 병렬 루프의 시작 부분에있는 각 변수에 있어야하는 값이 있는지 확인했습니다. 그래서 병렬 섹션에서 하나 이상의 코어를 사용하여 프로그램을 실행할 때 다른 값을 얻을 수 있습니다.

나는 이것이 앞으로 나아갈 것이 많지 않다는 것을 알고 있지만, 그럼에도 불구하고 여러분 중 일부는 아이디어가 있기를 바랍니다. 더 많은 정보가 필요하다면, 나에게 더 많이 제공 할 수 있기 때문에 언제든지 물어보십시오.

내가 궁금해하는 것들 : 결국 유해한 메모리 경쟁이 가능할 수 있습니까? 잘못 될 수있는 다른 것을 본 적이 있습니까? 멀티 코어 프로그램을 확인하는 데 사용할 수있는 (상대적으로) 사용하기 쉬운 프로그램을 알고 있습니까?

+0

여기에 분명히 누락 된 것이 있습니다. 병렬 루프 컨텍스트에서 스레드 ID를 가져와 사용하는 이유는 무엇입니까? 루프와 완전히 독립적 인 calc 변수에 액세스하고 있습니까? 나중에 다른 곳으로 병합됩니까?최소한 스레드 ID를 가져 와서 스레드 별 작업을 수행하는 omp 병렬 영역과 스레드 독립적 작업을 분산시키는 작업 공유 구조를 위해 포함 된 omp 병렬 영역으로 분할해야합니다. – njustn

+0

calc 변수는 병렬 for 루프 다음에 실제로 병합됩니다. 나는 또한 당신의 마지막 발언을 이해한다고 생각하지만, '문제'는 for 루프 동안 스레드 특정 작업 (예 : calc [thread_id] .iesc는 새로운 값과 같은 것을 얻는다. 그래서 내가 보는 방법은 for 루프 스레드를 독립적으로 만들 수 없습니다. 하지만 아마도 내가 너를 오해하고있다. 내가 원래의 질문에서 언급 한 코드를 사용하여 나눌 수있는 병렬 코드 예제와 지역 코드 예제를 작성할 수 있습니까? – 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 회 동안 실제로 합산되거나 누적 된 값이다. 기본적으로 그들은 특정 이벤트의 발생을 표시하는 카운터입니다. 이 일을해야합니까? 내가 사용해야 할 수도있는 절제 조항을 읽었지만 실제로 이해하지는 못합니다. 너 더 이상 조언 해 줄래? 지금까지 도와 줘서 고마워! – user2257570

+0

지연에 대한 사과, 나는 독일에서 돌아왔다. 누적 된 변수를 보호 할 필요가 있습니다. 문제는 foo ++와 같은 명령문이 실제로 레지스터에 대한 읽기, 추가, 다음 쓰기로 데이터 경주를 도입한다는 것입니다. reduction 절을 사용하려면 병렬 처리를 내부 함수로 옮겨야 할 필요가있을 것입니다. 그것들의 개인 복사본을 만드는 것이 더 합리적 일 수 있습니다. 그리고 calc 배열을 이미 마쳤 으면 마지막에 그들을 결합하십시오. – njustn