1. 程式人生 > >SIMD資料並行(二)——多媒體SIMD擴充套件指令集

SIMD資料並行(二)——多媒體SIMD擴充套件指令集

 

在計算機體系中,資料並行有兩種實現路徑:MIMD(Multiple Instruction Multiple Data,多指令流多資料流)和SIMD(Single Instruction Multiple Data,單指令流多資料流)。其中MIMD的表現形式主要有多發射、多執行緒、多核心,在當代設計的以處理能力為目標驅動的處理器中,均能看到它們的身影。同時,隨著多媒體、大資料、人工智慧等應用的興起,為處理器賦予SIMD處理能力變得愈發重要,因為這些應用存在大量細粒度、同質、獨立的資料操作,而SIMD天生就適合處理這些操作。

 

SIMD結構有三種變體:向量體系結構、多媒體SIMD指令集擴充套件和圖形處理單元。本文集中圍繞多媒體SIMD指令集擴充套件進行描述。


多媒體SIMD指令集擴充套件

1. 簡介

上世紀90年代,隨著網際網路的普及,音訊、圖片、視訊等多媒體應用迅速崛起,為應對多媒體密集的計算需求,Intel於1996年率先將多媒體SIMD擴充套件指令集引入通用處理器,在其奔騰處理器上集成了 SIMD 擴充套件部件 MMX。

 

多媒體應用中通常存在大量同質、獨立的訪存和計算操作,且使用的資料型別一般都很窄(如圖形系統使用8位表示三基色的每一種顏色,使用8位表示透明度;音訊取樣位寬通常為8位或16位)。SIMD擴充套件指令集具有獨立的長位寬向量暫存器(64/128/256/512/1024......),允許將原來需要多次裝載的連續記憶體地址資料一次性裝載到向量暫存器中,並使用分裂模式將長的向量暫存器當作多個獨立的窄位寬元素,通過一條SIMD擴充套件指令實現對SIMD向量暫存器中所有資料元素的並行處理。這種執行方式非常適合於處理計算密集、資料相關性少的音視訊解碼等多媒體程式。

 

SIMD擴充套件部件僅需要在原來標量部件的基礎上覆制幾份同樣的處理單元,不需要增加太多的額外硬體,就能對多媒體等特定應用帶來顯著的效能提升,且不增加通訊以及cache和記憶體的開銷,因此即使在多核時代,SIMD擴充套件部件仍然是程式加速的重要手段之一。

 

2. 實現形式

暫存器分裂:將長向量暫存器的位元位均勻“分裂”成多個窄位寬(8/16/32/64)元素,當作獨立資料進行操作。一般的多媒體SIMD擴充套件指令集均支援定點的有符號和無符號資料操作,部分處理器支援單、雙精度的浮點操作。

 

下圖舉了一個128位寬暫存器的例子:

圖1 將128位寬的暫存器分裂成4個32位單精度浮點暫存器或16個8位寬有符號定點暫存器

 

並行車道:SIMD擴充套件部件負責訪存、計算等操作的功能單元均支援多個並行子單元的同時處理,因此一條SIMD擴充套件指令可一次性操作多個元素。

 

下圖顯示了一個功能單元以四車道並行執行的例子:

圖2 SIMD多媒體擴充套件指令執行示意圖

 

3. 主要特徵

SIMD擴充套件指令在實現機制上與普通的標量指令沒有太多差別。在SIMD擴充套件指令集上均可以實現標量指令集支援的各種操作,如存取操作、算數運算、邏輯運算、位元操作,以及常用的飽和處理、四捨五入、條件執行等功能。

 

SIMD擴充套件指令特殊之處在於並行元素的處理。除部分計運算元單元支援條件執行外,各子單元的訪存和計算操作都將嚴格同步執行,一方面迫使使用它的程式需要具備可並行化的特徵,另一方面帶來訪存上的一些問題。

 

