为什么我的代码(与CUDA链接)偶尔会导致Python中的分段错误?

时间:2017-10-29 22:10:19

标签: python cuda

我正在为python创建一个GPU加速卷积例程,它是C的后端,它利用Cuda访问GPU。为此,请使用以下C代码:

#include <cuda.h>
#include <cuda_runtime_api.h>


/* 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GPU device functions for GPU modules 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
__global__ void d_VectorConvolve(float *a, float *b, float *c, size_t n_a, size_t n_b, size_t half)
{
    size_t idx = blockIdx.x * blockDim.x + threadIdx.x;
    float val = 0.0;

    if (idx < n_a)
    {
        for (int j = 0; j < n_b; j++)
        {
            int check = idx - half + j; // this is needed to ensure we dont attempt to index
                        // a value outsize the size of a.
            if (check > 0 && check < n_a)
            {
                val = val + a[idx - half + j]*b[j];
            }   
        }
        c[idx] = val;
    }
}
extern "C" {
void VectorConvolve(float *a, float *b, float *c, size_t n_a, size_t n_b, size_t half)
{
    float *d_a, *d_b, *d_c;

    cudaMalloc( &d_a, n_a*sizeof(float));
    cudaMalloc( &d_b, n_b*sizeof(float));
    cudaMalloc( &d_c, n_a*sizeof(float));

    cudaMemcpy( d_a, a, n_a*sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy( d_b, b, n_b*sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy( d_c, c, n_a*sizeof(float), cudaMemcpyHostToDevice);

    d_VectorConvolve <<< ceil(n_a / 256.0), 256 >>> (d_a, d_b, d_c, n_a, n_b, half);

    cudaMemcpy( c, d_c, n_a*sizeof(float), cudaMemcpyDeviceToHost);

    cudaFree(d_a);
    cudaFree(d_b);
    cudaFree(d_c);
}
}

此文件保存为vector_functions.cu,我使用nvcc编译生成.so:

nvcc -Xcompiler -fPIC -shared -o vector_functions.so vector_functions.cu 

一切正常,代码在C中工作。我创建一个 init .py文件,该文件使用创建的.so文件:

def get_vector_functions():
    dll = ctypes.CDLL(current_dir + '/vector_functions.so', mode=ctypes.RTLD_GLOBAL)

    # convolve funtion
    vector_convolve = dll.VectorConvolve
    vector_convolve.argtypes = [POINTER(c_float), POINTER(c_float), POINTER(c_float), c_size_t, c_size_t, c_size_t]

    return vector_convolve

# create __cuda_sum function with get_cuda_sum()
__vector_convolve = get_vector_functions()

def cuda_convolve(a,b):
    a = a.astype('float32')
    b = b.astype('float32')

    a_shape = a.shape[0]
    b_shape = b.shape[0]

    half = int(b_shape/2.)

    a_p = a.ctypes.data_as(POINTER(c_float))
    b_p = b.ctypes.data_as(POINTER(c_float))
    c_p = np.zeros(a_shape).ctypes.data_as(POINTER(c_float))

    __vector_convolve(a_p, b_p, c_p, a_shape, b_shape,  half)
    c = make_nd_array(c_p, [a_shape], dtype=np.float32, order='C', own_data=True)
    return c

现在这个工作正常,我可以加载我的模块来快速进行大卷积。问题是偶尔会出现分段错误,我不明白为什么。一旦我得到这个,我就不能再使用这个模块,直到我重新启动计算机。

我认为我没有正确管理我的记忆?但奇怪的是,有时它工作得很好,然后突然失败了。我也觉得链接.so文件可能是一个坏主意,可能与它有关,但它是一个快速解决方案将python链接到C.

我在python和进入C方面比较有经验。我在这里的大部分代码都是根据在线教程和其他人的代码改编而来的。我欢迎所有关于为什么会这样做的建议,以及如何避免这个问题。

编辑31/10/2017

如果我使用python解释器运行一些预热命令,问题似乎就消失了:

cuda_convolve(np.ones(2**5), np.ones(100))
cuda_convolve(np.ones(2**10), np.ones(100))
cuda_convolve(np.ones(2**15), np.ones(100))
cuda_convolve(np.ones(2**18), np.ones(100))

在此之后,我可以将它与大型阵列一起使用,绝对没有问题。但是如果我在没有这样做的情况下加载模块&#34;预热&#34;,我会遇到一个段错误。

1 个答案:

答案 0 :(得分:1)

这实际上并不是解决您的段错误问题的方法,而是找到段错误背后的实际罪魁祸首的方法。

您的代码中没有错误检查。您期望如何找到错误?

使用以下函数包装所有CUDA调用:

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort=true)
{
   if (code != cudaSuccess) 
   {
      fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
      if (abort) exit(code);
   }
}

现在修改你的代码:

extern "C" {
void VectorConvolve(float *a, float *b, float *c, size_t n_a, size_t n_b, size_t half)
{
    float *d_a, *d_b, *d_c;

    gpuErrchk(cudaMalloc( &d_a, n_a*sizeof(float)));
    gpuErrchk(cudaMalloc( &d_b, n_b*sizeof(float)));
    gpuErrchk(cudaMalloc( &d_c, n_a*sizeof(float)));

    gpuErrchk(cudaMemcpy( d_a, a, n_a*sizeof(float), cudaMemcpyHostToDevice));
    gpuErrchk(cudaMemcpy( d_b, b, n_b*sizeof(float), cudaMemcpyHostToDevice));
    gpuErrchk(cudaMemcpy( d_c, c, n_a*sizeof(float), cudaMemcpyHostToDevice));

    d_VectorConvolve <<< ceil(n_a / 256.0), 256 >>> (d_a, d_b, d_c, n_a, n_b, half);

    // check if cuda kernel executed correctly
    gpuErrchk(cudaPeekAtLastError())
    // make sure kernel execution has ended
    gpuErrchk(cudaDeviceSynchronize())

    gpuErrchk(cudaMemcpy( c, d_c, n_a*sizeof(float), cudaMemcpyDeviceToHost));

    gpuErrchk(cudaFree(d_a));
    gpuErrchk(cudaFree(d_b));
    gpuErrchk(cudaFree(d_c));
}
}