cublasXt矩阵乘法在C ++中成功,在Python中失败

时间:2017-11-24 04:55:38

标签: python cuda ctypes cublas

我试图在Ubuntu Linux 16.04上用Python 2.7.14中的ctypess包装CUDA 9.0中的cublasXt*gemm函数。这些函数接受主机内存中的数组作为它们的一些参数。我已经能够在C ++中成功使用它们,如下所示:

#include <iostream>
#include <cstdlib>
#include "cublasXt.h"
#include "cuda_runtime_api.h"

void rand_mat(float* &x, int m, int n) {
    x = new float[m*n];
    for (int i=0; i<m; ++i) {
        for (int j=0; j<n; ++j) {
            x[i*n+j] = ((float)rand())/RAND_MAX;
        }
    }
}

int main(void) {
    cublasXtHandle_t handle;
    cublasXtCreate(&handle);

    int devices[1] = {0};
    if (cublasXtDeviceSelect(handle, 1, devices) !=
        CUBLAS_STATUS_SUCCESS) {
        std::cout << "initialization failed" << std::endl; 
        return 1;
    }

    float *a, *b, *c;
    int m = 4, n = 4, k = 4;

    rand_mat(a, m, k);
    rand_mat(b, k, n);
    rand_mat(c, m, n);

    float alpha = 1.0;
    float beta = 0.0;

    if (cublasXtSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
                      m, n, k, &alpha, a, m, b, k, &beta, c, m) != 
           CUBLAS_STATUS_SUCCESS) {
        std::cout << "matrix multiply failed" << std::endl; 
        return 1;
    }
    delete a; delete b; delete c;
    cublasXtDestroy(handle);
}

但是,当我尝试按如下方式将它们包装在Python中时,我在cublasXt*gemm调用时遇到了段错误:

import ctypes
import numpy as np

_libcublas = ctypes.cdll.LoadLibrary('libcublas.so')
_libcublas.cublasXtCreate.restype = int
_libcublas.cublasXtCreate.argtypes = [ctypes.c_void_p]
_libcublas.cublasXtDestroy.restype = int
_libcublas.cublasXtDestroy.argtypes = [ctypes.c_void_p]
_libcublas.cublasXtDeviceSelect.restype = int
_libcublas.cublasXtDeviceSelect.argtypes = [ctypes.c_void_p,
                                            ctypes.c_int,
                                            ctypes.c_void_p]
_libcublas.cublasXtSgemm.restype = int
_libcublas.cublasXtSgemm.argtypes = [ctypes.c_void_p,
                                     ctypes.c_int,
                                     ctypes.c_int,
                                     ctypes.c_int,
                                     ctypes.c_int,
                                     ctypes.c_int,
                                     ctypes.c_void_p,
                                     ctypes.c_void_p,
                                     ctypes.c_int,
                                     ctypes.c_void_p,
                                     ctypes.c_int,
                                     ctypes.c_void_p,
                                     ctypes.c_void_p,
                                     ctypes.c_int]

handle = ctypes.c_void_p()
_libcublas.cublasXtCreate(ctypes.byref(handle))
deviceId = np.array([0], np.int32)
status = _libcublas.cublasXtDeviceSelect(handle, 1,
                                         deviceId.ctypes.data)
if status:
    raise RuntimeError

a = np.random.rand(4, 4).astype(np.float32)
b = np.random.rand(4, 4).astype(np.float32)
c = np.zeros((4, 4), np.float32)

status = _libcublas.cublasXtSgemm(handle, 0, 0, 4, 4, 4,
                                  ctypes.byref(ctypes.c_float(1.0)),
                                  a.ctypes.data, 4, b.ctypes.data, 4, 
                                  ctypes.byref(ctypes.c_float(0.0)),
                                  c.ctypes.data, 4)
if status:
    raise RuntimeError
print 'success? ', np.allclose(np.dot(a.T, b.T).T, c_gpu.get())
_libcublas.cublasXtDestroy(handle)

奇怪的是,如果我稍微修改它们以接受我已转移到GPU的pycuda.gpuarray.GPUArray矩阵,那么上面的Python包装器就可以工作了。有什么想法,为什么我在将主机内存传递给函数时只在Python中遇到段错误?

1 个答案:

答案 0 :(得分:2)

这些Xt<t>gemm函数的CUBLAS文档中似乎存在错误。至少从CUDA 8开始,参数mnkldaldbldc都是size_t类型1}}。通过查看头文件cublasXt.h可以发现这一点。

您的包装器的以下修改似乎对我有效:

$ cat t1340.py
import ctypes
import numpy as np

_libcublas = ctypes.cdll.LoadLibrary('libcublas.so')
_libcublas.cublasXtCreate.restype = int
_libcublas.cublasXtCreate.argtypes = [ctypes.c_void_p]
_libcublas.cublasXtDestroy.restype = int
_libcublas.cublasXtDestroy.argtypes = [ctypes.c_void_p]
_libcublas.cublasXtDeviceSelect.restype = int
_libcublas.cublasXtDeviceSelect.argtypes = [ctypes.c_void_p,
                                            ctypes.c_int,
                                            ctypes.c_void_p]
_libcublas.cublasXtSgemm.restype = int
_libcublas.cublasXtSgemm.argtypes = [ctypes.c_void_p,
                                     ctypes.c_int,
                                     ctypes.c_int,
                                     ctypes.c_size_t,
                                     ctypes.c_size_t,
                                     ctypes.c_size_t,
                                     ctypes.c_void_p,
                                     ctypes.c_void_p,
                                     ctypes.c_size_t,
                                     ctypes.c_void_p,
                                     ctypes.c_size_t,
                                     ctypes.c_void_p,
                                     ctypes.c_void_p,
                                     ctypes.c_size_t]

handle = ctypes.c_void_p()
_libcublas.cublasXtCreate(ctypes.byref(handle))
deviceId = np.array([0], np.int32)
status = _libcublas.cublasXtDeviceSelect(handle, 1,
                                         deviceId.ctypes.data)
if status:
    raise RuntimeError

a = np.random.rand(4, 4).astype(np.float32)
b = np.random.rand(4, 4).astype(np.float32)
c = np.zeros((4, 4), np.float32)
alpha = ctypes.c_float(1.0)
beta = ctypes.c_float(0.0)

status = _libcublas.cublasXtSgemm(handle, 0, 0, 4, 4, 4,
                                 ctypes.byref(alpha),
                                 a.ctypes.data, 4, b.ctypes.data, 4,
                                 ctypes.byref(beta),
                                 c.ctypes.data, 4)
if status:
    raise RuntimeError
print 'success? ', np.allclose(np.dot(a.T, b.T).T, c)
_libcublas.cublasXtDestroy(handle)
$ python t1340.py
success?  True
$

枚举我所做的更改:

  1. 针对{{argtypesmnkldaldb参数更改了ldc 1}}从cublasXtSgemmc_int
  2. 为您的alpha和beta参数提供了显式变量;这可能是无关紧要的
  3. 在您的c_size_t功能中,将np.allclose更改为c_gpu.get
  4. 以上是在CUDA 8和CUDA 9上测试的。我已经向NVIDIA提交了内部错误,以更新文档(即使当前的CUDA 9文档也没有反映头文件的当前状态。)