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



我正在为python创建一个GPU加速卷积例程,该例程后端为C,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.co中,我用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方面相对有经验。我在这里的大部分代码都改编自在线教程和其他人的代码。我欢迎所有关于为什么会出现这种情况以及可以采取什么措施来避免这个问题的建议。


2017年10月31日编辑

如果我用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))

在这之后,我可以将它与大型阵列一起使用,绝对没有问题。然而,如果我在没有进行"预热"的情况下加载模块,我会出现seg故障。

这实际上不是segfault问题的解决方案,而是找到segfault背后真正罪魁祸首的一种方法。

检查代码时没有错误。您希望如何查找错误?

使用以下函数包装所有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 %dn", 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));
}
}

最新更新