Arrays 如何获得;sum";cuda中并行阵列的性能?

Arrays 如何获得;sum";cuda中并行阵列的性能?,arrays,cuda,sum,kernel,cublas,Arrays,Cuda,Sum,Kernel,Cublas,我的问题是如何获得一些相同长度数组的“和”。例如,我总共有一个M*N(100*2000)长的浮点数组。我想得到每N(2000)个浮点数的M(100)和值。我找到了两种做这项工作的方法。一种是在M的for循环中使用Cublas函数,如cublasSasum。另一个是自编核函数,在循环中添加数字。我的问题是这两种方法的速度以及如何在它们之间进行选择 对于Cublas方法,无论N(4000~2E6)有多大,耗时主要取决于循环数M 对于自行编写的犬舍功能,速度随N变化很大。具体而言,如果N很小,低于50

我的问题是如何获得一些相同长度数组的“和”。例如,我总共有一个M*N(100*2000)长的浮点数组。我想得到每N(2000)个浮点数的M(100)和值。我找到了两种做这项工作的方法。一种是在M的for循环中使用Cublas函数,如
cublasSasum
。另一个是自编核函数,在循环中添加数字。我的问题是这两种方法的速度以及如何在它们之间进行选择

对于Cublas方法,无论N(4000~2E6)有多大,耗时主要取决于循环数M

对于自行编写的犬舍功能,速度随N变化很大。具体而言,如果N很小,低于5000,则运行速度比Cublas方式快得多。然后,时间消耗随着N的增加而增加

N=4000 | 10000 | 40000 | 80000 | 1E6 | 2E6

t=254ms | 422ms | 1365ms | 4361ms | 5399ms | 10635ms

如果N足够大,它的运行速度比库布拉斯方式慢得多。我的问题是如何用M或N进行预测,以决定我应该使用哪种方法?我的代码可能会在不同的GPU设备上使用。我必须比较参数扫描中的速度,然后“猜测”以在每个GPU设备中做出选择,或者我可以从GPU设备信息推断

此外,对于内核函数方式,我在决定
blockSize
gridSize
时也有问题。我必须指出,我更关心的是速度,而不是效率。因为内存有限。例如,如果我有8G内存。我的数据格式是4字节的浮点格式。N是1E5。那么M最多是2E4,它小于
MaxGridSize
。所以我有两种方法,如下所示。我发现网格越大越好,我不知道原因。是关于每个线程的寄存器号的使用吗?但我认为在这个内核函数中,每个线程不需要很多寄存器

如有任何建议或信息,将不胜感激。多谢各位

库布拉斯路

for (int j = 0;j<M;j++)
    cublasStatus = cublasSasum(cublasHandle,N,d_in+N*j,1,d_out+j);

for(int j=0;j使用CuBLAS通常是一个非常好的主意,如果有您想要的专用函数,尤其是对于大型数据集,则应该首选它。也就是说,对于在如此小的数据集上工作的GPU内核来说,计时非常糟糕。让我们了解原因

网格越大速度越快。我不知道原因。
getSum(M,N,d_-in,d_-out);

getSum(M,N,d_-in,d_-out);

调用CUDA内核的语法是
kernel
。因此,第一行提交一个包含1个线程的M个块的内核。不要这样做:这是非常低效的。事实上

NVIDIA GPU体系结构是围绕多线程流式多处理器(SMs)的可扩展阵列构建的。当主机CPU上的CUDA程序调用内核网格时,网格块被枚举并分配给具有可用执行能力的多处理器。线程块的线程在一个多处理器上并发执行,多个线程块可以在一个多处理器上并发执行。作为线程块ks终止,在空出的多处理器上启动新块。[…]
多处理器创建、管理、调度和执行一组32个并行线程(称为warps)中的线程。[…]
warp一次执行一条公共指令,因此,当warp的所有32个线程在其执行路径上一致时,将实现最高效率。如果warp的线程通过依赖数据的条件分支发散,则warp将执行所采用的每个分支路径,从而禁用不在该路径上的线程。分支发散仅在hin warp;不同的warp独立执行,无论它们执行的是公共或不相交的代码路径

因此,第一次调用创建由1个线程组成的
M
块,浪费了31个CUDA内核,每个warp中有32个可用。这意味着您可能只能读取GPU峰值性能的3%

第二次调用创建了一个
M
线程块。因为
M
不是32的倍数,所以很少有CUDA内核被浪费。此外,它只使用了1个SM,比GPU上可用的多个SM多,因为你只有一个块。现代GPU有几十个SMs(我的GTX-1660S有22个SM)。这意味着您将只使用GPU功能的一小部分(很少)。更不用说内存访问模式不是连续的,这将进一步降低计算速度

如果你想更有效地使用GPU,你需要提供更多的并行性,并浪费更少的资源。你可以从编写一个在2D网格上工作的内核开始,使用原子来执行缩减操作。这不是完美的,但比你最初的代码要好得多。你还应该连续读取内存(共享同一扭曲的线程应读取/写入连续的内存块)

在编写CUDA代码之前,请仔细阅读或阅读教程。它非常准确地描述了所有这些


更新:

根据新信息,您正在试验的
块大小问题可能是由于内核中的跨步内存访问(更具体地说是
N*i
).跨步内存访问模式很慢,通常在跨步变大时会变慢。在内核中,每个线程将访问内存中的不同块。GPU(实际上是大多数硬件计算单元)如前所述,针对访问连续数据块进行了优化。如果要解决此问题并获得更快的结果,则需要并行处理其他维度(因此不是
M
,而是
N

此外,BLAS调用效率低下,因为CPU上循环的每次迭代都会调用GPU上的内核。在CPU中调用内核
__global__ void getSum(int M, int N, float* in, float * out)
{
    int i = threadIdx.x + blockIdx.x * blockDim.x;
    if(i<M){
        float tmp = 0;
        for(int ii = 0; ii<N; ii++){
            tmp += *(in+N*i+ii);
        }
        out[i] = tmp;
    }
}
getSum<<<M,1>>>(M, N, d_in, d_out); //faster
getSum<<<1,M>>>(M, N, d_in, d_out); 

cudaEventRecord(start, 0);
//blockSize = 1:1024;
int gridSize = (M + blockSize - 1) / blockSize;
getSum<<<gridSize1,blockSize1>>>...
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&time, start, stop);