在CUDA中读取具有线程的数组

在CUDA中读取具有线程的数组,cuda,gpu,Cuda,Gpu,我想知道这是否可能,以及在CUDA中从带有线程的数组中读取单元格的最佳方式是什么。为了简化我的意思,这是一个示例: 我有一个数组:{1,2,3,4,5,6,…},我希望每个线程读取数组中的n个单元,这主要取决于它的大小 我一直在尝试一些事情,但似乎不起作用,所以如果有人能指出一个(正确的)方法来做,那就太好了 多谢各位 通常,您希望连续线程读取连续数组索引。这样做会导致“合并”内存事务。考虑它的简单方法是,如果32个线程在物理上并行运行,并且它们都执行一个加载,那么如果所有32个加载都落在同一个

我想知道这是否可能,以及在CUDA中从带有线程的数组中读取单元格的最佳方式是什么。为了简化我的意思,这是一个示例:

我有一个数组:{1,2,3,4,5,6,…},我希望每个线程读取数组中的n个单元,这主要取决于它的大小

我一直在尝试一些事情,但似乎不起作用,所以如果有人能指出一个(正确的)方法来做,那就太好了


多谢各位

通常,您希望连续线程读取连续数组索引。这样做会导致“合并”内存事务。考虑它的简单方法是,如果32个线程在物理上并行运行,并且它们都执行一个加载,那么如果所有32个加载都落在同一个缓存线中,那么可以执行单个内存访问来填充缓存线,而不是32个单独的缓存线

因此,您要做的是让每个线程访问以线程数为步长的
n
单元格,如下所示(假设输入数据位于
float
数组
data

intidx=blockDim.x*blockIdx.x+threadIdx.x;
int stride=blockDim.x*gridDim.x;
对于(int i=idx;i
如果您的算法要求每个线程读取
n
连续的数据元素,那么您将产生非合并负载,这将非常昂贵。在这种情况下,我会考虑重新设计算法,所以不需要这种类型的访问。

您需要:

线程必须查看接下来的n个数字

因此,您可以使用:

#define N 2
#define NTHREAD 1024
#define ARRAYSIZE N*NTHREAD

// develop the kernel as:
__global__ void accessArray(int *array){
    int tid = blockDim.x * blockIdx.x + threadIdx.x;
    int startId = tid*N;

    // access thread's stride
    for(int i=0; i<N; i++){
        array[startId+i]=tid;
    }
}
// call the kernel by:
accessArray<<<NTHREAD/256, 256>>>(d_array);

您的阵列有多大?根据数组的大小,您甚至可以为每个项目启动一个线程,而不用担心它。这就是问题所在,我不能为每个线程使用一个项目,因为线程必须查看接下来的n个数字。这是显而易见的方法。我在回答中没有这样写的原因是因为这样做效率很低。您将获得大约硬件可用内存带宽的1/32。@Anoracx简单地说,用于扭曲执行。相邻线程(每32个线程)使用
warp
锁步执行。如果线程正在访问同一内存块,则扭曲中线程的内存访问可以合并为一个内存事务(称为
内存访问合并
)。当
N
较大时,此代码不会利用内存访问合并和线程对不同块的访问导致许多内存访问(降低性能)。因此,基本上块大小应始终可被32整除?如果线程块大小是32的倍数,则不会浪费扭曲的任何通道。但是,你可以有任意大小的线程块,并且硬件会相应地用虚拟线程填充扭曲。其速度慢的原因是每个线程访问一个元素时都会远离它的邻居2000个元素。因此,每个线程都必须进行单独的内存访问,而不是一个warp的所有32个线程共享一次内存访问(一次加载整个缓存线)。感谢您的回复,但我已经看了几分钟了,但是,我真的不知道I+=stride将如何工作。如果我们需要在一个固定大小的数组中添加两个线程的N个数字(或者看N个数字),它将添加0+2+4+N而不是0+1+2+N,或者我遗漏了什么?但不幸的是,我需要读取N个连续的数据元素,我相信很多算法都在这样做,比如快速字符串搜索。因为我使用的算法无法重新设计。我实际上是想看看这个问题是否得到了解决。线程之间跨越2000个元素总是会导致可能的最大内存事务、最大缓存未命中等。我的建议是用一个扭曲处理每组2000个元素,而不是一个线程。因此,有了20000k个元素,最好将数组分割成若干块,将n个元素的卡盘发送到我的cuda卡(其中n是32的倍数),然后在warp中处理它们?
#define N 2
#define NTHREAD 1024
#define ARRAYSIZE N*NTHREAD

// develop the kernel as:
__global__ void accessArray(int *array){
    int tid = blockDim.x * blockIdx.x + threadIdx.x;
    int startId = tid*N;

    // access thread's stride
    for(int i=0; i<N; i++){
        array[startId+i]=tid;
    }
}
// call the kernel by:
accessArray<<<NTHREAD/256, 256>>>(d_array);
#include <cuda.h>
#include <stdio.h>


#define N 2
#define NTHREAD 1024
#define ARRAYSIZE N*NTHREAD

// develop the kernel as:
__global__ void accessArray(int *array){
    int tid = blockDim.x * blockIdx.x + threadIdx.x;
    int startId = tid*N;

    // access thread's stride
    for(int i=0; i<N; i++){
        array[startId+i]=tid;
    }
}

int  main()
{
    int h_array[ARRAYSIZE];
    int *d_array;
    size_t memsize= ARRAYSIZE * sizeof(float);

    for (int i=0; i< ARRAYSIZE; i++) {
        h_array[i] = 0;
    }

    cudaMalloc(&d_array, memsize);
    cudaMemcpy(d_array, h_array, memsize,  cudaMemcpyHostToDevice);

    accessArray<<<NTHREAD/256, 256>>>(d_array);
    cudaMemcpy(h_array, d_array, memsize,  cudaMemcpyDeviceToHost);

    for (int i=0; i<ARRAYSIZE; i++)
        printf("A[%d] => %d\n",i,h_array[i]);

    cudaFree(d_array);
}