JCuda. Повторное использование уже используемого указателя

У меня проблемы с работой с JCUDA. У меня есть задача сделать 1D FFT с использованием библиотеки CUFFT, но результат должен быть умножен на 2. Поэтому я решил сделать 1D FFT с типом CUFFT_R2C. Класс ответственный за это происходит следующим образом:

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, что замедляет выполнение моей программы.

1 ответ

Где именно происходит ошибка в какой строке?

Ошибка Cuda также может быть предыдущей ошибкой.

Почему вы используете Pointer.to(inputDataPointer), у вас уже есть этот указатель устройства. Теперь вы передаете указатель на устройство указатель на устройство?

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

Я также рекомендую использовать квалификатор "this" или любую другую маркировку для обнаружения переменных экземпляра. Я ненавижу и отказываюсь просматривать код, особенно такой, как вложенный и длинный, как ваш пример, если я не могу увидеть, в какой области видимости переменная в методах пытается отладить ее, просто прочитав ее.
Я не хочу спрашивать себя всегда, откуда, черт возьми, эта переменная.

Если сложный код в вопросе в SO не отформатирован должным образом, я его не читаю.

Другие вопросы по тегам