前言

为什么要写这本书

本书主要是为软件工程师写的。在解释为什么笔者认为软件工程师需要这本书之前,先来介绍并行、并发和代码性能优化这3个概念,理解这3个概念是阅读本书的基础。

·并行对应的英文单词是parallelism,是指在具有多个处理单元的系统上,通过将计算或数据划分为多个部分,然后将各个部分分配到不同的处理单元上,各处理单元相互协作,同时运行,以达到加快求解速度或者扩大求解问题规模的目的。

·并发对应的英文单词是concurrency,是指在一个处理单元上运行多个应用,各应用分时占用处理单元。这是一种微观上串行、宏观上并行的模式,有时也称其为时间上串行、空间上并行。

·代码性能优化是指通过调整源代码,使其生成的机器指令能够更高效地执行。通常高效是指执行时间更少、使用的存储器更少或能够计算更大规模的问题。

从大的方面来说,并行和并发都是代码性能优化的一种方式。但是今天并行和并发已经变得如此重要,以至于需要“开宗立派”。为了划清并行、并发和代码性能优化的界线,在本书中,代码性能优化特指除并行和并发以外的代码优化方法,比如向量化和提高指令流水线效率。在本书中,笔者将向量化独立出来解说。

2003年以前,在摩尔定律的作用下,单核标量处理器的性能持续提升,软件开发人员只需要写好软件,而性能的提升则等待下次硬件的更新。2003年之前的几十年里,这种“免费午餐”的模式一直在持续。2003年后,主要由于功耗的原因,这种“免费午餐”已经不复存在了。为了生存,各硬件生产商不得不采用各种方式提高硬件的计算能力。目前最流行的3种方式如下:

1)让处理器在一个周期处理多条指令,多条指令可相同可不同。如Intel Haswell处理器一个周期可执行4条整数加法指令、2条浮点乘加指令,访存和运算指令也可同时执行。

2)使用向量指令,主要是SIMD和VLIW技术。SIMD技术将处理器一次能够处理的数据位数从字长扩大到128位或256位,从而提升了计算能力。

3)在同一个芯片中集成多个处理单元,根据集成方式的不同,相应地称为多核处理器或多路处理器。多核处理器是如此的重要,以至于现在即使是手机上的嵌入式ARM处理器都已经是四核或八核的了。

目前绝大部分应用软件都是串行的,串行执行过程符合人类的思维习惯,易于理解、分析和验证。由于串行软件只能在多核处理器中的一个核上运行,和2003年以前的CPU没有多少区别,这意味着花多核处理器的价钱买到了单核的性能。通过多核技术,硬件生产商成功地将提高实际计算能力的任务转嫁给了软件开发人员,而软件开发人员则没有选择,只有直面挑战。

标量单核的计算能力没有办法持续大幅度提升,而应用对硬件计算能力的需求依旧在提升,这是个实实在在的矛盾。在可见的将来,要解决这个矛盾,软件开发人员只有选择代码优化和并行。代码优化并不能利用多核CPU的全部计算能力,它也不要求软件开发人员掌握并行开发技术,另外通常也无须对软件架构做改动,而且串行代码优化有时能够获得非常好的性能(如果原来的代码写得很差的话)。因此相比采用并行技术,应当优先选择串行代码优化。一般来说,采用并行技术获得的性能加速不超过核数,这是一个非常大的限制,因为目前CPU硬件生产商最多只能集成几十个核。

从2006年开始,可编程的GPU越来越为大众所认可。GPU是图形处理单元(Graphics Processing Unit)的简称,最初主要用于图形渲染。自20世纪90年代开始,NVIDIA、AMD(ATI)等GPU生产商对硬件和软件加以改进,GPU的可编程能力不断提高,GPGPU(General-Purpose computing on Graphics Processing Units)比以前容易许多。另外,由于GPU具有比CPU更强大的峰值计算能力,引起了许多科研人员和企业的兴趣。

近两三年来,在互联网企业中,GPU和并行计算越来越受到重视。无论是国外的Google、Facebook,还是国内的百度、腾讯、阿里和360,都在使用代码优化、并行计算和GPU来完成以前不能完成的任务。

