Pycuda Blocks和Grids可以处理大数据

问题描述 投票:3回答:2

我需要帮助才能知道我的块和网格的大小。我正在构建一个python应用程序来执行基于scipy的度量计算:Euclidean distance,Manhattan,Pearson,Cosine,加入其他。

该项目是PycudaDistances(https://github.com/vinigracindo/pycudaDistances)。

它似乎与小数组一起工作得很好。当我进行更详尽的测试时,不幸的是它没有用。我下载了movielens set(http://www.grouplens.org/node/73)。

使用Movielens 100k,我声明了一个有形状的数组(943,1682)。也就是说,用户评价了943和1682部电影。不是分类器用户的电影我将值配置为0。

使用更大的阵列算法不再有效。我遇到以下错误:pycuda._driver.LogicError:cuFuncSetBlockShape失败:值无效。

研究这个错误,我找到了一个解释,告诉安德鲁支持512个线程加入并使用更大的块,有必要使用块和网格。

我想要一个帮助,使算法欧几里德距离数组适应从小型到巨型阵列的工作。

def euclidean_distances(X, Y=None, inverse=True):
    X, Y = check_pairwise_arrays(X,Y)
    rows = X.shape[0]
    cols = Y.shape[0]
    solution = numpy.zeros((rows, cols))
    solution = solution.astype(numpy.float32)

    kernel_code_template = """
    #include <math.h>

    __global__ void euclidean(float *x, float *y, float *solution) {

        int idx = threadIdx.x + blockDim.x * blockIdx.x;
        int idy = threadIdx.y + blockDim.y * blockIdx.y;

        float result = 0.0;

        for(int iter = 0; iter < %(NDIM)s; iter++) {

            float x_e = x[%(NDIM)s * idy + iter];
            float y_e = y[%(NDIM)s * idx + iter];
            result += pow((x_e - y_e), 2);
        }
        int pos = idx + %(NCOLS)s * idy;
        solution[pos] = sqrt(result);
    }
    """
    kernel_code = kernel_code_template % {
        'NCOLS': cols,
        'NDIM': X.shape[1]
    }

    mod = SourceModule(kernel_code)

    func = mod.get_function("euclidean")
    func(drv.In(X), drv.In(Y), drv.Out(solution), block=(cols, rows, 1))

    return numpy.divide(1.0, (1.0 + solution)) if inverse else solution

有关详细信息,请参阅:https://github.com/vinigracindo/pycudaDistances/blob/master/distances.py

我感谢任何帮助。非常感谢你。

python cuda gpu pycuda euclidean-distance
2个回答
14
投票

要获取内核的执行参数,您需要做两件事(按此顺序):

1.确定块大小

您的块大小主要取决于硬件限制和性能。我建议阅读this answer以获取更多详细信息,但非常简短的总结是您的GPU对每个块可以运行的线程总数有限制,并且它具有有限的寄存器文件,共享和本地内存大小。您选择的块尺寸必须在这些限制范围内,否则内核将无法运行。块大小也会影响内核的性能,您会发现块大小可以提供最佳性能。块大小应始终为warp大小的四倍,在迄今为止发布的所有CUDA兼容硬件上为32。

2.确定网格大小

对于您显示的内核类型,您需要的块数与输入数据量和每个块的尺寸直接相关。

例如,如果您的输入数组大小为943x1682,并且块大小为16x16,则需要59 x 106网格,这将在内核启动时产生944x1696个线程。在这种情况下,输入数据大小不是块大小的倍数,您需要修改内核以确保它不读取越界。一种方法可能是这样的:

__global__ void euclidean(float *x, float *y, float *solution) {
    int idx = threadIdx.x + blockDim.x * blockIdx.x;
    int idy = threadIdx.y + blockDim.y * blockIdx.y;

     if ( ( idx < %(NCOLS)s ) && ( idy < %(NDIM)s ) ) {

        .....
     }
}

启动内核的python代码可能类似于:

bdim = (16, 16, 1)
dx, mx = divmod(cols, bdim[0])
dy, my = divmod(rows, bdim[1])

gdim = ( (dx + (mx>0)) * bdim[0], (dy + (my>0)) * bdim[1]) )
func(drv.In(X), drv.In(Y), drv.Out(solution), block=bdim, grid=gdim)

This question and answer也可能有助于理解这个过程是如何运作的。

请注意,上述所有代码都是在浏览器中编写的,从未经过测试。需要您自担风险使用它。

另请注意,它基于对代码的简要读取,可能不正确,因为您还没有真正描述过如何在您的问题中调用代码。


1
投票

接受的答案原则上是正确的,但是talonmies列出的代码并不完全正确。行:gdim = ( (dx + (mx>0)) * bdim[0], (dy + (my>0)) * bdim[1]) )应该是:gdim = ( (dx + (mx>0)), (dy + (my>0)) )除了明显的额外括号之外,gdim会产生比你想要的线程太多的线程。 talonmies在他的文本中解释说线程是块大小* gridSize。但是,他列出的gdim会给你总线程,而不是正确的网格大小。

© www.soinside.com 2019 - 2024. All rights reserved.