我正在实验如何在numba中使用cuda。但是,我遇到了与期望不同的事情。这是我的代码
from numba import cuda
@cuda.jit
def matmul(A, B, C):
"""Perform square matrix multiplication of C = A * B
"""
d=cuda.local.array((3,3),dtype=numba.float64)
i, j = cuda.grid(2)
if i < C.shape[0] and j < C.shape[1]:
tmp = 0.
for k in range(A.shape[1]):
tmp += A[i, k] * B[k, j]
C[i, j] = tmp
这是我为使用numba.cuda测试而自定义的矩阵函数。在运行测试之前,我还通过以下代码加载了数组:
import numpy as np
a=np.random.rand(2000,2000)
b=np.random.rand(2000,2000)
c=np.empty((2000,2000))
a1=cuda.to_device(a)
b1=cuda.to_device(b)
c1=cuda.to_device(c)
然后我将以下代码用于实验:
from time import time
count =0
start=time()
for i in range(2000):
matmul[(256,256),(16,16)](a1,b1,c1)
count +=1
print(count)
for循环在第一个1028年运行非常快。但是在1028年之后它运行非常缓慢,究竟是什么原因造成的,以及如何解决它。顺便说一下,我在win10上运行。
这是我从numba.cuda调用的cuda信息
from numba import cuda
gpu = cuda.get_current_device()
print("name = %s" % gpu.name)
print("maxThreadsPerBlock = %s" % str(gpu.MAX_THREADS_PER_BLOCK))
print("maxBlockDimX = %s" % str(gpu.MAX_BLOCK_DIM_X))
print("maxBlockDimY = %s" % str(gpu.MAX_BLOCK_DIM_Y))
print("maxBlockDimZ = %s" % str(gpu.MAX_BLOCK_DIM_Z))
print("maxGridDimX = %s" % str(gpu.MAX_GRID_DIM_X))
print("maxGridDimY = %s" % str(gpu.MAX_GRID_DIM_Y))
print("maxGridDimZ = %s" % str(gpu.MAX_GRID_DIM_Z))
print("maxSharedMemoryPerBlock = %s" %
str(gpu.MAX_SHARED_MEMORY_PER_BLOCK))
print("asyncEngineCount = %s" % str(gpu.ASYNC_ENGINE_COUNT))
print("canMapHostMemory = %s" % str(gpu.CAN_MAP_HOST_MEMORY))
print("multiProcessorCount = %s" % str(gpu.MULTIPROCESSOR_COUNT))
print("warpSize = %s" % str(gpu.WARP_SIZE))
print("unifiedAddressing = %s" % str(gpu.UNIFIED_ADDRESSING))
print("pciBusID = %s" % str(gpu.PCI_BUS_ID))
print("pciDeviceID = %s" % str(gpu.PCI_DEVICE_ID))
,输出为:
名称= b'GeForce GTX 1050 Ti'
maxThreadsPerBlock = 1024
maxBlockDimX = 1024
maxBlockDimY = 1024
maxBlockDimZ = 64
maxGridDimX = 2147483647
maxGridDimY = 65535
maxGridDimZ = 65535
maxSharedMemoryPerBlock = 49152
asyncEngineCount = 2
canMapHostMemory = 1
multiProcessorCount = 6
warpSize = 32
unifiedAddressing = 1
pciBusID = 3
pciDeviceID = 0
答案 0 :(得分:4)
这是由与GPU内核启动相关联的异步启动队列引起的。
当您告诉numba提交GPU内核时:
matmul[(256,256),(16,16)](a1,b1,c1)
此请求进入队列,发出该内核调用的CPU线程(即python)可以继续,即使GPU内核尚未完成甚至尚未启动。
CUDA运行时将这些请求排队,并在GPU准备进行更多工作时发出这些请求。
在for-loop快速增加的过程中,最初看到的是队列中充满了工作请求。那不能代表GPU执行工作所需的实际时间。
最终队列填满,并且CUDA运行时在内核启动时停止CPU线程(即python),直到打开了一个队列插槽。在这一点上,允许for循环再进行一次迭代。正是在这一点上(大约1028次迭代),您开始看到“减速”。之后,for循环以大约的速度执行GPU内核并从处理队列中删除。
这里没有要修复的东西;这是预期的行为。
如果您希望for循环仅以实际执行GPU内核的速度进行,则应在for循环中插入一个同步函数。
例如,numba提供了numba.cuda.synchronize(),因此,如果您按以下方式修改for循环:
for i in range(2000):
matmul[(256,256),(16,16)](a1,b1,c1)
cuda.synchronize()
count +=1
print(count)
您将看到for循环以GPU工作完成的实际速率而不是“队列填充”速率进行。