本書主要是為軟件工程師寫的。在解釋為什么筆者認為軟件工程師需要這本書之前,先來介紹并行、并發和代碼性能優化這3個概念,理解這3個概念是閱讀本書的基礎。并行對應的英文單詞是parallelism,是指在具有多個處理單元的系統上,通過將計算或數據劃分為多個部分,然后將各個部分分配到不同的處理單元上,各處理單元相互協作,同時運行,以達到加快求解速度或者擴大求解問題規模的目的。并發對應的英文單詞是concurrency,是指在一個處理單元上運行多個應用,各應用分時占用處理單元。這是一種微觀上串行、宏觀上并行的模式,有時也稱其為時間上串行、空間上并行。
國內頂尖的并行計算領域知名專家風辰多年實踐經驗總結,兼具深度和高度
簡潔明了,輔以大量示例,全面介紹主流硬件平臺上的向量化庫,并行編程語言的設計細節,OpenCL程序在硬件平臺的映射與執行,并給出多個性能優化實戰案例
近兩三年來,在互聯網企業中,代碼優化、并行計算和GPU的使用越來越受到重視。無論是國外的Google、Facebook還是國內的百度、騰訊、阿里和360,都在使用代碼優化、并行計算和GPU來完成以前不能完成的任務。而現在中小型企業也在使用代碼優化和并行化技術來提升產品的使用體驗。對于軟件開發人員來說,如果不掌握并行計算和代碼性能優化技術,在不久的將來就會被淘汰。
《高性能計算技術叢書:并行編程方法與優化實踐》不但涵蓋主流的現代多核處理器平臺架構、并行指令集和編譯制導語句,還介紹了常見并行程序設計語言、圖像處理和線性代數算法的代碼性能優化,OpenCL程序如何在AMD、GCN、GPU和NVIDIA GPU上的編程執行,以及CUDA和OpenCL在GPU上的執行區別等。
筆者基于自身豐富的理論和實踐經驗,將大量“不傳之秘”融入本書,主要體現在:
深入剖析了SSE/AVX和NEON指令集、OpenMP編譯制導語句、基于GPU的異構并行計算環境(OpenCL、CUDA和OpenACC),并輔以細節豐富的簡單示例。
簡潔明了地展現了如何在不同的現代處理器上執行OpenCL程序及執行的區別,通過了解這些區別,讀者能夠編寫出在各個處理器上性能良好的程序。
詳細描述了如何在X86、ARM、AMD和NVIDIA GPU上實現、優化代碼的性能,讓讀者能夠很好地評估在不同處理器上實現性能良好的程序需要的考量
為什么要寫這本書本書主要是為軟件工程師寫的。在解釋為什么筆者認為軟件工程師需要這本書之前,先來介紹并行、并發和代碼性能優化這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本書,本書是此系列的第二本。本書的重點在于介紹如何利用目前主流的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,或微信聯系“風辰”,以便再版時修正,筆者會盡快回復郵件。如果你有更多的寶貴意見,也歡迎發送郵件,期待能夠得到你們的真摯反饋。
致謝首先要感謝我的老婆,她改變了我的人生軌跡,讓我意識到人生有如此多的樂趣。
感謝中國地質大學(武漢)圖書館,那是使我對并行計算產生興趣的地方。感謝中國科學院研究生院和中國科學院圖書館,那里為我奠定了從事并行計算事業的基礎。
感謝我的朋友陳實富、賴俊杰、高洋、李修宇等,如果沒有你們,我會需要更多時間來提升水平。感謝我的領導王鵬、吳韌和湯曉歐,在這些“技術大佬”和“人生贏家”的指導下,我才會成長得如此迅速。
感謝機械工業出版社華章公司的高婧雅和楊福川,我本無意出版此書,是你們鼓勵我將它付梓成書;是你們幫我修改書稿,讓它變得可讀可理解;是你們幫我修正錯誤,是你們的鼓勵和幫助使得我順利完成全部書稿。
最后感謝我的爸爸、媽媽、姥姥、姥爺、奶奶、爺爺,感謝你們將我培養成人,并時時刻刻為我提供精神力量!
謹以此書獻給我最愛的家人,以及眾多熱愛代碼優化、向量化、并行計算的朋友們!愿你們快樂地閱讀本書!
風辰
前言
第1章 X86SSE/AVX指令集
1.1 SSE內置函數
1.1.1 算術運算
1.1.2 邏輯運算
1.1.3 比較
1.1.4 加載和存儲
1.2 AVX內置函數
1.2.1 算術運算
1.2.2 邏輯運算
1.2.3 比較
1.2.4 加載和存儲
1.3 優化實例及分析
1.3.1 如何測得CPU的浮點峰值性能
1.3.2 積分計算圓周率π
1.3.3 稀疏矩陣向量乘法
1.3.4 二維單通道圖像離散卷積
1.4 本章小結
第2章 ARMNEONSIMD指令優化
2.1 NEON指令集綜述
2.2 ARMA15處理器性能
2.3 NEON支持的操作
2.3.1 基本算術運算
2.3.2 基本比較運算
2.3.3 基本數據類型轉換及舍入運算
2.3.4 基本位運算
2.3.5 基本邏輯運算
2.3.6 基本設置加載存儲操作
2.3.7 特殊操作
2.4 應用實例
2.4.1 彩色圖像轉灰度圖像
2.4.2 矩陣轉置
2.4.3 矩陣乘
2.5 本章小結
第3章 OpenMP程序設計
3.1 OpenMP編程模型
3.1.1 OpenMP執行模型
3.1.2 OpenMP存儲器模型
3.2 環境變量
3.3 函數
3.3.1 普通函數
3.3.2 鎖函數
3.4 OpenMP編譯制導語句
3.4.1 常用的OpenMP構造
3.4.2 常用的OpenMP子句
3.5 OpenMP異構并行計算
3.6 OpenMP程序優化
3.6.1 OpenMP程序優化準則
3.6.2 OpenMP并行優化實例
3.7 本章小結
第4章 基于GPU的異構并行計算環境:CUDA與OpenCL
4.1 GPU計算概述
4.1.1 GPU計算歷史
4.1.2 CUDA概述
4.1.3 OpenCL概述
4.2 異構并行計算模型
4.2.1 平臺模型
4.2.2 執行模型
4.2.3 存儲器模型
4.2.4 編程模型
4.3 C語言接口
4.3.1 OpenCLC語言
4.3.2 CUDAC語言
4.4 基于GPU的異構并行計算性能優化
4.4.1 總體優化準則
4.4.2 全局存儲器優化
4.4.3 合并訪問
4.4.4 局部存儲器
4.4.5 存儲體沖突
4.4.6 常量存儲器優化
4.4.7 CUDA紋理存儲器優化
4.4.8 寄存器及私有存儲器優化
4.4.9 工作組數目及大小
4.4.1 0占用率
4.4.1 1指令優化
4.4.1 2分支優化
4.4.1 3數據傳輸優化
4.5 GPU與CPU精度差別
4.6 矩陣轉置
4.6.1 初次實現
4.6.2 滿足合并訪問的實現
4.6.3 沒有存儲體沖突的實現
4.7 矩陣乘法
4.7.1 初次實現
4.7.2 矩陣分塊實現
4.8 本章小結
第5章 OpenACC
5.1 OpenACC編程模型
5.1.1 執行模型
5.1.2 存儲器模型
5.2 編譯制導語句
5.2.1 kernels構造
5.2.2 parallel構造
5.2.3 線程配置相關子句
5.2.4 data構造
5.2.5 loop構造
5.2.6 atomic構造
5.2.7 dtype子句
5.2.8 reduction子句
5.2.9 變量可見性子句
5.2.1 0if子句
5.2.1 1async和wait
5.3 OpenACC和CUDA協作
5.3.1 CUDA使用OpenACC生產的數據
5.3.2 OpenACC使用CUDA生產的數據
5.4 兩小時性能提升10倍
5.5 本章小結
第6章 多核向量處理器架構及OpenCL程序映射
6.1 多核向量處理器架構
6.1.1 IntelHaswellCPU架構
6.1.2 ARMA15多核向量處理器架構
6.1.3 AMDGCNGPU架構
6.1.4 NVIDIAKepler和MaxwellGPU架構
6.2 OpenCL程序在多核向量處理器上的映射
6.2.1 OpenCL程序在多核向量CPU上的映射
6.2.2 OpenCL程序在NVIDIAGPU上的映射
6.2.3 OpenCL程序在AMDGCN上的映射
6.3 本章小結
第7章 利用多種技術優化圖像處理中的算法性能
7.1 圖像濾波
7.1.1 均值濾波
7.1.2 中值濾波
7.2 圖像直方圖
7.2.1 OpenMP實現
7.2.2 CUDA實現
7.3 曼德勃羅集
7.3.1 串行算法
7.3.2 不適合進行向量化
7.3.3 OpenMP實現
7.3.4 CUDA實現
7.4 本章小結
第8章 利用多種技術優化線性代數中的算法性能
8.1 兩向量距離
8.1.1 串行代碼
8.1.2 循環展開代碼
8.1.3 AVX指令加速
8.1.4 NEON實現
8.1.5 CUDA實現
8.2 稠密矩陣與向量乘法
8.2.1 串行算法
8.2.2 AVX指令加速
8.2.3 NEON實現
8.2.4 CUDA實現
8.2.5 OpenMP實現
8.3 本章小結