0

我在使用 JCUDA 时遇到了麻烦。我的任务是使用 CUFFT 库进行 1D FFT,但结果应该乘以 2。所以我决定使用 CUFFT_R2C 类型进行 1D FFT。接下来负责这个的类:

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();
    }
}

负责乘法的方法使用 cuda 核函数。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++ 做同样的事情,这没有问题。请你帮助我好吗。

PS我可以解决这个问题,但是我需要将数据从设备内存复制到主机内存,然后在每次调用新的cuda函数之前通过创建新指针复制回来,这会减慢我的程序执行速度。

4

1 回答 1

1

错误究竟发生在哪一行?

Cuda 错误也可能是先前的错误。

为什么要使用 Pointer.to(inputDataPointer),你已经有了那个设备指针。现在你将一个指向设备的指针传递给设备?

Pointer kernelParameters = Pointer.to(Pointer.to(inputDataPointer),  

我还建议使用“this”限定符或任何其他标记来检测实例变量。如果我看不到方法中的变量试图通过阅读它来调试它的哪个范围,我讨厌并拒绝查看代码,尤其是嵌套的并且像你的示例一样长。
我不想总是问自己这个变量到底是从哪里来的。

如果 SO 问题中的复杂代码格式不正确,我不会阅读它。

于 2012-05-22T12:49:39.867 回答