彩虹、独角兽和性能便携性

一个古老的犹太族寓言讲述了一个穷人向拉比寻求建议,他家里人多,房子小,感觉很拥挤。拉比告诉信徒,在房子里养一只山羊,一个月后再来见他。穷人很疑惑,但是没有争辩,他将一只山羊安置在房子里。一个月以后,拉比让穷人把山羊带走,一周后再来见他。不出所料,一周后,穷人感谢拉比,他的心情舒畅多了,因为他觉得家里没有那么挤了。

目前,只有专用语言才能编写 GPGPU 程序,因此,编程人员认为在代码无法在 CPU 和 GPU 上通用的原因是缺乏支持所有硬件目标的语言。这种情况就像是在房子里养山羊。带走山羊只会回到最初的问题,造成问题已经解决的假象。

现在,OpenCL™、OpenACC 语言不断改进,使用 C++ 语言编写 GPU 程序也将很快变为现实,人们对此抱有极大的期待。每次实现技术突破时,书刊报纸和网上便会出现关于便携式代码(甚至性能便携性)的文章。近期还有消息称支持 GPGPU 的 C++ 有了新突破,受这些消息的引导,读者可能会认为在 CPU 和 GPU 之间维护单个来源代码库成为了可能。

真正的问题是什么?

即使存在支持 CPU 和 GPGPU 通用语言,问题的关键仍然是多数 CPU 和多数 GPU 之间的硬件架构有多方面的差异,妨碍了性能便携性。

“便携性”和“性能便携性”之间的区别无疑是性能。从实用角度来说,我们都了解依赖(至少在一定程度上依赖)应用要求意味着什么。假如您有两个不同的应用,一个面向 CPU,另一个面向 GPGPU,它们的性能相当不错。但是,您知道长期维护两个代码库比维护一个代码库更昂贵,您想用性能便携性应用替换两个代码库。如果用单个应用代替两个最佳应用,您准备放弃多少性能?

可能多数人会接受失去 5% 的性能,很少人能够接受失去 5 倍的性能。

面向并行计算的硬件架构支持存在着差异,这是众所周知的事实:

  • CPU(如基于最新版双插槽英特尔® 至强® 处理器的服务器)提供数十个双向超线程内核。这些内核是高性能乱序内核。相比之下,GPGPU 提供数百个低性能标量处理器。

  • GPGPU 一般使用超线程交换来隐藏长时延迟操作的延迟,包括内存访问。CPU 最多使用少数几个的超线程,依赖高速缓存架构吸收内存延迟。

  • GPGPU 一般使用硬件在线程之间交换,而 CPU 使用操作系统线程和用户模式调度程序处理调度任务。

  • 从架构上来讲,许多 CPU 在软件上显示两个级别的并行性:内核数量和 SIMD(单指令,多数据)支持。SIMD 支持在单个指令内处理多个数据因素。相比之下,在多个内核上运行相同的指令序列,内核可以彼此独立执行,处理的进度也各不相同。

硬件架构的差异决定了算法选择的不同。算法选择通常与对算法进行并行化处理相结合。大家都知道,有些算法的序列算法形式更高效,但是无法实行并行化,或面向某些硬件目标的并行效率不高,而有些算法序列化的效果不理想,并行化实施却非常高效。

硬件差异对算法选择的影响有很多种,难以进行简要概括。算法对并行化的影响也非常多,至少人们已经深入了解了部分影响:

  • 相比 CPU,并行化 GPGPU 需要更多的并行任务。

  • 为了分摊软件调度程序的开销,GPGPU 并行化支持细粒度,CPU 并行化支持粗粒度。对于 CPU,矢量化属于细粒度,但是对于多数 GPGPU,并行化和矢量化借助内核编程一同进行。

  • 采用分块、阻塞和预取等技术的并行化的高速缓效率存对 CPU 的影响更大。GPGPU 编程人员无需费力提升实施的高速缓存效率,便可接近最佳性能。

总之,缺乏语言语法必然在优化性能时妨碍效率的提升,同时,需要在语言的范围以外考虑性能工程,尤其是并行编程领域的性能工程。语言的改进和跨平台可用性是重大的进步,但是它们本身无法实现性能便携性。

Para obtener información más completa sobre las optimizaciones del compilador, consulte nuestro Aviso de optimización.