Pycuda Blocks和Grids可以处理大数据
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pycuda Blocks和Grids可以处理大数据相关的知识,希望对你有一定的参考价值。
我需要帮助才能知道我的块和网格的大小。我正在构建一个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
我感谢任何帮助。非常感谢你。
要获取内核的执行参数,您需要做两件事(按此顺序):
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也可能有助于理解这个过程是如何运作的。
请注意,上述所有代码都是在浏览器中编写的,从未经过测试。需要您自担风险使用它。
另请注意,它基于对代码的简要读取,可能不正确,因为您还没有真正描述过如何在您的问题中调用代码。
接受的答案原则上是正确的,但是talonmies列出的代码并不完全正确。行:gdim = ( (dx + (mx>0)) * bdim[0], (dy + (my>0)) * bdim[1]) )
应该是:gdim = ( (dx + (mx>0)), (dy + (my>0)) )
除了明显的额外括号之外,gdim会产生比你想要的线程太多的线程。 talonmies在他的文本中解释说线程是块大小* gridSize。但是,他列出的gdim会给你总线程,而不是正确的网格大小。
以上是关于Pycuda Blocks和Grids可以处理大数据的主要内容,如果未能解决你的问题,请参考以下文章