0

我是 Cuda 开发的新手,我决定开始编写小示例脚本以了解它是如何工作的。我决定分享我制作的核函数并计算两个相等大小矩阵的相应行之间的平方欧几里德距离。

__global__ void cudaEuclid( float* A, float* B, float* C, int rows, int cols )
{
    int i, squareEuclDist = 0;
    int r = blockDim.x * blockIdx.x + threadIdx.x; // rows
    //int c = blockDim.y * blockIdx.y + threadIdx.y; // cols

    if( r < rows  ){ // take each row with var r (thread)
        for ( i = 0; i < cols; i++ )//compute squared Euclid dist of each row 
            squareEuclDist  += ( A[r + rows*i] - B[r + rows*i] ) * ( A[r + rows*i] - B[r + rows*i] );
        C[r] = squareEuclDist;
        squareEuclDist = 0;
    }   
}

内核初始化由

int threadsPerBlock = 256;
int blocksPerGrid = ceil( (double) numElements  / threadsPerBlock); 
// numElements  = 1500x200 (matrix size) ==> 1172 blocks/grid

并被称为

cudaEuclid<<<blocksPerGrid, threadsPerBlock>>>( d_A, d_B, d_C, rows, cols );

d_A 和 d_B 是插入的矩阵,在这个大小为 1500 x 200 的示例中。

问题 1:我已经阅读了选择每个块的线程和每个网格数的块的基本理论,但仍然缺少一些东西。我试图在这个简单的内核中理解什么是最佳内核参数初始化,并且我正在寻求一些帮助以开始以 CUDA 方式思考。

问题2:我想问的另一件事是,是否有关于如何提高代码效率的建议?我们可以用它int c = blockDim.y * blockIdx.y + threadIdx.y来让事情更并行吗?共享内存在这里适用吗?

下面附上我的 GPU 信息。

Device 0: "GeForce 9600 GT"
  CUDA Driver Version / Runtime Version          5.5 / 5.0
  CUDA Capability Major/Minor version number:    1.1
  Total amount of global memory:                 512 MBytes (536870912 bytes)
  ( 8) Multiprocessors x (  8) CUDA Cores/MP:    64 CUDA Cores
  GPU Clock rate:                                1680 MHz (1.68 GHz)
  Memory Clock rate:                             700 Mhz
  Memory Bus Width:                              256-bit
  Max Texture Dimension Size (x,y,z)             1D=(8192), 2D=(65536,32768), 3D=(2048,2048,2048)
  Max Layered Texture Size (dim) x layers        1D=(8192) x 512, 2D=(8192,8192) x 512
  Total amount of constant memory:               65536 bytes
  Total amount of shared memory per block:       16384 bytes
  Total number of registers available per block: 8192
  Warp size:                                     32
  Maximum number of threads per multiprocessor:  768
  Maximum number of threads per block:           512
  Maximum sizes of each dimension of a block:    512 x 512 x 64
  Maximum sizes of each dimension of a grid:     65535 x 65535 x 1
  Maximum memory pitch:                          2147483647 bytes
  Texture alignment:                             256 bytes
  Concurrent copy and kernel execution:          Yes with 1 copy engine(s)
  Run time limit on kernels:                     Yes
  Integrated GPU sharing Host Memory:            No
  Support host page-locked memory mapping:       Yes
  Alignment requirement for Surfaces:            Yes
  Device has ECC support:                        Disabled
  Concurrent kernel execution:                   No
  Device supports Unified Addressing (UVA):      No
  Device PCI Bus ID / PCI location ID:           1 / 0

问题 3:我们可以用 GPU 拥有的共享内存和其他类型的内存来表示全局内存的数量吗?线程数与此有关吗?

问题 4:如果每个块的最大线程数是 512,那么块的每个维度的最大大小怎么可能是 512x512x62(= 16252628 个线程)?与我的网格每个维度的最大尺寸有什么关系?

问题5:使用内存时钟频率我们可以说每秒处理了多少个线程?

更新

for 循环替换为列线程

