Каково значение аргумента sharedMemBytes в вызове ядра cuLaunchKernel()?

Я пытаюсь реализовать простую программу умножения матриц, используя общую память в JCuda.

Ниже приведен мой код JCudaSharedMatrixMul.java:

import static jcuda.driver.JCudaDriver.cuCtxCreate;
import static jcuda.driver.JCudaDriver.cuCtxSynchronize;
import static jcuda.driver.JCudaDriver.cuDeviceGet;
import static jcuda.driver.JCudaDriver.cuInit;
import static jcuda.driver.JCudaDriver.cuLaunchKernel;
import static jcuda.driver.JCudaDriver.cuMemAlloc;
import static jcuda.driver.JCudaDriver.cuMemFree;
import static jcuda.driver.JCudaDriver.cuMemcpyDtoH;
import static jcuda.driver.JCudaDriver.cuMemcpyHtoD;
import static jcuda.driver.JCudaDriver.cuModuleGetFunction;
import static jcuda.driver.JCudaDriver.cuModuleLoad;
import static jcuda.runtime.JCuda.cudaEventCreate;
import static jcuda.runtime.JCuda.cudaEventRecord;
import static jcuda.runtime.JCuda.*;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

import jcuda.Pointer;
import jcuda.Sizeof;
import jcuda.driver.CUcontext;
import jcuda.driver.CUdevice;
import jcuda.driver.CUdeviceptr;
import jcuda.driver.CUfunction;
import jcuda.driver.CUmodule;
import jcuda.driver.JCudaDriver;
import jcuda.runtime.cudaEvent_t;



public class JCudaSharedMatrixMul
{

    public static void main(String[] args) throws IOException 
    {
        // Enable exceptions and omit all subsequent error checks
        JCudaDriver.setExceptionsEnabled(true);

        // Create the PTX file by calling the NVCC
        String ptxFilename = preparePtxFile("JCudaSharedMatrixMulKernel.cu");

        //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 PTX file
        CUmodule module = new CUmodule();
        cuModuleLoad(module,ptxFilename);

        //Obtain a function pointer to the Add function
        CUfunction function = new CUfunction();
        cuModuleGetFunction(function, module, "jCudaSharedMatrixMulKernel");

        int numRows = 16;
        int numCols = 16;

        //Allocate and fill Host input Matrices:
        float hostMatrixA[] = new float[numRows*numCols];
        float hostMatrixB[] = new float[numRows*numCols];
        float hostMatrixC[] = new float[numRows*numCols];


        for(int i = 0; i<numRows; i++)

        {
            for(int j = 0; j<numCols; j++)
            {
                hostMatrixA[i*numCols+j] = (float) 1;
                hostMatrixB[i*numCols+j] = (float) 1;
            }
        }
        // Allocate the device input data, and copy the
        // host input data to the device
        CUdeviceptr devMatrixA = new CUdeviceptr();
        cuMemAlloc(devMatrixA, numRows * numCols * Sizeof.FLOAT);

        //This is the part where it gives me the error
        cuMemcpyHtoD(devMatrixA, Pointer.to(hostMatrixA), numRows * numCols * Sizeof.FLOAT);

        CUdeviceptr devMatrixB = new CUdeviceptr();
        cuMemAlloc(devMatrixB, numRows * numCols * Sizeof.FLOAT);

        //This is the part where it gives me the error
        cuMemcpyHtoD(devMatrixB, Pointer.to(hostMatrixB ), numRows * numCols * Sizeof.FLOAT);

        //Allocate device matrix C to store output
        CUdeviceptr devMatrixC = new CUdeviceptr();
        cuMemAlloc(devMatrixC, numRows * numCols * Sizeof.FLOAT);

        // Set up the kernel parameters: A pointer to an array
        // of pointers which point to the actual values.

        Pointer kernelParameters = Pointer.to(
                                Pointer.to(new int[]{numCols}), 
                                Pointer.to(devMatrixA),
                                Pointer.to(devMatrixB),
                                Pointer.to(devMatrixC));

        //Kernel thread configuration
        int blockSize = 16;
        int gridSize = 1;

        cudaEvent_t start = new cudaEvent_t();
        cudaEvent_t stop = new cudaEvent_t();
        cudaEventCreate(start);
        cudaEventCreate(stop);
        long start_nano=System.nanoTime();
        cudaEventRecord(start, null);

        cuLaunchKernel(function, 
                       gridSize, 1, 1,
                       blockSize, 16, 1,
                       250, null, kernelParameters, null);

        cuCtxSynchronize();
        cudaEventRecord(stop, null);
        long end_nano=System.nanoTime();
        float elapsedTimeMsArray[] = { Float.NaN };
        cudaEventElapsedTime(elapsedTimeMsArray, start, stop);
        float elapsedTimeMs = elapsedTimeMsArray[0];
        System.out.println("Time Required (Using cudaevent elapsed time) = " + " " +elapsedTimeMs+
                "Time Required (Using nanotime)= "+(end_nano-start_nano)/1000000);
        // Allocate host output memory and copy the device output
        // to the host.

        //This is the part where it gives me the error
        cuMemcpyDtoH(Pointer.to(hostMatrixC), devMatrixC, numRows * numCols * Sizeof.FLOAT);



        //verify the result
        for (int i =0; i<numRows; i++)
        {
            for (int j =0; j<numRows; j++)
            {
                System.out.print("   "+ hostMatrixC[i*numCols+j]);
            }
            System.out.println("");
        }

        cuMemFree(devMatrixA);
        cuMemFree(devMatrixB);
        cuMemFree(devMatrixC);
    }