10年前,并行计算还是大实验室里教授们的研究对象,而今天,多核处理器和GPU的普及已经使得普通人就可以研究它们。对于软件开发人员来说,如果不掌握并行计算和代码性能优化技术,在不久的将来就会被淘汰。

为了学习如何在X86和ARM平台上向量化代码,软件开发人员需要了解Intel和ARM提供的参考资料,它们通常细节丰富,开发人员需要从多如牛毛的细节上(而且通常没有示例)了解其概要,可谓难上加难。而本书通过丰富的简单示例,让读者能够从全局上把握这些扩展的特性。

NVIDIA的CUDA和开放的OpenCL标准越来越得到大家的重视,OpenCL和CUDA在概念上有太多相似的地方,在本书中,笔者尝试将它们放在一起描述,以便于读者理解。

为了帮助读者理解,本书使用了大量的示例。开发人员通常比较忙,因此本书力求简洁明了,点到为止。

读者对象

由于多核处理器和GPU已经非常便宜,而代码优化、向量化和并行已经深入IT行业的骨髓,所有IT行业的从业者都应当阅读本书。如果非要列一个清单,笔者认为下列人员应当阅读:

·互联网及传统行业的IT从业者,尤其是希望将应用移植到多核向量处理器或GPU上的开发人员

·对向量化和并行化感兴趣的专业工作者

·高等院校、研究所的学生及教师

如何阅读本书

本系列包括3本书,本书是此系列的第二本 除本书之外,另两本书分别是《并行算法设计与性能优化》和《科学计算与企业级应用的并行优化》,均由机械工业出版社华章公司(www.hzbook.com)出版。——编者注。本书的重点在于介绍如何利用目前主流的C语言的各种特定硬件或平台的向量化扩展、并行化库,来编写和优化串行代码的性能。而本系列的第一本《并行算法设计与性能优化》则关注并行优化和并行计算相关的理论、算法设计及高层次的实践经验。本书同时关注C程序设计语言的向量化和并行化扩展,以及算法到硬件的映射。

本书不但包括如何使用SSE/AVX向量化扩展、如何使用OpenMP编译制导语句优化运行在X86多核处理器上的代码的性能,还包括如何使用NEON向量化扩展、OpenMP编译制导语句优化运行在移动处理器(ARM)上的代码性能优化,以及使用CUDA和OpenCL优化运行在图形处理器(GPU)上的代码性能优化及并行。不但有实际的各个扩展解析或语言的介绍说明,还有丰富的算法优化实例。作者希望通过这种方式让阅读本书的软件开发人员了解、掌握常见的向量化库、并行编程语言,学会如何使用这些语言或库,将常见算法映射到具体硬件上以获得高性能,以及这些库和语言的优缺点。

整体而言,本书分为如下几个部分:

·代码向量化优化,主要介绍常见的C语言的向量化库,主要是X86平台和ARM平台的向量化扩展。主要内容见第1章和第2章。

·并行程序设计语言,主要介绍目前主流的并行程序设计语言OpenMP、CUDA、OpenCL及OpenACC。主要内容见第3~5章。

·主流的向量化和并行化硬件平台,以及OpenCL程序如何映射到这些平台上。主要介绍Intel Haswell、ARM A15、AMD GCN GPU和NVIDIA Kepler/Maxwell GPU的架构,及OpenCL程序如何在这些硬件上执行。主要内容见第6章。

·常见简单应用的向量化和并行化,主要介绍如何使用前面提到的向量化扩展和并行语言来优化图像处理、线性代数应用的性能。主要内容见第7章和第8章。

第1章 主要介绍Intel SSE/AVX的C语言向量化扩展(内置函数)的相关细节,如支持哪些操作,使用AVX/SSE向量化时需要注意哪些问题。另外还介绍了如何在Intel X86上测得峰值,及如何使用SSE/AVX优化一些实例代码(这些示例由高洋提供)。

第2章 介绍了ARM A15高性能处理器的特性,及ARM提供的向量化扩展(内置函数)NEON的细节。另外介绍了如何在ARM A15处理器上优化彩色图转灰度图、矩阵转置和矩阵乘法等。

