CUDA 강의
홈 > CUDA > CUDA 강의 > cuFFT 강좌

cuFFT 강좌

 cuFFT는 NVIDIA에서 제공하는 CUDA를 이용한 GPU가속을 지원하는 FFT 지원 라이브러리입니다.  cuFFT의 특징은 CUDA 병렬화 기법을 모르더라도 NVIDIA에서 제공하는 최적화된 라이브러리를 통해 FFT를 가속시킬 수 있습니다. cuFFT는 최신 NVIDIA 그래픽드라이버와 Toolkit을 설치하면 바로 사용가능합니다.

 

아래의 파일에서 보는 것처럼 CUFFT 함수의 라이브러리인 cufft.lib 파일은 CUDA toolkit의 설치 디렉토리에 기본으로 내장되어 있습니다.  cufftemu.lib를 활용하면 CUDA 가능한 그래픽카드가 없는 경우에도 코딩 및 간단한 디버깅이 가능한 에뮬레이션 모드를 제공하여 쉽게 CUFFT를 테스트할  수 있습니다.


 

 

 

 

또한, 구체적인 사용법은 CUDA toolkit의 문서디렉토리에서 CUFFT에 대한 가이드를 확인하실 수 있습니다. 


 

CUFFT는 2.3버전부터 float, double, complex 타입까지 모두 지원하고 있습니다.  특히 주의하실 점은 CUFFT는 FFTW의 함수 형식과 설정 방법을 그대로 가져왔기 때문에 FFTW의 사용법에 대해 한번 살펴보실 것을 권장합니다. http://www.fftw.org 

 

또한, 만약 CUFFT의 소스코드가 필요하시면 http://developer.nvidia.com 을 가입하시면 CUFFT 1.1 버전의 소스코드를 다운로드하여 확인하실 수 있습니다. 특히, FFTW와 CUFFT 소스코드를 비교하시면 추후 자체적인 CUFFT 코드를 만드는 것도 가능할 것으로 생각됩니다.


 

 

이제 본격적으로 CUFFT를 사용해 봅시다.

 

1. 간단한 CUFFT예제 실행

이제 아래의 source.c 파일을 표준 C 프로젝트에서 작동되는 예제를 살펴보겠습니다.

#include

#include

#include

#include

#include

 

#define NX      256

#define BATCH   10

 

int main(int argc, char *argv[])

{

        cufftHandle plan;

        cufftComplex *devPtr;

        cufftComplex data[NX*BATCH];

        int i;

 

 

        /* source data creation */

        for(i=  0 ; i < NX*BATCH ; i++){

                data[i].x = 1.0f;

                data[i].y = 1.0f;

        }

 

        /* GPU memory allocation */

        cudaMalloc((void**)&devPtr, sizeof(cufftComplex)*NX*BATCH);

 

        /* transfer to GPU memory */

        cudaMemcpy(devPtr, data, sizeof(cufftComplex)*NX*BATCH, cudaMemcpyHostToDevice);

 

        /* creates 1D FFT plan */

        cufftPlan1d(&plan, NX, CUFFT_C2C, BATCH);

 

        /* executes FFT processes */

        cufftExecC2C(plan, devPtr, devPtr, CUFFT_FORWARD);

 

        /* executes FFT processes (inverse transformation) */

        cufftExecC2C(plan, devPtr, devPtr, CUFFT_INVERSE);

 

        /* transfer results from GPU memory */

        cudaMemcpy(data, devPtr, sizeof(cufftComplex)*NX*BATCH, cudaMemcpyDeviceToHost);

 

        /* deletes CUFFT plan */

        cufftDestroy(plan);

 

        /* frees GPU memory */

        cudaFree(devPtr);

 

        for(i = 0 ; i < NX*BATCH ; i++){

                printf("data[%d] %f %f\n", i, data[i].x, data[i].y);

        }

 

        return 0;

}

 

A. Windows 32 console Application 생성

Win32 Console Application을 생성하고 Empty Project를 만듭니다.


 

 

B. 헤더파일 넣기