    private static String preparePtxFile(String cuFileName) throws IOException
    {
        int endIndex = cuFileName.lastIndexOf('.');
        if (endIndex == -1)
            endIndex = cuFileName.length()-1;
        {
        }
        String ptxFileName = cuFileName.substring(0, endIndex+1)+"ptx";
        File ptxFile = new File(ptxFileName);
        if (ptxFile.exists())
        {
            return ptxFileName;
        }

        File cuFile = new File(cuFileName);
        if (!cuFile.exists())
        {
            throw new IOException("Input file not found: "+cuFileName);
        }
        String modelString = "-m"+System.getProperty("sun.arch.data.model");
        String command = "nvcc " + modelString + " -ptx "+ cuFile.getPath()+" -o "+ptxFileName;

        System.out.println("Executing\n"+command);
        Process process = Runtime.getRuntime().exec(command);

        String errorMessage = new String(toByteArray(process.getErrorStream()));
        String outputMessage = new String(toByteArray(process.getInputStream()));
        int exitValue = 0;
        try
        {
            exitValue = process.waitFor();
        }
        catch (InterruptedException e)
        {
            Thread.currentThread().interrupt();
            throw new IOException(
                "Interrupted while waiting for nvcc output", e);
        }

        if (exitValue != 0)
        {
            System.out.println("nvcc process exitValue "+exitValue);
            System.out.println("errorMessage:\n"+errorMessage);
            System.out.println("outputMessage:\n"+outputMessage);
            throw new IOException(
                "Could not create .ptx file: "+errorMessage);
        }

        System.out.println("Finished creating PTX file");
        return ptxFileName;

    }

    private static byte[] toByteArray(InputStream inputStream) throws IOException
    {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte buffer[] = new byte[8192];
        while (true)
        {
            int read = inputStream.read(buffer);
            if (read == -1)
            {
                break;
            }
            baos.write(buffer, 0, read);
        }
        return baos.toByteArray();
    }


}

Ниже приведен мой код JCudaSharedMatrixMulKernel.cu:

extern "C"
__global__ void jCudaSharedMatrixMulKernel(int N,float *ad,float *bd,float *cd)
{
    float pvalue=0;
    int TILE=blockDim.x;
    int ty=threadIdx.y;
    int tx=threadIdx.x;

    __shared__ float ads[4][4];
    __shared__ float bds[4][4];

    int Row = blockIdx.y * blockDim.y + threadIdx.y;
    int Col = blockIdx.x * blockDim.x + threadIdx.x;

    for(int i=0;i< N/TILE;++i)
    {
        ads[ty][tx] = ad[Row * N + (i * TILE) + tx];
        bds[ty][tx] = bd[(i * TILE + ty) * N + Col];

        __syncthreads();

        for(int k=0;k<TILE;k++)
                pvalue += ads[ty][k] * bds[k][tx];

        __syncthreads();  
    }

    cd[Row * N + Col] = pvalue;
}

В приведенном выше примере общая общая память, используемая на блок, составляет 2*4*4*4 = 128 байт. В cuLaunchKernel, когда я определяю параметр sharedMemBytes как 0(ноль), это дает мне следующую ошибку:

**Exception in thread "main" jcuda.CudaException: CUDA_ERROR_LAUNCH_FAILED
    at jcuda.driver.JCudaDriver.checkResult(JCudaDriver.java:282)
    at jcuda.driver.JCudaDriver.cuCtxSynchronize(JCudaDriver.java:1795)
    at JCudaSharedMatrixMul.main(JCudaSharedMatrixMul.java:121)**

Когда я определяю это как 128, тогда это дает ту же самую ошибку выше. Но когда я делаю это как 129 тогда, это дает мне правильный вывод! Когда я даю любое значение от 129 до 49024, это дает мне правильный результат. У меня вопрос, почему я не могу получить правильный вывод, когда я определяю его как 128? И какой максимальный общий объем памяти можно определить? Почему этот диапазон 129-49024 работает здесь?

1 ответ

Решение

Вы запускаете блоки потоков 16х16:

    cuLaunchKernel(function, 
                   gridSize, 1, 1,
                   blockSize, 16, 1,  <-- the first two params are block.x and block.y
                   250, null, kernelParameters, null);

так __shared__ float ads[4][4]; не должно работать вообще. Например, эти строки кода ядра будут обращаться к этим общим массивам за пределами некоторых потоков:

    ads[ty][tx] = ad[Row * N + (i * TILE) + tx];
    bds[ty][tx] = bd[(i * TILE + ty) * N + Col];
         ^   ^
         |   tx goes from 0..15 for a 16x16 threadblock
         ty goes from 0..15 for a 16x16 threadblock

Ваш код не работает в этом отношении. Если вы запускаете свой код с cuda-memcheck он может поймать эти вне доступа, даже в вашем "преходящем" случае. Глядя на matrixMulDrv пример кода cuda, будет поучительным, и вы увидите, что распределение общей памяти 2*block_size*block_size, как и должно быть для вашего случая, но ваши определения общей памяти должны быть [16][16] не [4][4] Может случиться так, что гранулярность распределения общей памяти сработает, когда вы превысите 128 байтов, но в вашем коде есть дефект.

Ваши общие определения должны быть:

__shared__ float ads[16][16];
__shared__ float bds[16][16];

Поскольку вышеприведенные распределения являются статическими, а sharedMemBytes Параметр определяется как динамическое распределение разделяемой памяти, для этого примера вам не нужно выделять какую-либо (0 - это нормально) динамическую разделяемую память, и он все еще работает. Разница между статическим и динамическим рассматривается здесь.

Максимальная общая память на блок доступна в документации, или если вы запускаете CUDA deviceQuery образец кода. Это 48 Кбайт для cc2.0 и более новых устройств.

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