理解并优化pyCUDA中的线程,块和网格

时间:2017-01-19 13:38:09

标签: python parallel-processing cuda gpu pycuda

我对GPU编程和pyCUDA都很陌生,并且在我的知识方面存在相当大的差距。我花了很多时间搜索SO,查看示例代码并阅读CUDA / pyCUDA的支持文档,但是在解释中找不到多样性,并且无法理解我的一些事情。< / p>

我无法正确定义块和网格尺寸。我目前运行的代码如下,旨在通过float a对数组b进行元素乘法运算:

from __future__ import division
import pycuda.gpuarray as gpuarray
import pycuda.driver as cuda
import pycuda.autoinit
from pycuda.compiler import SourceModule
import numpy as np

rows = 256
cols = 10
a = np.ones((rows, cols), dtype=np.float32)
a_gpu = cuda.mem_alloc(a.nbytes)
cuda.memcpy_htod(a_gpu, a)

b = np.float32(2)

mod = SourceModule("""
  __global__ void MatMult(float *a, float b)
  {
    const int i = threadIdx.x + blockDim.x * blockIdx.x;
    const int j = threadIdx.y + blockDim.y * blockIdx.y;
    int Idx = i + j*gridDim.x;
    a[Idx] *= b;
  }
  """)

func = mod.get_function("MatMult")

xBlock = np.int32(np.floor(1024/rows))
yBlock = np.int32(cols)

bdim = (xBlock, yBlock, 1)
dx, mx = divmod(rows, bdim[0])
dy, my = divmod(cols, bdim[1])
gdim = ( (dx + (mx>0)) * bdim[0], (dy + (my>0)) * bdim[1])
print "bdim=",bdim, ", gdim=", gdim

func(a_gpu, b, block=bdim, grid=gdim)
a_doubled = np.empty_like(a)
cuda.memcpy_dtoh(a_doubled, a_gpu)
print a_doubled - 2*a

代码应打印块尺寸bdim和网格尺寸gdim,以及零数组。

这适用于小数组大小,例如,如果rows=256cols=10(如上例所示)输出如下:

bdim= (4, 10, 1) , gdim= (256, 10)
[[ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]
 ..., 
 [ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]]

但是,如果我增加rows=512,我会得到以下输出:

bdim= (2, 10, 1) , gdim= (512, 10)
[[ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]
 ..., 
 [ 2.  2.  2. ...,  2.  2.  2.]
 [ 2.  2.  2. ...,  2.  2.  2.]
 [ 2.  2.  2. ...,  2.  2.  2.]]

指示对于阵列的某些元素,乘法发生两次。

但是,如果我将块尺寸强制为bdim = (1,1,1),则问题不再出现,并且我得到以下(正确的)输出以获得更大的数组大小:

bdim= (1, 1, 1) , gdim= (512, 10)
[[ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]
 ..., 
 [ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]
 [ 0.  0.  0. ...,  0.  0.  0.]]

我不明白这一点。这里发生了什么意味着这种定义块和网格尺寸的方法不再适用于数组大小增加?另外,如果块具有尺寸(1,1,1),这是否意味着计算是连续执行的?

提前感谢任何指示和帮助!

1 个答案:

答案 0 :(得分:1)

您在2D网格的2D网格上操作。在您的内核中,您似乎假设gridDim.x将返回网格的x维度中的线程数。

__global__ void MatMult(float *a, float b)
{
    const int i = threadIdx.x + blockDim.x * blockIdx.x;
    const int j = threadIdx.y + blockDim.y * blockIdx.y;
    int Idx = i + j*gridDim.x;
    a[Idx] *= b;
}

gridDim.x返回块的数量r x网格方向,而不是线程数。为了获得给定方向上的线程数,您应该将块中的线程数乘以网格中相同方向的块数:

int Idx = i + j * blockDim.x * gridDim.x