當需要並行處理的元素個數小於一條SIMD擴充套件指令所能支援的並行操作個數,或是面臨非連續訪存的並行處理需求時,強行使用SIMD擴充套件指令可能會帶來記憶體越界的問題。早期的SIMD向量存取指令只能訪問在記憶體中連續對齊的資料,因此當程式中存在不對齊或不連續記憶體引用時,需要通過移位或者重組等輔助指令才能組成向量。在向量重組指令能力較弱或者根本不支援向量重組指令時,強制將不連續的訪存資料組成向量可能帶來大的開銷,甚至導致向量化沒有收益。

 

但隨著處理器技術的發展,目前已有處理器已經能支援非對齊的訪存操作,並添加了大量用於資料重組的指令,部分處理器還支援集散(gather-scatter)記憶體訪問,這給SIMD向量化發掘提供了堅實的基礎。

 

SIMD擴充套件指令集支援的計算功能也在不斷豐富,如今很多處理器支援的SIMD擴充套件指令達上百條之多。很多指令的設計初衷早已跨越多媒體應用處理範疇,延伸到訊號處理、科學計算等需要高效能運算能力的領域。


在各處理器上的應用情況

Intel對多媒體SIMD擴充套件指令集的應用很具代表性。1996 年,Intel 在其奔騰處理器上集成了 SIMD 擴充套件部件 MMX,其向量暫存器為64位寬。後來又相繼推出了 SSE(128位寬),AVX(256位寬),IMCI 和 AVX-512(512位寬)。

 

圖3 Intel x86處理器的多媒體SIMD擴充套件指令集演進歷程

 

其他 SIMD 擴充套件部件還包括摩托羅拉 PowerPC 處理器的 AltiVec、Sun 公司 SPARC 處理器中的 VIS、HP 公司 PA-RISC 處理器中的 MAX、DEC 公司 Alpha 處理器中的 MVI-2、MIPS公司 V 處理器中的 MDMX、AMD處理器中的3DNow!、ARM核心中的NEON、CEVA公司的VCU 等。

 

SIMD 擴充套件部件最初僅用於多媒體領域和數字訊號處理器中,後來,研究人員將SIMD 擴充套件部件應用到高效能運算機中,如,IBM 的超級計算機 BlueGene/L 和國產的神威藍光超級計算機中都整合短向量擴充套件部件。國產處理器中,龍芯、邁創以及魂芯一號都含有 SIMD 擴充套件部件。

 

圖4 帶有SIMD擴充套件部件的處理器

 

很多通用指令集也支援SIMD形式的訪存和計算操作,但它們支援的暫存器位寬有限,通過將通用暫存器(32/16位寬)分裂成16/8位的更小單元進行獨立的並行操作,如ARM指令集、TIC64X指令集,等。這種SIMD指令在實現機制上和多媒體SIMD擴充套件指令並無多少區別,不過考慮到多媒體SIMD擴充套件指令集針對特殊應用場景而“擴充套件”的用意,還是暫不把他們歸為多媒體SIMD擴充套件指令。


如何發揮SIMD擴充套件指令集的效能

多媒體SIMD擴充套件指令集必須要有軟體的支援。與標量部件相比,SIMD擴充套件部件的收益來自於向量運算和向量訪存。因此將程式儘可能向量化成SIMD指令可執行的形式,是提高SIMD整體效益的關鍵。

 

