CUDA中的矩阵乘法耗尽内存

时间:2017-05-12 20:15:18

标签: python cuda gpu numba

我尝试使用脚本计算矩阵乘法:

import numpy as np
import math
from timeit import default_timer as timer
from numba import cuda
from numba import *


def mult(a,b):
    return a*b
mult_gpu=cuda.jit(restype=float32,argtypes=[float32,float32],device=True)(mult)

@cuda.jit(argtypes=[float32[:,:],float32[:,:],float32[:,:,:]])
def mult_kernel(a,b,c):
    Ni=c.shape[0]
    Nj=c.shape[1]
    Nk=c.shape[2]

    startX,startY,startZ=cuda.grid(3)
    gridX=cuda.gridDim.x*cuda.blockDim.x
    gridY=cuda.gridDim.y*cuda.blockDim.y
    gridZ=cuda.gridDim.z*cuda.blockDim.z

    for i in range(startX,Ni,gridX):
        for j in range(startY,Nj,gridY):
            for k in range(startZ,Nk,gridZ):
                c[i,j,k]=mult_gpu(a[i,k],b[j,k])

def main():
    A=np.ones((20,50000),dtype=np.float32)
    B=np.ones((3072,50000),dtype=np.float32)
    C=np.ones((20,3072,50000),dtype=np.float32)
    (Ni,Nj,Nk)=C.shape

    my_gpu=cuda.get_current_device()
    thread_ct=my_gpu.WARP_SIZE
    block_ct_x=int(math.ceil(float(Ni)/thread_ct))
    block_ct_y=int(math.ceil(float(Nj)/thread_ct))
    block_ct_z=int(math.ceil(float(Nk)/thread_ct))

    blockdim=thread_ct,thread_ct,thread_ct
    griddim=block_ct_x,block_ct_y,block_ct_z
    print "Threads per block:",blockdim
    print "Blocks per grid:",griddim

    start=timer()
    Cg=cuda.to_device(C)
    mult_kernel[griddim,blockdim](A,B,Cg)
    Cg.to_host()
    dt=timer()-start
    print "Computation done in %f s"%(dt)

    print 'C[:3,1,1] = ',C[:3,1,1]
    print 'C[-3:,1,1] = ',C[-3:,1,1]


if __name__=='__main__':
    main()

执行此操作会产生错误:

numba.cuda.cudadrv.driver.CudaAPIError: [2] Call to cuMemAlloc results in CUDA_ERROR_OUT_OF_MEMORY

我怎样才能解决这个内存问题?

然而,使用较小的矩阵

    A=np.ones((20,500),dtype=np.float32)
    B=np.ones((372,500),dtype=np.float32)
    C=np.ones((20,372,500),dtype=np.float32)

仍有错误:

numba.cuda.cudadrv.driver.CudaAPIError: [1] Call to cuLaunchKernel results in CUDA_ERROR_INVALID_VALUE

我受到Mandelbrot Example的启发,以实现上面的计算。

EDIT1

为了解决任何混淆,这实际上是3D矩阵乘法的3D矩阵:

    A=np.ones((20,1,50000),dtype=np.float32)
    B=np.ones((1,3072,50000),dtype=np.float32)
    C=np.ones((20,3072,50000),dtype=np.float32)

我在AB中跳过了一个维度,因为它不需要进行计算。

EDIT2

我的GPU是:

    In [1]: from numba import cuda

    In [2]: gpu=cuda.get_current_device()

    In [3]: gpu.name
    Out[3]: 'GeForce GT 750M'

EDIT3

根据GPU的内存(2GB),我将每个维度的大小减少了2:

    dimx=10
    dimy=1536
    dimz=25000
    A=np.ones((dimx,dimz),dtype=np.float32)
    B=np.ones((dimy,dimz),dtype=np.float32)
    C=np.ones((dimx,dimy,dimz),dtype=np.float32)

但我仍然收到CUDA_ERROR_OUT_OF_MEMORY错误。怎么能解释一下呢?

对于3个矩阵,计算得出大约1.7GB的大小: (10*1536*25000*4.+10*25000*4+1536*25000*4.)/(10**9)=1.6906

1 个答案:

答案 0 :(得分:4)

关于第一个问题,你的内存不足。一个主要的贡献者是,这不是人们通常做矩阵矩阵乘法的方式。通常,当您将行和列元素相乘时,您将保持运行总和,然后将该总和存储在product(result)矩阵中的适当位置。这将允许您为c矩阵设置更小的尺寸,即。它只需要2维,而不是3.你可能只想研究矩阵 - 矩阵乘法的线性代数定义。当您将2D矩阵乘以2D矩阵时,结果是2D矩阵,而不是3D矩阵。

简而言之,这样的事情:

for i in range(startX,Ni,gridX):
    for j in range(startY,Nj,gridY):
        c[i,j] = 0
        for k in range(startZ,Nk,gridZ):
            c[i,j]= c[i,j] + mult_gpu(a[i,k],b[j,k])

并相应地调整c形状。

如果您确实需要3D形式的单个产品,那么我可以说很多,除了您需要根据您正在使用的GPU扩展问题以适应GPU内存大小。

关于第二个问题,你有一个问题:

thread_ct=my_gpu.WARP_SIZE
...

blockdim=thread_ct,thread_ct,thread_ct

WARP_SIZE是32(大概),因此您要求的是尺寸为32 * 32 * 32 = 32K线程的3D块。但是CUDA线程块最多限制为1024个线程,这个限制是各个维度的乘积。

如果您将thread_ct更改为8,例如:

thread_ct=8

你应该能够解决这个问题。