第3章 介绍了OpenMP程序设计的细节内容。主要包括OpenMP的环境变量、函数和编译制导语句。本章提供了许多OpenMP编译制导语句构造和子句的编程实例,以帮助读者理解。本章还简单介绍了OpenMP 4.0标准引入的异构并行计算的内容。并以如何使用OpenMP计算圆周率为例,介绍了OpenMP的互斥同步支持,并比较了这些不同同步方式的性能。

第4章 介绍了目前用于异构并行计算平台(主要是GPU)的CUDA和OpenCL的细节。首先介绍了基于GPU的异构并行计算的历史与现状,及GPU和CPU的优缺点。然后介绍了CUDA和OpenCL编程的细节,并展示了如何使用CUDA和OpenCL来计算圆周率。接着介绍了如何基于GPU优化CUDA和OpenCL程序性能。最后以矩阵转置和矩阵乘法为例介绍了如何在GPU上优化程序性能。

第5章 简单介绍了用于异构平台的OpenACC标准。介绍了常见的OpenACC编译制导语句,然后介绍了CUDA和OpenACC如何通过共享存储器指针实现通信。最后以一个简单的迭代算法为例,介绍了如何优化OpenACC程序。

第6章 介绍了常见的并行编程硬件平台的架构及OpenCL程序如何映射到这些平台上执行。简略介绍了Intel Haswell、ARM A15、AMD GCN GPU和NVIDIA Kepler/Maxwell GPU的架构。然后介绍了OpenCL程序如何映射到Intel Haswell处理器、AMD GCN GPU和NVIDIA Kepler/Maxwell GPU上执行。

第7章 详细介绍了如何使用SSE/AVX、ARM NEON、OpenMP和CUDA来优化常见的图像处理应用,比如均值滤波和中值滤波、图像直方图和曼德勃罗集。

第8章 详细介绍了如何使用SSE/AVX、ARM NEON、OpenMP和CUDA来优化两向量距离和稠密矩阵向量乘法。

本书希望通过这种方式让读者渐进地、踏实地拥有并行思维,了解各个向量化和并行化编程库的优缺点,并且能够写出优良的向量化、并行代码。

对并行和代码优化不太了解的人员,笔者希望你们按章节顺序仔细阅读本书;对并行或代码优化非常了解的人员,可按照需求选择章节阅读。

勘误和支持

笔者的水平有限,工作繁忙,编写时间仓促,而向量化、并行和代码优化又是一个正在高速发展、与硬件及算法密切相关、影响因素非常多、博大精深、又具有个人特色的领域,许多问题还没有统一的解决方案。虽然笔者已经努力确认很多细节,但书中难免会出现一些不准确的地方,甚至是错误,恳请读者批评指正。你可以将书中的错误或写得不好的地方通过邮件发送到ly152832912@163.com,或微信联系“风辰”,以便再版时修正,笔者会尽快回复邮件。如果你有更多的宝贵意见,也欢迎发送邮件,期待能够得到你们的真挚反馈。

致谢

首先要感谢我的老婆,她改变了我的人生轨迹,让我意识到人生有如此多的乐趣。

感谢中国地质大学(武汉)图书馆,那是使我对并行计算产生兴趣的地方。感谢中国科学院研究生院和中国科学院图书馆,那里为我奠定了从事并行计算事业的基础。

感谢我的朋友陈实富、赖俊杰、高洋、李修宇等,如果没有你们,我会需要更多时间来提升水平。感谢我的领导王鹏、吴韧和汤晓欧,在这些“技术大佬”和“人生赢家”的指导下,我才会成长得如此迅速。

感谢机械工业出版社华章公司的高婧雅和杨福川,我本无意出版此书,是你们鼓励我将它付梓成书;是你们帮我修改书稿,让它变得可读可理解;是你们帮我修正错误,是你们的鼓励和帮助使得我顺利完成全部书稿。

最后感谢我的爸爸、妈妈、姥姥、姥爷、奶奶、爷爷,感谢你们将我培养成人,并时时刻刻为我提供精神力量!

谨以此书献给我最爱的家人,以及众多热爱代码优化、向量化、并行计算的朋友们!愿你们快乐地阅读本书!

风辰