對於軟體開發者而言,有大概5種方式來利用多媒體SIMD擴充套件指令集的並行加速功能:

  1. 使用匯編語言編寫程式碼。包括在彙編原始檔中書寫彙編指令,或是在高階語言中嵌入擴充套件彙編;

  2. 使用內嵌指令函式。一般SIMD擴充套件指令集設計商都會提供各彙編指令的封裝函式,程式設計者只需呼叫這些函式就能實現彙編指令的插入,將複雜的暫存器分配的任務交給編譯器來完成;

  3. 使用C++類庫擴充套件。這種高階語言提供的類庫將偏處理器底層的資訊封裝起來,留給程式設計者更接近行為體驗的語法和函式介面;

  4. 使用庫函式。SIMD擴充套件指令集設計商往往會公開一些典型應用的庫函式,這些庫函式一般充分挖掘了指令集的特性,效率很高;

  5. 使用編譯製導語句輔助編譯器優化。現今的主流編譯器整合有自動向量化演算法,能夠在編譯期間分析程式碼的可並行性,並生成SIMD擴充套件彙編指令。不過由於編譯器能夠從程式碼中提取的資訊有限,對一些複雜的處理過程,即使存在並行開發的可能,編譯器也可能無法成功向量化。編譯製導語句可以從開發者的角度提供給編譯器更多編譯期間無法提取的資訊,提高編譯器將程式碼向量化的概率。如OpenMP和OpenACC就支援通過新增編譯指示的方式發掘程式的SIMD並行性.

 

另外,由於很多SIMD擴充套件指令一般都是在一個週期內發出,但是執行結果可能若干個週期才能有效。在做迴圈的優化中,如果能結合迴圈展開方式開發軟體流水線程式碼,可以減少由於前後指令相關造成的延遲等待時間,儘可能地挖掘處理器的峰值效率。

 

處理非整數倍並行長度的應用

在開發中,很容易碰到這樣的情況:要對一段具有N並行度的程式碼進行向量化,已知SIMD擴充套件指令一次可處理8個這樣的元素,但N在編譯時未知。這時,應該如何通過手動向量化的方法來編寫程式碼呢?

 

一種方法就是人為將N擴充套件為8的整數倍,如N = (N+7)/8,然後進行向量化。這樣編寫起來很方便,帶來的問題就是需要預留足夠的記憶體空間,否則容易引起記憶體越界。

 

這裡主要介紹一種不需要人為擴充套件記憶體邊界的方法。假設我們要處理的程式碼是:

for(i=0; i<N; i++)

{

    a[i] = b[i] + c[i];

}

 

可以這樣編寫SIMD擴充套件指令向量化的程式碼:

v_len = N/8;

for(i=0; i<v_len; i++)

{

    v_b = vload(&b[8*i]);

    v_c = vload(&c[8*i]);

    v_a = vadd(v_b, v_c);

    vstore(&a[8*i]);

}

/***收尾處理***/

v_b = vload(&b[N-8]);

v_c = vload(&c[N-8]);

v_a = vadd(v_b, v_c);

vstore(&a[N-8]);

 

這種方式通過將尾部不足向量長度的元素與前幾個元素拼湊成一組整的向量,實現收尾處理,將不會帶來記憶體越界的問題。不過這種方式也有不足之處,就是程式碼體積將增大一倍左右,且不能處理N小於向量長度的情況。

 


參考資料

【1】高偉, 趙榮彩, 韓林, 龐建民, 丁銳. SIMD自動向量化編譯優化概述[J].軟體學報,2015, 26(6): 1265-1284.

【2】John L. Hennessy,David A. Patterson . 計算機體系結構:量化研究方法:第5版[M].北京:人民郵電出版社,2013. (原名《Computer Architecture:A Quantitative Approach》)

【3】David A. Patterson,John L. Hennessy . 計算機組成與設計:硬體/軟體介面:第5版[M].北京:機械工業出版社,2015. (原名《Computer Organization and Design:The Hardware/Software Interface》)

 

 

·END·

 


 

你可能還感興趣:

SIMD資料並行(一)——向量體系結構

計算機系統中與儲存有關的那些事

現代處理器與程式碼效能優化

《大話處理器》筆記摘抄及一點延伸

翻譯 | 指令排程基礎

翻譯 | 淺析演算法複雜度分析

關於程式碼執行效率優化的一次內部分享

 

 

想進一步跟蹤本部落格動態,歡迎關注我的個人微信訂閱號:訊號君

 

訊號君:尋求簡單之道

技術成長 | 讀書筆記 | 認知升級

關注一下再走吧~