【梳理】計算機組成與設計 第3章 算術 第3節 數據級並行(內附文檔高清截圖)

配套教材:
Computer Organization and Design: The Hardware / Software Interface (5th Edition)
這是專業必修課《計算機組成原理》的複習指引。建議將本複習指導與博客中的《簡明操作系統原理》配合複習。
在本文的最後附有複習指導的高清截圖。需要掌握的概念在文檔截圖中以藍色標識,並用可讀性更好的字體顯示 Linux 命令和代碼。代碼部分語法高亮。
計算機組成原理不是語言課,本複習指導對用到的編程語言的語法的講解也不會很細緻。如果不知道代碼中的一些關鍵字、指令或函數的具體用法,你應該自行查找相關資料。


第三節 數據級並行

24、隨着圖形和音頻的需求越來越強勁,CPU架構師們實現了數據級並行(data level parallelism,DLP),也稱子字(亞字)並行(subword parallelism)。數據級並行在實際應用中一般說成更具體的方法,比如向量化(矢量化,vectorization)或者SIMD(single instruction multiple data,單指令多數據)。向量化計算是一種特殊的並行計算的方式,相比於一般程序在同一時間只執行一個操作的方式,它可以在同一時間執行多次操作,通常是對不同的數據同時執行同樣的一個或一批指令,或者說把指令應用於一個數組 / 向量。也就是說,向量化使得一次可以對多個數據進行操作。

25、ARM的NEON指令集就是一種向量化的指令集。它可以並行8-bit、16-bit、32-bit和64-bit的整數,以及32-bit的浮點,但不可以並行64-bit的浮點。Intel的MMX / SSE / AVX也是向量化的指令集。NEON支持的基本操作如下圖:(I代表定點數(小數點位置不變),S和U代表有符號和無符號整數)

Intel在SSE2(2001)時期就支持對雙精度的並行化。SSE / SSE2的浮點指令如下。其中xmm是SSE指令專用的寄存器,SS = 單個未向量化的單精度浮點,SD = 單個未向量化的雙精度浮點,PS = 打包的單精度浮點,PD = 打包的雙精度浮點(S = scalar,P = packed)。A代表128-bit操作數在內存中對齊(aligned),U代表未對齊。H和L分別代表128-bit寄存器的高半部分和低半部分。

後來Intel發佈了AVX指令集,同時支持將更多的整數和浮點放在一個寄存器裏同時預算。

26、C++早已支持了通過硬件進行矢量化運算。下面是一個DGEMM(Double precision general matrix multiply,雙精度通用矩陣乘法)運算及其矢量化版本:
void dgemm(int n, double* A, double* B, double* C) {
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j) {
double cij = C[i + j * n]; /* cij = C[i][j] /
for (int k = 0; k < n; ++k)
cij += A[i + k * n] * B[k + j * n]; /
cij += A[i][k]*B[k][j] /
C[i + j * n] = cij; /
C[i][j] = cij */
}
}

void dgemm(int n, double *A, double *B, double C) {
for (int i = 0; i < n; i += 4)
for (int j = 0; j < n; ++j) {
__m256d c0 = _mm256_load_pd(C + i + j * n); /
c0 = C[i][j] /
for (int k = 0; k < n; ++k)
c0 = _mm256_add_pd(c0, /
c0 += A[i][k]*B[k][j] /
_mm256_mul_pd(_mm256_load_pd(A + i + k * n),
_mm256_broadcast_sd(B + k + j * n)));
_mm256_store_pd(C + i + j * n, c0); /
C[i][j] = c0 */
}
}
爲了能夠使用硬件支持的向量化指令,在GCC和MSVC(Microsoft Visual C++ Compiler)上應該分別引用頭文件<x86intrin.h>和<intrin.h>。
它們在Sandy Bridge CPU上產生的彙編代碼分別如下:

  1. vmovsd (%r10),%xmm0 ; Load 1 element of C into %xmm0

  2. mov %rsi,%rcx ; register %rcx = %rsi

  3. xor %eax,%eax ; register %eax = 0

  4. vmovsd (%rcx),%xmm1 ; Load 1 element of B into %xmm1

  5. add %r9,%rcx ; register %rcx = %rcx + %r9

  6. vmulsd (%r8,%rax,8),%xmm1,%xmm1 ; Multiply %xmm1, element of A

  7. add $0x1,%rax ; register %rax = %rax + 1

  8. cmp %eax,%edi ; compare %eax to %edi

  9. vaddsd %xmm1,%xmm0,%xmm0 ; Add %xmm1, %xmm0

  10. jg 30 <dgemm+0x30> ; jump if %eax > %edi

  11. add $0x1,%r11d ; register %r11 = %r11 + 1

  12. vmovsd %xmm0,(%r10) ; Store %xmm0 into C element

  13. vmovapd (%r11),%ymm0 ; Load 4 elements of C into %ymm0

  14. mov %rbx,%rcx ; register %rcx = %rbx

  15. xor %eax,%eax ; register %eax = 0

  16. vbroadcastsd (%rax,%r8,1),%ymm1 ; Make 4 copies of B element

  17. add $0x8,%rax ; register %rax = %rax + 8

  18. vmulpd (%rcx),%ymm1,%ymm1 ; Parallel mul %ymm1,4 A elements

  19. add %r9,%rcx ; register %rcx = %rcx + %r9

  20. cmp %r10,%rax ; compare %r10 to %rax

  21. vaddpd %ymm1,%ymm0,%ymm0 ; Parallel add %ymm1, %ymm0

  22. jne 50 <dgemm+0x50> ; jump if not %r10 != %rax

  23. add $0x1,%esi ; register % esi = % esi + 1

  24. vmovapd %ymm0,(%r11) ; Store %ymm0 into 4 C elements
    高度矢量化的代碼可以讓運行速度成倍提升,有時的提升甚至接近4倍。

