2012-03-24 2 views
2

여기 [0,1] 간격으로 4.0/(1+x^2) 함수의 통합을 수행하는이 Open MP 코드가 있습니다. 이것에 대한 분석적인 대답은 pi = 3.14159...12 개의 스레드를 사용하여 시작할 때 잘못된 대답을주는 OpenMP 코드

입니다. 함수를 통합하는 방법은 단순한 근사식 리만 합에 불과합니다. 이제 코드는 11 개의 OpenMP 스레드까지 최대 1 개의 OpenMP 스레드를 사용할 때 정확한 대답을 제공합니다.

그러나 OpenMP 스레드 12 개 이상을 사용하면 점점 잘못된 대답을하기 시작합니다. 왜 이런 일이 일어날 수 있습니까? 우선 여기에 C++ 코드가 있습니다. 우분투 10.10 환경에서 gcc를 사용하고 있습니다. 코드가 g++ -fopenmp integration_OpenMP.cpp

// f(x) = 4/(1+x^2) 
// Domain of integration: [0,1] 
// Integral over the domain = pi =(approx) 3.14159 

#include <iostream> 
#include <omp.h> 
#include <vector> 
#include <algorithm> 
#include <functional> 
#include <numeric> 


int main (void) 
{ 
    //Information common to serial and parallel computation. 
    int num_steps = 2e8; 
    double dx  = 1.0/num_steps; 


    //Serial Computation: Method pf integration is just a plain Riemann sum 
    double start = omp_get_wtime(); 

    double serial_sum = 0; 
    double x   = 0; 
    for (int i=0;i< num_steps; ++i) 
     { 
     serial_sum += 4.0*dx/(1.0+x*x); 
       x += dx; 
    } 

    double end = omp_get_wtime(); 
    std::cout << "Time taken for the serial computation: "  << end-start   << " seconds"; 
    std::cout << "\t\tPi serial: "        << serial_sum  << std::endl; 





    //OpenMP computation. Method of integration, just a plain Riemann sum 
    std::cout << "How many OpenMP threads do you need for parallel computation? "; 
    int t;//number of openmp threads 
    std::cin >> t; 

    start = omp_get_wtime(); 
    double parallel_sum = 0; //will be modified atomically 
    #pragma omp parallel num_threads(t) 
    { 
     int threadIdx = omp_get_thread_num(); 
     int begin = threadIdx * num_steps/t; //integer index of left end point of subinterval 
     int end = begin + num_steps/t; // integer index of right-endpoint of sub-interval 
     double dx_local = dx; 
     double temp = 0; 
     double x = begin*dx; 

     for (int i = begin; i < end; ++i) 
    {  
     temp += 4.0*dx_local/(1.0+x*x); 
     x += dx_local; 
    } 
    #pragma omp atomic 
     parallel_sum += temp; 
    } 
    end = omp_get_wtime(); 
    std::cout << "Time taken for the parallel computation: " << end-start << " seconds"; 
    std::cout << "\tPi parallel: "        << parallel_sum  << std::endl; 

    return 0; 
} 

컴파일 여기 11 개 실로부터 다른 스레드의 수에 대해 출력된다.

OpenMP: ./a.out 
Time taken for the serial computation: 1.27744 seconds  Pi serial: 3.14159 
How many OpenMP threads do you need for parallel computation? 11 
Time taken for the parallel computation: 0.366467 seconds Pi parallel: 3.14159 
OpenMP: 
OpenMP: 
OpenMP: 
OpenMP: 
OpenMP: 
OpenMP: ./a.out 
Time taken for the serial computation: 1.28167 seconds  Pi serial: 3.14159 
How many OpenMP threads do you need for parallel computation? 12 
Time taken for the parallel computation: 0.351284 seconds Pi parallel: 3.16496 
OpenMP: 
OpenMP: 
OpenMP: 
OpenMP: 
OpenMP: 
OpenMP: ./a.out 
Time taken for the serial computation: 1.28178 seconds  Pi serial: 3.14159 
How many OpenMP threads do you need for parallel computation? 13 
Time taken for the parallel computation: 0.434283 seconds Pi parallel: 3.21112 


OpenMP: ./a.out 
Time taken for the serial computation: 1.2765 seconds  Pi serial: 3.14159 
How many OpenMP threads do you need for parallel computation? 14 
Time taken for the parallel computation: 0.375078 seconds Pi parallel: 3.27163 
OpenMP: 

답변

4

parallel for 대신 정적 파티션을 사용해야하는 이유는 무엇입니까?

#pragma omp parallel shared(dx) num_threads(t) 
{ 
    double x = omp_get_thread_num() * 1.0/t; 

    #pragma omp for reduction(+ : parallel_Sum) 
    for (int i = 0; i < num_steps; ++i) 
    {  
     parallel_Sum += 4.0*dx/(1.0+x*x); 
     x += dx; 
    } 
} 

그런 다음 결과의 모든 파티션 및 원자 집합을 직접 관리하지 않아도됩니다.

x을 올바르게 초기화하기 위해 x = (begin * dx) = (threadIdx * num_steps/t) * (1.0/num_steps) = (threadIdx * 1.0)/t을 확인했습니다.

편집 : 내 컴퓨터에서이 최종 버전을 테스트했는데 제대로 작동하는 것 같습니다.

+0

Better : 이제는 사용하기 전에'x'를 올바르게 초기화해야하며 여러분의 게시물을'+ 1' 할 수 있습니다 :) – Lol4t0

+0

@ Lol4t0 : 이제는 괜찮을 것 같습니다. – Tudor

2

문제는 begin을 계산이다 : 그래서 당신의 start 잘못 계산됩니다 threadIdx==11, num_steps * threadIdx 32 비트 정수 오버 플로우로 이어질 것입니다

당신이 num_steps = 2e8을 설정하면서

,.

threadIdx, beginend에 대해 long long int을 사용하는 것이 좋습니다.

편집 :

또한 시작과 끝이 단계 (정밀)으로 이어질 수 계산 당신의 방법이 손실 될 수 있음을주의. 예를 들어, 313 스레드의 경우 199 걸음을 풀었습니다.

오른쪽 방법은 시작 계산과 끝은 다음과 같습니다

long long int begin = threadIdx * num_steps/t; 
long long int end = (threadIdx + 1) * num_steps/t; 

이 같은 이유로, 당신은 괄호로 트릭을 할 수는 없지만, long long을 사용해야합니다.

+0

@Tudor : 아니요, 잘림 오류가 발생합니다. –

관련 문제