JCUDA에서 작업하는 데 문제가 있습니다. CUFFT 라이브러리를 사용하여 1D FFT를 만드는 작업이 있지만 결과는 2가되어야합니다. 그래서 CUFFT_R2C 유형의 1D FFT를 만들기로했습니다. 다음에 책임지는 클래스 :JCuda. 이미 사용 된 포인터 재사용
public class FFTTransformer {
private Pointer inputDataPointer;
private Pointer outputDataPointer;
private int fftType;
private float[] inputData;
private float[] outputData;
private int batchSize = 1;
public FFTTransformer (int type, float[] inputData) {
this.fftType = type;
this.inputData = inputData;
inputDataPointer = new CUdeviceptr();
JCuda.cudaMalloc(inputDataPointer, inputData.length * Sizeof.FLOAT);
JCuda.cudaMemcpy(inputDataPointer, Pointer.to(inputData),
inputData.length * Sizeof.FLOAT, cudaMemcpyKind.cudaMemcpyHostToDevice);
outputDataPointer = new CUdeviceptr();
JCuda.cudaMalloc(outputDataPointer, (inputData.length + 2) * Sizeof.FLOAT);
}
public Pointer getInputDataPointer() {
return inputDataPointer;
}
public Pointer getOutputDataPointer() {
return outputDataPointer;
}
public int getFftType() {
return fftType;
}
public void setFftType(int fftType) {
this.fftType = fftType;
}
public float[] getInputData() {
return inputData;
}
public int getBatchSize() {
return batchSize;
}
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
public float[] getOutputData() {
return outputData;
}
private void R2CTransform() {
cufftHandle plan = new cufftHandle();
JCufft.cufftPlan1d(plan, inputData.length, cufftType.CUFFT_R2C, batchSize);
JCufft.cufftExecR2C(plan, inputDataPointer, outputDataPointer);
JCufft.cufftDestroy(plan);
}
private void C2CTransform(){
cufftHandle plan = new cufftHandle();
JCufft.cufftPlan1d(plan, inputData.length, cufftType.CUFFT_C2C, batchSize);
JCufft.cufftExecC2C(plan, inputDataPointer, outputDataPointer, fftType);
JCufft.cufftDestroy(plan);
}
public void transform(){
if (fftType == JCufft.CUFFT_FORWARD) {
R2CTransform();
} else {
C2CTransform();
}
}
public float[] getFFTResult() {
outputData = new float[inputData.length + 2];
JCuda.cudaMemcpy(Pointer.to(outputData), outputDataPointer,
outputData.length * Sizeof.FLOAT, cudaMemcpyKind.cudaMemcpyDeviceToHost);
return outputData;
}
public void releaseGPUResources(){
JCuda.cudaFree(inputDataPointer);
JCuda.cudaFree(outputDataPointer);
}
public static void main(String... args) {
float[] inputData = new float[65536];
for(int i = 0; i < inputData.length; i++) {
inputData[i] = (float) Math.sin(i);
}
FFTTransformer transformer = new FFTTransformer(JCufft.CUFFT_FORWARD, inputData);
transformer.transform();
float[] result = transformer.getFFTResult();
HilbertSpectrumTicksKernelInvoker.multiplyOn2(transformer.getOutputDataPointer(), inputData.length+2);
transformer.releaseGPUResources();
}
}
책임있는 방법은 쿠다 커널 기능을 사용합니다. Java 메소드 코드 :
public static void multiplyOn2(Pointer inputDataPointer, int dataSize){
// Enable exceptions and omit all subsequent error checks
JCudaDriver.setExceptionsEnabled(true);
// Create the PTX file by calling the NVCC
String ptxFileName = null;
try {
ptxFileName = FileService.preparePtxFile("resources\\HilbertSpectrumTicksKernel.cu");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Initialize the driver and create a context for the first device.
cuInit(0);
CUdevice device = new CUdevice();
cuDeviceGet(device, 0);
CUcontext context = new CUcontext();
cuCtxCreate(context, 0, device);
// Load the ptx file.
CUmodule module = new CUmodule();
cuModuleLoad(module, ptxFileName);
// Obtain a function pointer to the "add" function.
CUfunction function = new CUfunction();
cuModuleGetFunction(function, module, "calcSpectrumSamples");
// Set up the kernel parameters: A pointer to an array
// of pointers which point to the actual values.
int N = (dataSize + 1)/2 + 1;
int pair = (dataSize + 1) % 2 > 0 ? 1 : -1;
Pointer kernelParameters = Pointer.to(Pointer.to(inputDataPointer),
Pointer.to(new int[] { dataSize }),
Pointer.to(new int[] { N }), Pointer.to(new int[] { pair }));
// Call the kernel function.
int blockSizeX = 128;
int gridSizeX = (int) Math.ceil((double) dataSize/blockSizeX);
cuLaunchKernel(function, gridSizeX, 1, 1, // Grid dimension
blockSizeX, 1, 1, // Block dimension
0, null, // Shared memory size and stream
kernelParameters, null // Kernel- and extra parameters
);
cuCtxSynchronize();
// Allocate host output memory and copy the device output
// to the host.
float freq[] = new float[dataSize];
cuMemcpyDtoH(Pointer.to(freq), (CUdeviceptr)inputDataPointer, dataSize
* Sizeof.FLOAT);
그리고 커널 함수는 다음과 같습니다
extern "C"
__global__ void calcSpectrumSamples(float* complexData, int dataSize, int N, int pair) {
int i = threadIdx.x + blockIdx.x * blockDim.x;
if(i >= dataSize) return;
complexData[i] = complexData[i] * 2;
}
하지만 난에 (장치 메모리) FFT의 결과를 가리키는 포인터를 전달하기 위해 노력하고있어 때 multiplyOn2 메서드를 호출하면 cuCtxSynchronize() 호출에서 예외가 발생합니다. 예외 :
Exception in thread "main" jcuda.CudaException: CUDA_ERROR_UNKNOWN
at jcuda.driver.JCudaDriver.checkResult(JCudaDriver.java:263)
at jcuda.driver.JCudaDriver.cuCtxSynchronize(JCudaDriver.java:1709)
at com.ifntung.cufft.HilbertSpectrumTicksKernelInvoker.multiplyOn2(HilbertSpectrumTicksKernelInvoker.java:73)
at com.ifntung.cufft.FFTTransformer.main(FFTTransformer.java:123)
저는 Visual Studion C++를 사용하여 동일한 작업을 수행하려고했으나 아무런 문제가 없습니다. 저 좀 도와 주 시겠어요.
P. 이 prolem을 해결할 수는 있지만 장치 메모리에서 호스트 메모리로 데이터를 복사 한 다음 새 cuda 함수를 호출하기 전에 매번 새로운 포인터를 작성하여 복사해야하므로 프로그램 실행 속도가 느려집니다.