JCuda。重用已经使用过的指针

时间:2012-05-13 13:23:52

标签: cuda jcuda

我在使用JCUDA时遇到了麻烦。我有一个任务是使用CUFFT库进行一维FFT,但结果应该乘以2.所以我决定使用CUFFT_R2C类型进行一维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 ++做同样的事情并没有问题。你能帮我吗?

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

1 个答案:

答案 0 :(得分:1)

错误发生在哪一行?

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

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

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

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

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