转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=961

上周的帖子刚提到NVIDIA宣布了CUDA 4,昨天就收到NV的邮件说CUDA 4.0 RC可以下载了。developer注册用户可以从http://developer.nvidia.com/object/cuda_4_0_RC_downloads.html找到。

本来不打算说什么,碰巧在某网站看到了一篇所谓的“新特性解析”,典型的一个不懂技术的小编装懂地写软文。所以我不得不在这里拨乱反正,以免国内读者受其误导。

CUDA 4.0的更新主要集中在三方面

  1. 简化并行程序的移植
  2. 加速多GPU编程
  3. 更好的工具链支持

简化并行程序移植

在CUDA(其实还有AMD的stream)出来之前,并行程序移植GPU只能直接用shader,限制诸多,代码不灵活,基本算重写,而不是移植。有了CUDA之后,情况有所好转。在CUDA 4.0下,移植的任务变得更简单了。NV的说法是,它支持以下几大新特性:

  1. 在多个CPU线程之间共享GPU
  2. 单个CPU线程也可以访问所有GPU
  3. 系统内存地址可以直接映射给GPU,不需要拷贝
  4. 新的CUDA C/C++特性
  5. Thrust模板primitive库
  6. NPP图像/视频处理库
  7. 多层纹理

其中第1点和第2点说的是一回事。在以前的CUDA上,要使用多个GPU就得开多个thread,每一个thread负责与一个GPU打交道(或者说不同GPU得在不同的context下)。这其实是个很愚蠢的限制。现在CUDA 4取消了这个限制,任何thread可以与任何GPU打交道,简化了context管理。只能说它没那么愚蠢了。

第3点我认为是CUDA 4里最重要的更新。内存地址映射在以前的CUDA上就部分支持,能把一个cudaHostMalloc出来的系统内存的地址映射到GPU,让GPU可以直接访问系统内存中的数据。这样就可以克服显存不足的缺点(但是是以牺牲一部分性能为代价的)。CUDA 4的好处是,任意malloc/new出来的内存空间,都可以通过调用cudaHostRegister注册给GPU,同样也有cudaHostUnregister来取消这个注册,而不需要cudaMemcpy。实际上PCIE本身就支持把一个host地址映射到device端,而且无论D3D还是OpenGL都用这种方式来处理buffer和texture等大数据,不是什么新东西。除了性能之外,这个特性带来的另一个好处是,可以把一个大的CPU程序一点一点地改成GPU程序,并一步一步验证结果。以前这件事情需要前后都插入不少cudaMalloc和cudaMemcpy等代码,繁琐而且容易出错。

第4点只是个编译器更新,把C++风格的new/delete、虚函数、内嵌PTX汇编等加入CUDA编译器。D3D11级别的GPU都支持函数指针,所以虚函数并不是什么难事。至于new/delete和内嵌PTX汇编,更是改不了几行编译器代码就能实现的,倒是应该问问为什么以前不支持了。

第5点的Thrust是个第三方库,模仿C++ STL的方法来把CUDA的一些数据结构和primitive算法(scan、reduce、sort等)封装起来,让没有CUDA基础的人也可以在C++程序里使用GPU加速。这个库已经发布很久了,跟以前的CUDA也可以很好地配合。这次只是把它集成了,也不是新东西。

第6点也是个CUDA库,NV自己的,集成进来了。

第7点的Layered texture其实就是D3D10+的Texture Array,以前不知道为什么从来不在CUDA里暴露出来,现在突然列为“新特性”。

加速多GPU编程

这里的新技术被称为GPUDirect 2.0。之前有个不出名的GPUDirect 1.0,先在升级成让GPU之间可以点对点显存访问、传输数据、同步,可能需要特定的芯片组才能支持。这对多GPU来说确实是个福音。以前甚至让多个GPU做同样的事情,都需要把同样的数据依次拷贝到每一个GPU上,然后开始干活。以至于拷贝的时间掩盖了计算的时间(这个拷贝是独占的,不能让多个GPU并行拷贝),GPU越多反而越慢。更恶心的是,一个GPU要访问另一个GPU的计算结果,就得cudaMemcpy到host memory,然后再次cudaMemcpy到另一个GPU。现在只要一次cudaMemcpy就能轻松搞定。

实际上,这件事情也要求把host memory和多个device memory统一编址(UVA),这样的cudaMemcpy才能认清是从哪里到哪里。引用NV的一张图就是:

Unified Virtual Addressing

有了UVA,在cudaMemcpy的时候就不用指定cudaMemcpyHostToHost、cudaMemcpyHostToDevice、cudaMemcpyDeviceToHost、cudaMemcpyDeviceToDevice,而直接用cudaMemcpyDefault就可以了。为什么这个到了4.0才支持,就得问NV了。

更好的工具链支持

从文本界面的profiler,到Visual Profiler,CUDA的工具链一直在进步。CUDA 4中的profiler除了以前的显示kernel执行时间、占用率、各种指令的开销之外,还可以给出总的统计和优化提示,更加实用。cuda-gdb也得到了更新,支持C++调试。

CUDA x86

最后NV还提到了PGI CUDA x86的最新进展。四五月份会发布1.0(非常的初级,甚至不支持多核),八月份发布1.1(支持多核、SSE/AVX)。

总结

我认为CUDA 4的绝大部分更新都来自于上层和外围的改进,核心更新的亮点是系统内存地址映射和统一寻址,其它都是不值一提的老掉牙的东西。也许NV也学了google的版本大法,没事就出一个新的大版本。CUDA 4虽然值得期待,但老这样把本来就应该有的东西当作新特性来作秀就不好了。

Share GPUs across multiple threads
• Single thread access to all GPUs
• No-copy pinning of system memory
• New CUDA C/C++ features
• Thrust templated primitives library
• NPP image/video processing library
• Layered Textures