尝试从 gpu 复制数据时出现 CudaAPIError 716

CudaAPIError 716 when trying to copy data from gpu

我正在学习 Numba 和 CUDA Python。我一直在关注一组 youtube tutorials 并且(我相信)理解了其中的原则。我的问题是从我的 GPU 复制计算值。我使用以下行来执行此操作:

aVals = retVal.copy_to_host()

我也试过使用这条线:

retVal.copy_to_host( aVals[:] )

两者都不起作用,并且都给出相同的错误:

numba.cuda.cudadrv.driver.CudaAPIError: [716] Call to cuMemcpyDtoH results in UNKNOWN_CUDA_ERROR

我有理由相信上面几行是问题所在,就好像我注释掉了代码运行没有错误的那一行一样。将数组从 GPU 复制到 CPU 时,我忽略了一些潜在问题吗?我是不是把数组搞砸了?

我的代码中有很多乱七八糟的地方,但这是一个简单的版本:

import numpy as np
import time
from math import sin, cos, tan, sqrt, pi, floor

from numba import vectorize, cuda

@cuda.jit('void(double[:],double[:],double[:],double)')
def CalculatePreValues(retVal,ecc, incl,  ke):
    i= cuda.grid(1)

    if i >= ecc.shape[0]:
        return

    retVal[i] = (ke/ecc[i])**(2/3)


def main():
    eccen = np.ones(num_lines, dtype=np.float32)
    inclin = np.ones(num_lines, dtype=np.float32)
    ke = 0.0743669161
    aVals = np.zeros(eccen.shape[0])
    start = time.time()
    retVal = cuda.device_array(aVals.shape[0])

    ecc = cuda.to_device(eccen)
    inc = cuda.to_device(inclin)


    threadsPerBlock = 256
    numBlocks = int((ecc.shape[0]+threadsPerBlock-1)/threadsPerBlock)


    CalculatePreValues[numBlocks, threadsPerBlock](retVal,ecc,inc)

    aVals = retVal.copy_to_host()

    preCalcTime = time.time() - start
    print ("Precalculation took % seconds" % preCalcTime)
    print (aVals.shape[0])

if __name__=='__main__':
    main()

这里有几点要说明。

首先,您看到的错误来源是来自内核执行的 运行时间错误。如果我使用 cuda-memcheck 运行 一个 hacky "fixed" 版本的代码,我看到这个:

$ cuda-memcheck python ./error.py 
========= CUDA-MEMCHECK
========= Invalid __global__ read of size 8
=========     at 0x00000178 in cudapy::__main__::CalculatePreValues1(Array<double, int=1, A, mutable, aligned>, Array<double, int=1, A, mutable, aligned>, Array<double, int=1, A, mutable, aligned>)
=========     by thread (255,0,0) in block (482,0,0)
=========     Address 0x7061317f8 is out of bounds

原因是您内核中的边界检查被破坏了:

    if i > ecc.shape[0]:
        return

应该是

    if i >= ecc.shape[0]:
        return

当问题更新为包含 MCVE 时,很明显还有另一个问题。内核签名为所有数组指定 double

@cuda.jit('void(double[:],double[:],double[:],double)')
                ^^^^^^    ^^^^^^    ^^^^^^

但创建的数组类型实际上是 float(即 np.float32):

eccen = np.ones(num_lines, dtype=np.float32)
inclin = np.ones(num_lines, dtype=np.float32)
                                  ^^^^^^^^^^

这是不匹配的。使用 double 索引对数组进行索引,当仅使用 float 值创建数组时,可能会创建越界索引。

解决方法是将创建的数组转换为dtype=np.float64,或者将签名中的数组转换为float:

@cuda.jit('void(float[:],float[:],float[:],double)')

消除越界索引。