2013-02-21 4 views
5

여러 FFT를 동시에 수행하려고합니다. 나는 FFTW와 OpenMP를 사용하고있다. 각 FFT가 다르기 때문에 FFTW의 멀티 스레딩 (OpenMP를 사용하는 것으로 알고 있음)에 의존하지 않습니다.OpenMP를 사용한 FFTW 계획 생성

int m; 

// assume: 
// int numberOfColumns = 100; 
// int numberOfRows = 100; 

#pragma omp parallel for default(none) private(m) shared(numberOfColumns, numberOfRows)// num_threads(4) 
    for(m = 0; m < 36; m++){ 

     // create pointers 
     double   *inputTest; 
     fftw_complex *outputTest; 
     fftw_plan  testPlan; 

     // preallocate vectors for FFTW 
     outputTest = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*numberOfRows*numberOfColumns); 
     inputTest = (double *)fftw_malloc(sizeof(double)*numberOfRows*numberOfColumns); 

     // confirm that preallocation worked 
     if (inputTest == NULL || outputTest == NULL){ 
      logger_.log_error("\t\t FFTW memory not allocated on m = %i", m); 
     } 

     // EDIT: insert data into inputTest 
     inputTest = someDataSpecificToThisIteration(m); // same size for all m 

     // create FFTW plan 
     #pragma omp critical (make_plan) 
     { 
      testPlan = fftw_plan_dft_r2c_2d(numberOfRows, numberOfColumns, inputTest, outputTest, FFTW_ESTIMATE); 
     } 

     // confirm that plan was created correctly 
     if (testPlan == NULL){ 
      logger_.log_error("\t\t failed to create plan on m = %i", m); 
     } 

     // execute plan 
     fftw_execute(testPlan); 

     // clean up 
     fftw_free(inputTest); 
     fftw_free(outputTest); 
     fftw_destroy_plan(testPlan); 

    }// end parallelized for loop 

이 모든 것이 정상적으로 작동합니다. 그러나 계획 작성 (fftw_plan_dft_r2c_2d)에서 중요한 구성을 제거하면 코드가 실패합니다. 왜 누군가가 설명 할 수 있습니까? fftw_plan_dft_r2c_2d는 실제로 "고아"가 아닙니다. 맞습니까? 두 스레드가 모두 numberOfRows 또는 numberOfColumns 메모리 위치를 동시에 지정하려고했기 때문입니까?

+0

fftw의 멀티 스레딩 기능을 사용하고 있지 않습니다. 실제로 36 개의 단일 스레드 변환을 병렬로 수행하고 있습니다. –

+0

알아. 내 첫 질문에서 말하기 _ 각 FFT가 다르기 때문에 FFTW의 멀티 쓰레딩 (build-in multithreading)에 의존하지 않습니다 ._ 36 개의 단일 스레드 변환을 병렬로 수행하고 싶습니다. – tir38

+0

죄송합니다, 저의 실수, 정확히 반대쪽을 읽었습니다 .- –

답변

7

는 그것은 거의 모든 FFTW 설명서에 대한 thread safety로 작성 :

...하지만 몇 가지주의가 필요하기 때문에 통화 및 계획의 계획 루틴 공유 데이터 (예를 들어, 지혜와 삼각 테이블).

결론은 FFTW의 스레드 안전 (재진입) 루틴은 fftw_execute (및 그 새로운 배열 변형)이라는 것입니다. 다른 모든 루틴 (예 : 플래너)은 한 번에 하나의 스레드에서만 호출되어야합니다. 예를 들어, 플래너 호출에 대한 세마포 잠금을 래핑 할 수 있습니다. 훨씬 간단하게, 당신은 하나의 스레드에서 모든 계획을 작성할 수 있습니다. 이것이 중요한 제한이라고 생각하지 않습니다. FFTW는 성능에 민감한 코드 만이 실제 변환을 실행하는 상황을 위해 설계되었으며 계획간에 공유되는 데이터의 이점은 뛰어납니다.

FFT 계획의 일반적인 응용 프로그램은 거의 구성되어 있지 않으므로 작성을 동기화해야하는지 여부는 중요하지 않습니다. 데이터의 차원이 변경되지 않는 한 각 반복마다 새 계획을 만들 필요가 없습니다. 지금 계획은 각 스레드와 fftw_execute()의 각 실행으로 감소 할 직렬화 오버 헤드에 한 번만 생성됩니다

#pragma omp parallel default(none) private(m) shared(numberOfColumns, numberOfRows) 
{ 
    // create pointers 
    double   *inputTest; 
    fftw_complex *outputTest; 
    fftw_plan  testPlan; 

    // preallocate vectors for FFTW 
    outputTest = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*numberOfRows*numberOfColumns); 
    inputTest = (double *)fftw_malloc(sizeof(double)*numberOfRows*numberOfColumns); 

    // confirm that preallocation worked 
    if (inputTest == NULL || outputTest == NULL){ 
     logger_.log_error("\t\t FFTW memory not allocated on m = %i", m); 
    } 

    // create FFTW plan 
    #pragma omp critical (make_plan) 
    testPlan = fftw_plan_dft_r2c_2d(numberOfRows, numberOfColumns, inputTest, outputTest, FFTW_ESTIMATE); 

    #pragma omp for 
    for (m = 0; m < 36; m++) { 
     // execute plan 
     fftw_execute(testPlan); 
    } 

    // clean up 
    fftw_free(inputTest); 
    fftw_free(outputTest); 
    fftw_destroy_plan(testPlan); 
} 

: 당신은 오히려 다음을 수행합니다. NUMA 시스템 (예 : 다중 소켓 AMD64 또는 Intel (post-) Nehalem 시스템)에서 실행중인 경우 최대 성능을 얻으려면 스레드 바인딩을 활성화해야합니다.

+0

방금 ​​설명서의 해당 부분을 읽었습니다 ... 나는 내 자신의 질문에 대답하기 위해 돌아 왔고 당신의 것을 보았습니다. 수표를받습니다. "데이터의 차원이 바뀌지 않는 한"하지만 차원이 같지만 값이 다른 경우 어떻게됩니까? 이를 반영하기 위해 원래 질문을 업데이트했습니다. – tir38

+0

@ tir38 그 이유는 계획을 여러 번 실행하기 때문입니다. 그렇지 않습니까? 입력 및 출력 배열을 다시 사용하는 한 단일 계획은 OK입니다. 그것이 포인터이기 때문에'inputTest'에 할당하지 마십시오. 'someDataSpecificToThisIteration (m, inputData)'와 같은 것을 가지고, 함수의 출력을'inputData'에 넣어 두는 것이 좋습니다. –

+0

죄송합니다. 나는 someDataSpecificToThisIteration [m]을 의미했다. 그것은 메서드 호출이 아니지만 일부 일반 배열에서 끌어 오기. 그래서 나는 단지'inputData'를 그 데이터를 가리키는 포인터로 삼고 있습니다. 36 개의 배열 항목에 36 개의 포인터가 효율적으로 있으므로 36 개의 계획이 필요합니다. 맞습니까? – tir38