C/C++ 표준으로 작업할 경우 NVCC를 사용하지 않고, 표준 C/C++ 컴파일러를 사용하기 때무넹 다음과 같은 header file을 넣어 주어야 합니다. 

 

#include

#include

#include

#include

#include

 

 

C. 환경설정

환경설정은 표준적인 C/C++ 환경설정과 크게 다르지 않습니다. 헤더파일, 라이브러리 디렉토리, 라이브러리 등록의 순서를 거치면 됩니다.

 

- C/C++ General에서 헤더파일 설정


 

 

- Linker General에서 lib 디렉토리 설정


 

 

-Linker Input에서 필요한 lib 등록


 

 

- Comple 및 빌드


 

에러없이 성공적으로 파일이 실행되는 것을 확인하실 수 있습니다.


 

 

2. SDK 예제인 SimpleCUFFT 사용

 기본적인 예제는 SDK의 simpleCUFFT예제를 통해 확인해 볼 수 있습니다. simpleCUFFT 예제는 CUFFT 함수와 자체 제작한 CUDA 함수를 같이 사용하는 예제를 포함하고 있습니다.  따라서, CUDA 병렬 프로그래밍을 익히 신 후 CUFFT와 다른 CUDA 병렬화된 알고리즘을 동시에 사용하는 경우 매우 유익한 예제가 될 수 있습니다. 본 예제의  솔루션 파일을 열고, property 설정 등을 살펴보시면 도움이 됩니다.


 

 

3. SDK 예제인 oceanFFT

oceanFFT또한 직접 작성한 소스코드(oceanFFT_kernel.cu) 와 CUFFT함수 (oceanFFT.cpp)를 어떻게 사용하는지에 대한 유용한 예제입니다. 그래픽 출력을 위한 노하우도 함께 제공합니다.


 

 

CUFFT 라이브러리를 사용하면, 기존의 알고리즘에 쉽게 CUDA 가속기능을 내장할 수 있습니다. FFTW에 익숙한 사용자라면 CUFFT를 쉽게 이용할 수 있습니다. 또한, Matlab Jacket Plug-in을 사용해도 쉽게 CUDA 가속된 FFT를 사용하실 수 있습니다. Jacket의 장점은 CUDA로 가속된 FFT 사용 이외에 편리한 사용자인터페이스를 제공하여 그래픽 출력 등이 용이합니다.  SW제품 : Matlab Jacket : 예제 를 살펴보시면 Jacket을 이용한 FFT 예제를 보실 수 있습니다.

 

 

3-1. dll파일 만들기

이제, CUFFT라이브러리를 이용한 dll 응용에 대해서 살펴보도록 합시다.

본예제는 KCUG의 질문에 대한 답변을 준비하면서 만들었습니다.

 

A. Win32 Project를 생성합니다.

 

 

B. 옵션에서 dll을 선택하고 empty project를 선택합니다.

 

C. DEF 파일을 추가합니다.

 

 

내용은 다음과 같습니다.

 

LIBRARY "cuFFT_dll"

 

EXPORTS

   useCUFFT   @1



 

 

 

D. 소스코드를 입력합니다.

현재 표준  C를 사용하므로 extern "C"는 사용하지 않았습니다.

사용할 소스코드는 다음과 같습니다. 앞에서 살펴본 예제에서 main함수가 빠진 대신 __declspec(dllexport) 문이 추가되었습니다.

 

특히 헤더파일은

#include

#include

#include

#include

#include

#include

CUDA, cuFFT 관련 헤더를  모두 포함하고 있습니다.

 

#include

#include

#include

#include

#include

#include

 

#define DllExport  __declspec( dllexport )

#define NX      256

#define BATCH   10

 

 

DllExport useCUFFT( );

 

 

