Pycuda:多次调用Kernel的最佳方式

时间:2015-09-17 01:45:26

标签: python cuda pycuda

我使用pycuda制作相对论光线追踪器。基本上,对于每个像素"在大型2D阵列中,我们必须使用Runge Kutta解决6个ODE系统。由于每个集成都独立于其他集成,因此应该非常容易。其他人使用C / C ++ CUDA实现了它,效果很好(参见this project)。

问题出现在我不知道如何做到这一点的最佳方式。我正在编写一个执行Runge Kutta步骤的内核,然后将结果返回给CPU。为了整个光线集成,这个内核被多次调用。问题是出于某种原因非常慢。当然,我知道内存传输确实是CUDA中的一个瓶颈,但由于这种情况非常缓慢,我开始认为我做错了什么。

如果您能为我推荐这种情况的最佳编程实践,那将是很棒的。 (使用pycuda)。我徘徊的一些事情:

  1. 我是否需要在到达内核呼叫时创建新的上下文?
  2. 有一种方法无需将内存从GPU传输到CPU,即启动内核,暂停它以获取某些信息,重新设置并重复
  3. 每次RK4迭代大约需要半秒钟,这是疯狂的(也与执行某些类似操作的链接中的CUDA代码相比)。而且我认为这是因为我使用pycuda的方式有问题,所以如果你能以最好的方式解释这种操作的最佳方式,那就太棒了!。
  4. 澄清一下:我必须暂停/重启内核的原因是看门狗。监管机构杀死了超过10秒的内核。

    提前谢谢!

2 个答案:

答案 0 :(得分:2)

您的主要问题似乎过于笼统,如果没有看到代码,很难提出一些具体的建议。我会尝试回答你的问题(不是一个实际的答案,但是评论的时间有点长)

  

我是否需要在到达内核呼叫时创建新的上下文?

没有。

  

有一种方法可以不必将内存从GPU传输到CPU,即启动内核,暂停它以获取一些信息,重新设置并重复。

取决于“获取一些信息”的含义。如果它意味着在CPU上使用它,那么,当然,你必须转移它。如果要在另一个内核调用中使用,则不需要传输它。

  

每次RK4迭代大约需要半秒钟,这是疯狂的(也与执行某些类似操作的链接中的CUDA代码相比)。

这实际上取决于您使用的等式,线程数和视频卡。我可以想象一个RK步骤需要很长时间的情况。

  

我认为这是因为我使用pycuda的方式出了问题,所以如果你能以最好的方式解释这种操作的最佳方法,那就太棒了!。

没有代码就无法肯定地说。尝试创建一些最小的演示示例,或者至少发布指向 runnable 的链接(即使它很长),这段代码可以说明您的问题。至于PyCUDA,它是一个非常薄的CUDA包装器,适用于后者的所有编程实践也适用于前者。

答案 1 :(得分:-1)

我可能会帮助您处理内存,即在迭代期间不必从CPU复制到GPU。我正在使用euler时间步长随时间推移系统,我将所有数据保存在GPU上的方式如下所示。 但是,问题在于,一旦第一个内核启动,cpu就会继续执行它后面的行。即边界内核在时间演化步骤之前启动。

我需要的是一种同步事物的方法。我尝试过使用strm.synchronize()(参见我的代码),但它并不总是有效。如果您对此有任何想法,我将非常感谢您的意见!谢谢!

def curveShorten(dist,timestep,maxit):
"""
iterates the function image on a 2d grid through an euler anisotropic
diffusion operator with timestep=timestep maxit number of times
"""
image = 1*dist
forme = image.shape
if(np.size(forme)>2):
    sys.exit('Only works on gray images')

aSize = forme[0]*forme[1]
xdim  = np.int32(forme[0])
ydim  = np.int32(forme[1])  


image[0,:]      = image[1,:]
image[xdim-1,:] = image[xdim-2,:]
image[:,ydim-1] = image[:,ydim-2]
image[:,0]      = image[:,1]

#np arrays  i need to store things on the CPU, image is the initial 
#condition and final is the final state
image = image.reshape(aSize,order= 'C').astype(np.float32)
final = np.zeros(aSize).astype(np.float32)

#allocating memory to GPUs
image_gpu = drv.mem_alloc(image.nbytes)
final_gpu = drv.mem_alloc(final.nbytes)

#sending data to each memory location
drv.memcpy_htod(image_gpu,image) #host to device copying
drv.memcpy_htod(final_gpu,final)

#block size: B := dim1*dim2*dim3=1024
#gird size : dim1*dimr2*dim3 = ceiling(aSize/B)
blockX     = int(1024)
multiplier = aSize/float(1024)   
if(aSize/float(1024) > int(aSize/float(1024)) ):
    gridX = int(multiplier + 1)
else:
    gridX = int(multiplier)
strm1 = drv.Stream(1)
ev1   = drv.Event()
strm2 = drv.Stream()
for k in range(0,maxit):

    Kern_diffIteration(image_gpu,final_gpu,ydim, xdim, np.float32(timestep), block=(blockX,1,1), grid=(gridX,1,1),stream=strm1)
    strm1.synchronize()

    if(strm1.is_done()==1):
     Kern_boundaryX0(final_gpu,ydim,xdim,block=(blockX,1,1), grid=(gridX,1,1))
     Kern_boundaryX1(final_gpu,ydim,xdim,block=(blockX,1,1), grid=(gridX,1,1))#,stream=strm1)
     Kern_boundaryY0(final_gpu,ydim,xdim,block=(blockX,1,1), grid=(gridX,1,1))#,stream=strm2)
     Kern_boundaryY1(final_gpu,ydim,xdim,block=(blockX,1,1), grid=(gridX,1,1))#,stream=strm1)

    if(strm1.is_done()==1): 
      drv.memcpy_dtod(image_gpu, final_gpu, final.nbytes)
    #Kern_copy(image_gpu,final_gpu,ydim,xdim,block=(blockX,1,1), grid=(gridX,1,1),stream=strm1)



drv.memcpy_dtoh(final,final_gpu) #device to host copying
#final_gpu.free()
#image_gpu.free()


return final.reshape(forme,order='C')