程序猿玩家细说GTX 680轻取百万线程代码

openkk 12年前
   说起通用计算、并行计算、加速计算,NVIDIA CUDA 无疑是很多开发人员的首选。虽然还有开放的 OpenCL,但在开发环境方面还不够,CUDA 则在经过大力推广后拥有了不错的群众基础。GeForce GTX 600 时代虽然这方面的宣传似乎少了很多,开普勒架构的浮点性能看起来也不强,但其实在并行计算方面仍然有很多进步的,其中蕴藏着不少能量,比如费米架构首创性 地加入了和处理器一样的统一读写二级缓存,GK104核心更把底层的 SMX 进行了变革。    <p>        今天就有一位技术宅程序猿同学,<strong>利用一块 GeForce GTX 680 4GB,展示了如何在开普勒上取得百万级别的线程代码。</strong>看不懂不要紧,内行看门道,外行看热闹。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/1a835d26b73ec7294a106cde2728327c.jpg" width="550" height="366" /></a></p>    <p>        据说,技术宅的厕纸用完了之后就会用书本。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/9cc9311d228e521e911bd5947769dbdd.jpg" width="550" height="366" /></a></p>    <p>        技术宅受到刺激后,迷上了超大规模程序设计,那个叫什么 CUDA 神秘兮兮的东西。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/b6b04e348b46cf791e363539941fe25c.jpg" width="550" height="366" /></a></p>    <p>        平台艳照。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/3fc69893ebdae6e0ff99a65e37286cd6.jpg" width="550" height="503" /></a></p>    <p>        CUDA 的环境配置:<strong>首先安装 CUDA 工具箱,然后安装显卡驱动,最后安装 CUDA SDK。前两步必不可少,第三步可以省略。</strong></p>    <p style="text-align:center;"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/0406b481793ccb98e7721620c640d8ca.jpg" width="477" height="395" /></p>    <p>        G80、GT200的 SM 架构差异,导致了 CUDA 程序上的不同优化。</p>    <p>        老架构就不花时间去讨论了,直奔在通用计算大放异彩的费米架构,<strong>革命性的统一读写一级、二级缓存</strong>有些模仿 CPU 设计的味道。下面结合硬件来说说 CUDA 如何根据它来优化。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/dea9f0475ed65063c8fa3453fa8856e7.jpg" width="550" height="601" /></a></p>    <p>        <strong>费米的 SM 拥有 3 万个高速寄存器,保证了线程的快速切换,要知道八核心 CPU 也不过几百个而已。</strong>16路高速存储通道来并行操作 64KB 的片上高速缓存,速度可达到 1TB/s级别。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/ee27184cf7cf631ce6d9f8d324221720.jpg" width="550" height="469" /></a></p>    <p>        <strong>开普勒再度加强,在晶体管数量控制在 35.4 亿个的情况下竟然塞进了比费米多一倍的 CUDA 核心,寄存器也翻番达到6.5万个</strong>,存储通道也多了一倍。</p>    <p>        要让 CUDA 程序为开普勒架构优化,首要任务是让 Block 里面的线程尽量的多。GT200 SM 允许的活动线程是 1024 个,费米优化的代码可以增加到 1536 个,开普勒则进一步提高到 2048 个(注:GK110核心也会如此)。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/9ff57253a3f12cd91e81c98200571d06.jpg" width="550" height="526" /></a></p>    <p>        这个是软件层次对应的硬件层次。Block 编程上的线程块在 SM 里分时运行,一个 CUDA 程序会有多个 blcok 并行运行,而每个 block 线程块又包含了数十到上百的线程,也就构成了 CUDA。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/169e5f1b7e99c6ea42cc79d63209c875.jpg" width="550" height="399" /></a></p>    <p>        不同架构对应不同的计算规格,代码优化也要针对性地进行,比如<strong>开普勒可以支持更大的 block 内部线程</strong>,可以让更多线程进行片上同步工作,另外<strong>费米和开普勒有 DRAM 缓存</strong>,所以对 DRAM 优化的力度没有 G80 那么吃力。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/a2da33036d9cd8ca60026b332aa41994.jpg" width="550" height="275" /></a></p>    <p>        开普勒在 CUDA 上加入了更大的革新,特别是<strong>支持多内核函数的并行执行</strong>。注意,<strong>一个 CUDA 内核就是一个可具备百万线程的并行程序</strong>,而这些内核函数更可以并行执行。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/3ad7d53f7189bb7196f7341f17df761a.jpg" width="546" height="429" /></a></p>    <p>        开普勒在内核函数执行引擎上比费米有了大幅度的加强。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/db76fab01eedf753e506055e9bb0bdbf.jpg" width="550" height="341" /></a></p>    <p>        从 CUDA 的发展角度看,NVIDIA 一共经历了四代架构更新,而<strong>唯一不变的是 warp 硬件指令的宽度</strong>。(这里和 AMD 的稍微不同,后者硬件指令宽度是 64)。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/98e65210f329bdebcc4342b05258e21e.jpg" width="550" height="508" /></a></p>    <p>        想要 CUDA 编程的童鞋可以开始了。安装好驱动、工具箱后,就可以安装C/C++编辑器了。<strong>用微软的 Visual Studio 比较方便</strong>,只是它本身不支持 CUDA 语法,需要在工具箱里面找到语法配置文件,配置一下就可以了。</p>    <p>        其实,<strong>CUDA 代码也不神秘,就是C和一些C的扩展而已</strong>,懂C的朋友很容易就明白了,不过 CUDA 代码编译的系统执行文件有点小小麻烦,因为需要调用 2 个编译器,一个是C/C++,另外一个是 GPU 编译器。还好<strong>NVDIA 提供了比较方面的 NVCC 工具,这是一个半编译和连接工具,主要用语分离 CPU 和 GPU 代码,然后把 CPU 代码给 VS 编译,然后自己编译 GPU 代码。</strong>编译后连接好就可以编程执行代文件了。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/81aa52588cef84b3f00eefb1ae350f95.jpg" width="550" height="336" /></a></p>    <p>        这是 NVIDIA 的性能调试工具,非常不错,能够可视化分析硬件的使用率。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/71e90c0f2804f0d0ff547bf987e47fab.jpg" width="550" height="336" /></a></p>    <p>        程序的产生都可以清楚分析,这是一个 CUDA 二维傅立叶变换的例子。不过计算单元的使用率不高,虽然很密集地运行 kernel,但是每个 kernel 的执行时间不长,不到 30 微秒。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/71ac00f72aa9bc0ca6401fb1fa328a7d.jpg" width="550" height="338" /></a></p>    <p>        用性能工具来测试 CUDA 体渲染程序。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/a3fdc01f59112ed5227eba7a5b64076e.jpg" width="550" height="336" /></a></p>    <p>        开普勒有多 kernel 并行运行的优势。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/a05ec6fae71b87e67632e85437b2b8e6.jpg" width="550" height="336" /></a></p>    <p>        黄色感叹号表明程序可能有能够加强性能的改进之处。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/27892bfdb387d4f8c448f2a7478b03e4.jpg" width="550" height="317" /></a></p>    <p>        载入粒子模拟仿真。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/7b42bb50150d4babc4e5a3b2c42842a6.jpg" width="550" height="336" /></a></p>    <p>        程序性能分析。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/09b733de47ca6c9a7b8847ed56fae06d.jpg" width="550" height="336" /></a></p>    <p>        这一段小代码让 GPU 所有的 CUDA 核心都运行一个内循环加法。当然,真正的并行程序并没有那么简单。这里是主要是用性能工具来分析 GPU ALU 的使用率。</p>    <p align="center"><a target="_blank"><img alt="程序猿玩家细说GTX 680轻取百万线程代码" src="https://simg.open-open.com/show/e7a020b8ec6ad08439dfc3689301bc8f.jpg" width="550" height="336" /></a></p>    <p>        代码主要一一对应地生成 1536 个线程,每个线程做一个内循环,模拟 GPU 真实的负载工作。另外如果需要,可以调整 Grid 网格的规格增加线程,要生成一个百万线程,只需要在内核函数输入 1000000/192=540个 block 线程块就可以了。<strong>GPU 的最大优势是可以实现线程的高速切换</strong>,速度是 CPU 的上千到几千倍。</p>    <p>        最后,技术宅跟我们分享了 CUDA 的一些感觉。<strong>CUDA 是一个异构的执行体系,就是说 CUDA 里面你既要写 CPU 代码,也要内嵌 GPU 内核代码。它是用来发挥 CPU、GPU 两种架构的长处的。</strong>你 认为 CPU 执行更好的部分,可以在函数用 CPU 代码实现;你认为某部分交给 GPU 执行跟好,就可以写成 GPU 代码。打个比方,某个负责引用需要一个并行度极高的运算和一个串行读很高的复杂运算构成,那就可以先通过 PCI-E传输数据给 GPU 并行执行,然后返回给 CPU 做执行,最终输出结果。CUDA 的另外一个用法是 CUDA 图形相互操作,比如支持 PhysX 物理加速的游戏就是,在图形渲染哪里和 CUDA 物理加速相互操作。</p>    <p>        看到这里,是不是发现自己对 CUDA 有了更加深刻的认识?其实它也不是多么神秘的,就是个开发环境而已。</p>    <div id="come_from">    来自:     <a id="link_source2" href="/misc/goto?guid=4958522006188152242" target="_blank">驱动之家</a>    </div>