◆ CUDA的本质
CUDA的本质是,NVIDIA为自家的GPU编写了一套编译器NVCC极其相关的库文件。CUDA的应用程序扩展名可以选择是.cu,而不是.cpp等。
NVCC是一个预处理器和编译器的混合体。当遇到CUDA代码的时候,自动编译为GPU执行的代码,也就是生成调用CUDA Driver的代码。如果碰到Host C++代码,则调用平台自己的C++编译器进行编译,比如Visual Studio C++自己的Microsoft C++ Compiler。然后调用Linker把编译好的模块组合在一起,和CUDA库与标准CC++库链接成为最终的CUDA Application。由此可见,NVCC模仿了类似于GCC一样的通用编译器的工作原理(GCC编译CC++代码本质上就是调用cc和g++)。
CUDA在执行的时候是让host里面的一个一个的kernel按照线程网格(Grid)的概念在显卡硬件(GPU)上执行。每一个线程网格又可以包含多个线程块(block),每一个线程块中又可以包含多个线程(thread)。
以军队来打比方,每一个线程,就相当于每一个士兵,当要执行某一个大的军事任务的时候,大将军(Host)发布命令,把这次行动分解成一个一个的子任务(kernel_1,kernel_2……kernel_M),每个子任务由不同的统领(Grid)负责,各统领又把任务分成一部分一部分,划分给手下的小头目(Block),这些任务就由小头目下的士兵(Thread)去执行完成。
通过CUDA编程时,将GPU看作可以并行执行非常多个线程的计算设备(compute device)。它作为主CPU的协处理器或者主机(host)来运作:换句话说,在主机上运行的应用程序中数据并行的、计算密集的部分卸载到此设备上。
经过了CUDA对线程、线程块的定义和管理,在支持CUDA的GPU内部实际上已经成为了一个迷你网格计算系统。在内存访问方面,整个GPU可以支配的存储空间被分成了寄存器(Register)、全局内存(External DRAM)、共享内存(Parallel Data Cache)三大部分。其中寄存器和共享内存集成在GPU内部,拥有极高的速度,但容量很小。共享内存可以被同个线程块内的线程所共享,而全局内存则是我们熟知的显存,它在GPU外部,容量很大但速度较慢。经过多个级别的内存访问结构设计,CUDA已经可以提供让人满意的内存访问机制,而不是像传统GPGPU那样需要开发者自行定义。
在CUDA的帮助下普通程序员只要学习一点点额外的GPU架构知识,就能立刻用熟悉的C语言释放GPU恐怖的浮点运算能力,通过CUDA所能调度的运算力已经非常逼近万亿次浮点运算(GeForce 280GTX单卡浮点运算能力为933GF LOPS)。而在此之前要获得万亿次的计算能力至少需要购买价值几十万元的小型机。