一些基本的CUDA查询

时间:2013-10-01 21:08:41

标签: parallel-processing cuda gpu

我是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 );

2 个答案:

答案 0 :(得分: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 doc中的编程模型部分以获取更多信息。

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

答案 1 :(得分:2)

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

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

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

如果你看到,每个块的维度都小于512,那么每个块的线程数不能超过512个(在更好的体系结构中,在较新的CUDA版本中,限制已经更改为1024)。 Till Fermi每个处理器都有32或48个内核,因此拥有超过512个线程没有多大意义。新的Kepler架构每个多处理器有192个核心。

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

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