27、浮點數加法不滿足結合律。因爲浮點數是近似的表示,而且計算過程中也會損失精度。舉例:設c = –1.5×1038,a = 1.5×1038,b = 1.0。則:

28、對整數運算適用的並行方法不一定適用於浮點。一個原因也是因爲浮點數的精度限制,另一個原因是調度器影響。有的操作系統的調度器會視任務的多少而將分配給當前進程的核心數變動。不同的CPU核心數會對浮點的計算順序有一定的影響,於是使得結果有一點不同。
一種避免方法是將算法編寫兩個版本:串行版和並行版,並檢驗它們給出的結果相差多少,是否對結果有不能忽視的影響。其實這涉及到數值分析(numerical analysis)領域了。

29、MIPS沒有減立即數的指令,減一個正的立即數會被轉換成加它的相反數。所以MIPS指令中的立即數都是做帶符號擴展的。

30、有的人認爲只有理論數學家需要考慮浮點精度的問題。這麼想就大錯特錯了!

1994年,報紙的大標題上赫然刊登了Intel Pentium處理器的重大問題。當時,Pentium使用了一個標準的一步生成多位商的浮點除法算法:結合專門的表,使用除數和被除數的最高位來猜測商的下兩位。如果猜測錯誤,就做額外處理。
但是Intel的工程師認爲表中有5項從來不會被用到,於是將邏輯電路進行了充分優化。後來發生的一系列事件表明:這一步,確實走錯了。這個優化導致了浮點的有效位的第12位到52位可能出錯。
弗吉尼亞的University of Lynchburg數學教授Thomas Nicely在1994年9月發現了這個bug。他向Intel的技術支持部門詢問,但未獲得迴應。於是他將這個bug發佈到Internet上。這個網帖導致Intel在後來不得不召開媒體發佈會。當時Intel聲稱這個bug無傷大雅,隻影響理論數學家;使用計算機制作電子表格的用戶平均27000年纔會碰見1次這樣的錯誤。但IBM Research很快發表反駁,聲稱平均每24天這樣的錯誤就會被電子表格用戶遇到1次。在12月21日,Intel認輸了,併發布瞭如下聲明:
“我們爲最近處理被曝光的Pentium處理器出錯的方式進行誠摯的道歉。Intel Inside符號代表您的計算機中的微處理器擁有最佳的性能和質量。數千Intel員工努力工作來確保這點實現。但是沒有任何微處理器是絕對完美的。Intel相信技術上的一個極小錯誤已經開始了它的長期影響。雖然Intel堅決信任當前Pentium處理器的品質,但我們已認識到許多用戶表示了擔憂。我們會解決這些憂慮。在所有提出相關要求的用戶的計算機的生命週期內,Intel在任何時刻都可以免費把當前版本的Pentium處理器更換爲新版,新版將糾正浮點除法的錯誤。”
分析家們指出,這次召回令Intel損失了約5億美金(1994年)。Intel的工程師們的聖誕節獎金也泡湯了。這個故事引發了我們的諸多思考:如果Intel能夠儘早修復,損失會少多少?Intel接下來需要花費多少開銷來挽回失掉的名聲?處理器作爲應用極其廣泛的產品,許多工作都依賴其可靠性。CPU的廠家在修復缺陷方面應當負怎樣的責任?

在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章