cuda by example【读书笔记1】

时间:2021-12-13 23:47:47

cuda

1. 以前用OpenGL和DirectX API简介操作GPU,必须了解图形学的知识,直接操作GPU要考虑并发,原子操作等等,cuda架构为此专门设计。满足浮点运算,用裁剪后的指令集执行通用计算,不是仅限于执行图形计算,不仅可以任意读写内存,还可以访问共享内存。提供了许多功能加速计算,设计了CUDA C语言编写通用计算

2. 在GPU上执行的函数通常称为核函数 __global__修饰符告诉编译器,函数应该编译到GPU上而不是主机 cuda编译器将负责实现从主机代码中调用GPU代码

3. kernel<<1,1>>( ); 尖括号里面的参数不是传递给设备代码的参数(参数1是设备在执行核函数时使用的并行线程块数量,参数2是并行线程数量),而是告诉运行时如何启动设备代码。传递给设备代码的参数是放在圆括号里的。会根据尖括号的内容在多处理器上启动多个核函数副本。

4. CUDA C的强大之处是淡化了主机代码和设备代码之间的差异 一定不能在主机代码里对cudaMalloc( )返回的指针进行解引用,主机代码可以把它作为参数传递,算术运算,甚至转换为不同的类型,但绝对不可以使用这个指针来读取或写入内存。要用cudaFree( )释放。主机指针只能访问主机内存,设备指针只能访问设备内存。

5. cudaMemcpy( )的最后一个参数是cudaMemcpyDeviceToHost表示设备指针到主机指针copy

6. 并行线程块的集合称为线程格(Grid), 并行线程(Thread)的集合称为线程块(Block)。线程块的最大数量不能超过65535,线程数量不能超过512,可以综合利用线程块和线程数量计算。

7. CUDA C的程序中可以使用全局内存和共享内存: __shared__表示GPU上的共享内存单元,作用是实现线程间的通信。调用核函数时,每一个线程块会运行一个核函数的副本,同时编译器也会为每一个线程块产生一个共享内存单元的私有副本,供该线程块内所有线程使用。所以内存存储单元只用线程数索引就行。

8. 在GPU上对任意矢量求和,为了突破上述的硬件上限,我们将并行的线程数量看成是处理器的数量。尽管GPU处理单元的数量可能小于或大于这个值。我们认为每个线程在逻辑上都可以并行执行,硬件可以调度它们以便实际执行。要注意线程索引和线程递增量的写法。这里线程递增的是总的线程数。相当于对程序的工作量进行了总线程数规模的划分。

         __shared__ float cache[threadsPerBlock];
int tid = threadIdx.x + blockIdx.x * blockDim.x; // 线程索引
int cacheIndex = threadIdx.x; // 共享内存的线程索引 float temp = ;
while (tid < N) { // N维向量
temp += a[tid] * b[tid];
tid += blockDim.x * gridDim.x; // 每次递增 block数*线程数
} // set the cache values
cache[cacheIndex] = temp;

9. __syncthreads( ) 的用法是同步,等待所有线程完成这句指令在进行下面的语句。这里要注意不能随便把它放到if条件语句中,因为有线程中有if语句称为线程发散,除非线程块中每个线程都执行了__syncthreads( ) 否则没有任何线程能执行__syncthreads( ) 之后的指令。 一般在写入共享内存和读取共享内存之间添加同步点

10. 用常量内存来替换全局内存可以有效的减少内存带宽