__global__ void cudaEuclid( float* A, float* B, float* C, int rows, int cols ){

    int r = blockDim.x * blockIdx.x + threadIdx.x; // rows
    int c = blockDim.y * blockIdx.y + threadIdx.y; // cols

    float x=0;
    if(c < cols && r < rows){
       x = ( A[c + r*cols] - B[c + r*cols] ) * ( A[c + r*cols] - B[c + r*cols] );
     }
     C[r] = x;      
} 

调用:

int threadsPerBlock = 256;
int blocksPerGrid = ceil( (double) numElements  / threadsPerBlock);
cudaEuclid<<<blocksPerGrid, threadsPerBlock>>>( d_A, d_B, d_C, rows, cols );
4

2 回答 2

2

A1。优化每个块的线程基本上是启发式的。你可以试试

for(int threadsPerBlock=32; threadsPerBlock<=512;threadsPerBlock+=32){...}

A2。目前,您每行使用一个线程并将元素squareEuclDist线性求和。您可以考虑每行使用一个线程块。在块内,每个线程计算一个元素的平方差,您可以使用并行归约将它们加在一起。请参考以下链接进行并行缩减。

http://docs.nvidia.com/cuda/samples/6_Advanced/reduction/doc/reduction.pdf

A3。您显示的列表是全局/共享内存的总量。多个线程将共享这些硬件资源。您可以在您的 cuda 安装目录中找到此工具,以帮助您计算可在特定内核中使用的硬件资源的每个线程的确切数量。

$CUDA_HOME/tools/CUDA_Occupancy_Calculator.xls

A4。maximum sizes of each dimension并不意味着所有维度都可以同时达到最大值。但是,每个网格的块没有限制,因此网格中的 65536x65536x1 个块是可能的。

A5。mem时钟与线程号无关。您可以阅读 cuda 文档中的编程模型部分以获取更多信息。

http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#scalable-programming-model

于 2013-10-02T03:31:05.787 回答
2

好的,所以与内核相关的东西很少,一个是多处理器的数量(与块相关联)和核心数量(与核心相关联),块被安排在多处理器上运行(对你来说是 8 个),线程是计划在单个多处理器上的多个内核上运行。理想情况下,您希望拥有足够数量的块和线程,以便所有多处理器和每个多处理器中的所有内核都被占用。与多处理器和内核相比,建议使用更多数量的块和线程,因为可以合并线程/块。

多维度使编程更容易(例如:2D/3D图像,您可以将图像分成子部分并将其分配给不同的块,然后在多个线程上处理这些子图像),使用多维度更直观( x, y, z) 用于访问块和线程。在某些情况下,如果一维中的最大块数有限制,它可以帮助您拥有更多维度(例如,如果您有一个大图像,如果您只使用一维,则可能会达到最大块数的限制)。

我不确定我是否理解你在第三个问题中的意思,我可以谈谈共享内存。共享内存存在于单个多处理器上,由处理器上的内核共享。对你来说,共享内存的大小是 16KB,大多数现代 GPU 在处理器上都有 64KB 的共享内存,你可以选择你的应用程序需要多少,64KB 中的 16KB 通常保留用于缓存,你可以使用为您剩余 48KB 或增加缓存大小并降低共享内存大小。共享内存比全局内存快得多,因此如果您有一些会经常访问的数据,最好将其传输到共享内存。线程数与共享内存完全无关。此外,全局内存和共享内存是分开的。

如果您可以看到,每个块维度小于 512,则每个块的线程数不能超过 512 个(在更好的架构上较新的 CUDA 版本中,限制已更改为 1024)。在 Fermi 之前,每个处理器都有 32 或 48 个内核,因此拥有超过 512 个线程没有多大意义。新的 Kepler 架构每个多处理器有 192 个内核。

线程在 warp 中执行,通常是 16 个线程组合在一起并在多处理器的内核上同时执行。如果您假设共享内存中始终存在未命中,则取决于每个多处理器拥有的内核数量和内存时钟速率,您可以计算每秒处理线程的数量(您需要考虑每个线程处理的指令数,也可能需要一些时间来处理寄存器等操作)。

我希望这能在一定程度上回答你的问题。

于 2013-10-02T03:33:46.933 回答