DllExport useCUFFT( ){     

        cufftHandle plan;

        cufftComplex *devPtr;

        cufftComplex data[NX*BATCH]; // needs dynamic allocation

        int i;

 

        /* source data creation */

        for(i=  0 ; i < NX*BATCH ; i++){

                       data[i].x = 1.0f;

                       data[i].y = 1.0f + i*0.001;

        }

 

        /* GPU memory allocation */

        cudaMalloc((void**)&devPtr, sizeof(cufftComplex)*NX*BATCH);

 

        /* transfer to GPU memory */

        cudaMemcpy(devPtr, data, sizeof(cufftComplex)*NX*BATCH, cudaMemcpyHostToDevice);

 

        /* creates 1D FFT plan */

        cufftPlan1d(&plan, NX, CUFFT_C2C, BATCH);

 

        /* executes FFT processes */

        cufftExecC2C(plan, devPtr, devPtr, CUFFT_FORWARD);

 

        /* executes FFT processes (inverse transformation) */

        cufftExecC2C(plan, devPtr, devPtr, CUFFT_INVERSE);

 

        /* transfer results from GPU memory */

        cudaMemcpy(data, devPtr, sizeof(cufftComplex)*NX*BATCH, cudaMemcpyDeviceToHost);

 

        /* deletes CUFFT plan */

        cufftDestroy(plan);

 

        /* frees GPU memory */

        cudaFree(devPtr);

 

        for(i = 0 ; i < NX*BATCH ; i++){

                       printf("data[%d] %f %f\n", i, data[i].x, data[i].y);

        }

 

        return 0;

}

 

 

E. 필요한 헤더파일 위치, 라이브러리파일 위치, 추가 종속성관련 라이브러리를 등록합니다.

 

헤더 위치는 c:\CUDA\include 입니다.

 

 

종속성 디렉토리는 c:\CUDA\lib 입니다.



추가종속성은 cudart.lib cudafft.lib 입니다.


 

F. 컴파일 합니다.

정상적으로 컴파일되면 cuFFT_dll.dll 파일을 확인할 수 있습니다. (프로젝트명이 cuFFT_dll 임)

또한 cuFFT_dll.lib 파일도 확인합니다.


 

이제 dll이 완성되었습니다.

 

3-2. dll파일 사용하기

 

이제 dll을 사용합니다.

 

A. empty win32 console 프로젝트를 생성합니다.

 

사용한 소스코드는 다음과 같습니다. dll을 로드하는 방버도 있지만 여기서는 dll 함수만 선언했습니다.

 

#include

#include

 

#define DllImport   __declspec( dllimport )

 

 

DllImport useCUFFT( );

 

int main(int argc, char **argv){

       printf("dll import test\n");

       useCUFFT( );

       printf("successfully imported dll\n");

       return 0;

}

 

 

B. 종속성 설정

위에서처럼 dll 로딩 기능을 소스코드에 없앤 경우에는 아래처럼

생성된 프로젝트에 앞에서 만든 cuFFT_dll.lib 파일을 프로젝트 소스디렉토리에 넣습니다.

 

 

또한, 프로젝트의 추가종속성에는 cuFFT_dll.lib를 등록해줍니다.


 

 

C. 컴파일

 

컴파일하면 exe 파일이 생성됩니다. 앞에서 만든 cuFFT_dll.dll 파일을 실행파일이 있는 위치에 넣습니다.

 

 

아시겠지만 dll파일이 없을 경우 다음의 에러가 발생합니다.

 

 

dll이 정상로드도는 경우 아래와 같이 예제1에서의 결과와 동일한 결과를 출력합니다.

앞의 그림과 뒷의 그림이 다른 이유는

소스코드를  data[i].y = 1.0f 에서

         data[i].y = 1.0f + i*0.001; 로 변경했기 때문입니다.

 




 

 

그런데, 본 예제에서는

#define NX      256

#define BATCH   10

 

        cufftHandle plan;

        cufftComplex *devPtr;

        cufftComplex data[NX*BATCH]; // needs dynamic allocation

        int i;

를 사용하는데, NX, BATCH를 동적할당하는 경우 코드를 수정해 주어야 할 것으로 생각됩니다.

 

이제, dll을 사용하여 복잡한 문제를 해결할 경우 데이터 사이즈를 동적할당 시켜줘야 할 것 같네요.