以x86體系為代表的CPU已經占有了桌面和服務器處理器的絕大部分份額,而且這個趨勢還在不斷增強。CPU具有兼容性強、易編程、應用資源豐富、價格低廉的優勢,但是在某些領域,CPU存在天然的缺陷,以FPGA、GPU為代表的硬件可以克服CPU的缺陷,因此也擁有自己的市場。
1.1 圖解各類型芯片
從設計軟件進行計算任務的軟件工程人員的角度,可以將芯片分為CPU、GPU、FPGA和ASIC等類型。
對處理器芯片的特性和應用,理論上是軟件人員具有最大發言權。但每一類芯片的使用和理解都不是一件簡單的事情,以CPU為例:即使從事CPU環境的編程設計多年,也很難談得上深入理解了CPU的設計思想。能深入各種芯片編程的軟件人員更是鳳毛麟角,更別談進行分析和比較。另外一個問題是軟件和硬件設計已分離多年,軟件設計人員,很難深入理解芯片的設計思路,即使操作系統的設計人員也一樣。而芯片的設計廠商由于利益相關,往往只宣揚各自的優點,回避缺陷,在測試對比中選擇有利的測試條件,產生對己有利的測試數據。測試數據的真真假假,更加混淆了技術人員的視聽。
在對各種芯片比較和研究的過程中,我們認為不應該沉湎于具體芯片的架構和設計思路,而應該關注芯片的實際應用。有兩個原因支持我們的思路。一個原因是芯片的架構非常繁復,熟悉各種芯片幾乎是不可能的任務。另一個更重要的原因是技術的價值在于應用。不管何種芯片設計或者架構,最終決定芯片價值的是實際的應用。從應用的角度出發,應按照易用性和經濟性兩個維度考察芯片。
易用性指用芯片進行編程的難度以及相關編程資源的獲取難度。這個指標技術人員雖然不怎么關心,但其實對芯片發展有重大,甚至是絕對的重要性。例如在FPGA的編程實踐中,相關的編程資源非常難以獲得,即使獲得也往往是代價巨大。比如常見的JPEG圖片,相關的FPGA編解碼庫往往需要付出數萬美元的成本,這和CPU領域大量的開源庫完全不能相提并論。價格的昂貴還帶來了測試和驗證的繁雜。提供庫資源的廠商往往需要曠日持久的溝通和談判以及簽署協議才能進行驗證工作,這在很多研發項目運作中幾乎是不可承受的。
經濟性指提供相同性能情況下的芯片成本。芯片往往型號眾多,比如FPGA芯片,既有上千美元甚至幾千美元售價的高端型號,也有幾美元計價的低端型號。脫離芯片成本談論性能沒有意義。需要指出的是,成本是綜合的運營成本,而非單獨的芯片購買成本。比如某款芯片如果性能等于CPU十倍,那么它不僅僅是頂替了十顆CPU,而是頂替了十臺服務器的采購成本以及十臺服務器的運營成本,考慮到實際的運營成本往往大于采購成本,后者可能更具有重要性。
1.2 芯片的分類
對常用的處理器芯片進行分類,有一個明顯的特點:CPU&GPU需要軟件支持,而FPGA&ASIC則是軟硬件一體的架構,軟件就是硬件。這個特點是處理器芯片中最重要的一個特征。
上圖可以從兩個角度來說明:從ASIC->CPU的方向,沿著這個方向芯片的易用性越來越強,CPU&GPU的編程需要編譯系統的支持,編譯系統的作用是把高級軟件語言翻譯成機器可以識別的指令(也叫機器語言)。高級語言帶來了極大的便利性和易用性,因此用CPU&GPU實現同等功能的軟件開發周期要遠低于FPGA&ASIC芯片。沿著CPU->ASIC的方向,芯片中晶體管的效率越來越高。因為FPGA&ASIC等芯片實現的算法直接用晶體管門電路實現,比起指令系統,算法直接建筑在物理結構之上,沒有中間層次,因此晶體管的效率最高。
本質上軟件的操作對象是指令,而CPU&GPU則扮演高速執行指令的角色。指令的存在將程序執行變成了軟件和硬件兩部分,指令的存在也決定了各種處理器芯片的一些完全不同的特點以及各自的優劣勢。
FPGA&ASIC等芯片的功能是固定的,它們實現的算法直接用門電路實現,因此FPGA&ASIC編程就是用門電路實現算法的過程,軟件完成意味著門電路的組織形式已經確定了,從這個意義上,FPGA&ASIC的軟件就是硬件,軟件就決定了硬件的組織形式。軟硬件一體化的特點決定了FPGA&ASIC設計中極端重要的資源利用率特征。利用率指用門電路實現算法的過程中,算法對處理器芯片所擁有的門電路資源的占用情況。如果算法比較龐大,可能出現門電路資源不夠用或者雖然電路資源夠用,但實際布線困難無法進行的情況。
存在指令系統的處理器芯片CPU&GPU不存在利用率的情況。它們執行指令的過程是不斷從存儲器讀入指令,然后由執行器執行。由于存儲器相對于每條指令所占用的空間幾乎是無限的,即使算法再龐大也不存在存儲器空間不夠,無法把算法讀入的情況。而且計算機系統還可以外掛硬盤等擴展存儲,通過把暫時不執行的算法切換到硬盤保存更增加了指令存儲的空間。
處理器芯片各自長期發展的過程中,形成了一些使用和市場上鮮明的特點。CPU&GPU領域存在大量的開源軟件和應用軟件,任何新的技術首先會用CPU實現算法,因此CPU編程的資源豐富而且容易獲得,開發成本低而開發周期,而FPGA&ASIC編程需要的資源通常很難獲得,這些資源往往以IP(intellectual property)的方式授予和收費,授予的周期往往很長而且需要簽署法律協議,而費用也很昂貴。導致FPGA&ASIC的開發成本高而且周期很長。
1.3 CPU架構和編程設計
無論是x86體系為代表的繁雜指令系統(CISC)CPU還是精簡指令系統(RISC)CPU,其核心都是執行一套指令系統。x86體系的CPU不斷更新換代,不斷提升主頻,采用更先進的工藝和新架構,目的就是為了更高性能地執行x86指令。因為X86系列的CPU應用廣泛,已經成為事實上的標準,本文所指的CPU特指X86系列的CPU。
從CPU內部結構觀察,大致可分為控制器和執行器,再加上存儲管理部件MMU以及總線接口部件。控制器不斷從存儲器取出指令,進行指令譯碼,執行器從譯碼完成的指令隊列中取出譯碼指令執行。各個功能部件既能獨立工作,又能與其他部件配合工作,下圖給出了CPU各個部件之間的指令操作流水圖。
指令系統是計算機系統發展中的巨大進步。借助指令系統,高級語言的出現成為可能,大大方便了計算機的應用。但是事情的另一面是使用指令系統后,所有的計算任務都要翻譯為指令,執行一個簡單的計算任務可能就需要多條指令完成。從晶體管的角度來看,簡單的計算任務可能就需要眾多的晶體管共同參與。為提升性能,采用指令系統的CPU,其性能設計出發點是增強指令執行的效率。
以前CPU的架構設計一直圍繞如何增強指令執行的效率,為此采取的措施是不斷提升主頻、加多流水線(奔騰首次應用了雙路流水,而現在的CPU往往擁有20以上的流水數目
)以及增加CPU的cache提升取指令的效率(早期奔騰芯片擁有幾十K的緩存,而至強E5的三級緩存超過10MB,甚至可達到30MB)。近幾年,CPU的架構更加重視多核的應用,期望通過多核實現更高的性能。
CPU設計出發點是增強指令的運行性能,因此CPU的核心功能強大,占用的晶體管資源龐大,具有很高的運行效率,因此CPU的多核不可能做到非常多。目前頂級的X86 CPU具有十多個核心,而GPU已經達到幾千個核心。
對編程設計來說,如果線程完全獨立的執行計算任務,線程間數據不存在共享和競爭關系,那么并行效率可以達到線性效果。不過現實中的編程,有很大一類是單任務的并行化,即將一個繁雜的任務通過多核并行執行來加速,那么就面臨兩個困難:一個是將任務并行化之后面臨多線程之間的切換代價。因為CPU核心功能強大,因此操作系統切換線程時需要CPU內部大量的狀態寄存器置位,所以線程之間切換是代價很大的操作(實測中,線程切換大概需要幾十微秒),如果計算任務的執行時間小于這個數字,那么多線程執行對性能提升可能并無收益,甚至可能效率反而下降。
另一個問題是任務執行中數據的依賴關系。如果計算任務中某部分必須利用前面部分的計算結果,即存在數據依賴性,那么就必須等前面部分計算完成才能執行后面的計算,而不可能并行計算。數據依賴是計算中經常遇到的場景,編程設計需要調整代碼結構盡量減少相關性提升并行性。
1.4 GPU的架構和編程設計
GPU(圖形處理器)這個概念最早是顯卡廠商Nvidia公司提出來,如它的名字所象征的意義,主要是為圖形處理而設計。圖形處理計算的特征表現為高密度的計算而計算需要的數據之間較少存在相關性。下圖展示了GPU和CPU設計的不同之處。
圖 CPU架構和GPU對比
如圖所示,GPU的設計出發點在于GPU更適用于計算強度高、多并行的計算。因此,GPU把晶體管更多用于計算單元,而不像CPU用于數據Cache和流程控制器。這樣的設計是因為并行計算時每個數據單元執行相同程序,不需要繁瑣的流程控制而更需要高計算能力,因此也不需要大的cache容量。
圖 GPU內部結構
如上圖所示,GPU的架構圍繞流處理器(SMX)陣列構建的。 流處理器能同時并發執行上百線程。指令流水線化以利用單線程內的指令級并行,與CPU核不同,GPU指令順序發射,沒有分支預測和猜測執行。
流處理器以32個為一組創建、管理、調度和執行并行線程,這32個線程組稱為束(warps)。束內包含的不同線程從同一程序地址開始,但它們有自己的指令地址計數器和寄存器狀態,因此可自由分支和獨立執行。
束每次執行一個相同的指令,所以如果束內所有32個線程在同一條路徑上執行的話,會達到最高效率。如果由于數據依賴條件分支導致束分岔,束會順序執行每個分支路徑,而禁用不在此路徑上的線程,直到所有路徑完成,線程重新匯合到同一執行路徑。分支岔開只會在同一束內發生,不同的束獨立執行不管它們是執行相同或不同的代碼路徑。
需要注意的是,GPU的線程概念和CPU的線程概念不同,CPU有虛存概念,線程具有自己的線程空間和頁表項,還包括CPU的諸多狀態寄存器。因此CPU的線程功能更強大,切換線程的代價也更高,而GPU的線程可以被看做一些計算指令構成的計算塊,它們的調度、執行和切換要簡單的多。從一個線程的執行上下文切換到另一個線程的執行上下文沒有消耗,線程之間的切換是硬件切換,消耗的時間幾乎可以不考慮。
計算任務中可能存在串行的部分。串行指不可并行、必須順序執行的計算部分。這種串行部分極大的降低了任務的并行度和計算性能。在后續的實踐例子中,就遇到了這樣的串行部分。
單獨的GPU缺乏必要的環境,沒有外部設備和操作系統的支持,不能和網絡或者本地硬盤交換數據,因此在實際應用中,GPU總是要和CPU搭配使用,共同構成編程的環境,這種編程稱為異構編程。異構編程不可避免CPU管理的內存和GPU管理的內存之間的數據交互,數據交互的效率極大程度上將影響GPU的運行效率。
1.5 FPGA的架構和編程設計
FPGA不采用指令和軟件,是軟硬件合一的器件。對FPGA進行編程要使用硬件描述語言,硬件描述語言描述的邏輯可以直接被編譯為晶體管電路的組合。所以FPGA實際上直接用晶體管電路實現用戶的算法,沒有通過指令系統的翻譯。
FPGA的英文縮寫名翻譯過來,全稱是現場可編程邏輯門陣列,這個名稱已經揭示了FPGA的功能,它就是一堆邏輯門電路的組合,可以編程,還可以重復編程。不能重復編程的FPGA也有,主要是基于反熔絲技術,主要用于軍事用途。下圖展示了可重復編程FPGA的內部原理圖。
圖 FPGA內部結構圖
FPGA內部可以分為可配置邏輯模塊CLB、輸入輸出模塊IOB和內部連線等三個部分。IOB是FPGA輸入輸出的接口,提供芯片和外界電路的連接,完成不同電氣特性對輸入輸出信號的驅動和匹配。
CLB是FPGA的基本邏輯單元。CLB的實際數量根據芯片種類的不同而不同,以xilinx公司生產的早期FPGA Virtex5系列為例,每個CLB包含兩個silce。每個slice內部包含4個查找表(LUT)、4個觸發器和多路開關等資源。
數字邏輯電路從原理上,是通過時序部件和組合邏輯來完成一系列的功能,而FPGA通過芯片內部眾多的CLB單元提供了眾多的組合邏輯和時序邏輯。因此通過配置CLB就可以實現各種不同的功能。
本文關注的重點不是FPGA的硬件原理,也不是FPGA邏輯設計的技巧和語法,而是從并行計算的角度分析多種芯片和CPU程序設計的特點。
1.5.1 FPGA編程和CPU編程的特點
CPU編程和FPGA編程最大的不同之處是前者是軟件模式,而后者是硬件模式。軟件模式意味著代碼之間是串行模式,代碼之間有嚴格的執行順序(不考慮指令亂序執行的影響),而硬件模式則意味這代碼之間是并行模式,每一條語句,經過編譯(在硬件領域,編譯被稱為綜合)之后就是一個真實的邏輯電路。通過一個具體的例子可以更準確觀察到軟件模式和硬件模式的區別:
對于上面軟件模式的代碼,這兩條語句之間是串行執行的(不考慮指令流水),經過編譯之后,CPU先執行第一條語句,完成a變量的賦值運算,然后執行第二條語句,執行d變量的賦值運算。
如果后續沒有對變量a和d的再次賦值,那么變量將始終保持當前的賦值不變。
上面語句是FPGA硬件的賦值語句。和CPU軟件模式的執行方式不同,上面語句經過綜合后,將形成兩個邏輯電路,一個電路的輸入是b和c,輸出是a,而另一個電路的輸入是e和f,輸出是d。這兩者之間是完全獨立的,彼此并行而沒有任何的順序關系。
另外一點和CPU執行模式不同的是,只要輸入端b、c或者e、f有任何的變化,那么輸出端a或者d也立即變化,不需要再次的賦值。從硬件角度很容易理解這一點,因為FPGA的硬件描述語句被生成為邏輯電路,它是實實在在的存在并且一直執行,而CPU的指令被執行之后,除非被加載到執行單元再次執行,否則不會自動再執行。
使用FPGA編程,最大的難點在于將原有的串行思路轉變為并行思路。由于人腦的思維模式更接近串行模式,所以用并行模式實現算法和功能的時候,通常困難比串行模式來得大。除此之外,還有如下特性不同:
FPGA硬件具有資源占用率的概念。FPGA編程最終要用邏輯電路實現,因此復雜的算法需要耗用更多的邏輯電路,如果使用的邏輯電路超過芯片的資源是無法實現的。這個特性和CPU完全不同,CPU的程序存儲在外部存儲中,執行時從外部存儲載入內存執行。內存和外部存儲的容量遠遠超過算法需要的存儲量,基本不可能出現資源不夠用的情況。
FPGA通常運行的時鐘頻率遠小于CPU的時鐘頻率。對FPGA編程的過程,實際是將芯片內部邏輯電路連接起來實現算法和功能的過程。從FPGA的結構可以發現,FPGA是固定排列的門電路陣列,邏輯電路固定的排列方式決定了編程過程有大量的冗余電路沒有利用,走線也不能夠充分的精簡。因此當前主流FPGA芯片編程通常運行時鐘頻率為200Mhz~300Mhz,而CPU的運行頻率已超過Ghz的關口,現代主流的X86 CPU的時鐘頻率甚至超過3Ghz。
1.6 ASIC的架構
ASIC和FPGA類似,都是用門電路資源實現固定的算法,不同之處是FPGA是固定排列的門電路陣列,固定的排列方式決定了編程過程有大量的冗余電路沒有充分利用,造成門電路資源的浪費,而ASIC是經過專門優化之后的門電路布局,相比較FPGA精簡的多。根據廠商的提供的資料,實現同等功能FPGA所需的門電路數目可能比ASIC高10倍。
從使用方式來比較,FPGA可以重復編程,而ASIC一次編程后無法更改。由于FPGA可重復編程的靈活性,設計ASIC芯片多數時候會先用FPGA實現邏輯功能,然后基于FPGA的結果進行優化和整合,得到最終的ASIC芯片需要的電路設計。
在真正的實踐中,并沒有進行ASIC的設計和實際的制造,這是因為ASIC的設計和制造非常昂貴,和合作廠商溝通的結果大概需要百萬美元的投入。ASIC芯片的昂貴決定了它不可能在小規模應用的場合出現,而必須是大規模(達到百萬以上的量級)的應用場景才可以分攤ASIC的昂貴投入取得經濟上的合理性。這種特性導致一個有趣的結果,一種計算算法初步啟動時常常使用FPGA作為硬件載體,利用FPGA承載算法,隨著規模擴張,就有商業性的ASIC芯片出現。一旦規模達到經濟上的合理,ASIC芯片的成本就遠小于FPGA的成本并取代FPGA的地位。
一個事實就是PCIE SSD的應用。早期的PCIE SSD都是使用FPGA作為內部算法的硬件載體,而2014年以來隨著PCIE SSD應用的規模化,已經有ASIC芯片出現并在一些PCIE SSD的產品中應用。后續ASIC芯片很可能替代FPGA在PCIE SSD的應用。
1.7 計算實踐和對比
為比較各種芯片的計算性能,以jpeg格式的圖片進行解碼和重新編碼的計算為例子。下圖展示了jpeg圖像處理的算法過程。
?
jpeg圖像的壓縮過程:所有的圖像數據首先要進行色彩空間的轉換,從RGB色彩空間轉換為YUV色彩空間。然后將圖像分割為8像素X8像素的圖像塊,對每個圖像塊進行離散余弦變換(DCT),然后對變換后的數據量化得到量化值,最后對量化值進行墑編碼,得到壓縮后的圖像數據。
而jpeg圖像的解碼過程則是編碼過程的逆向過程,首先對壓縮的圖像數據進行墑解碼,得到量化之后的數據,然后執行反量化獲得量化之前、離散余弦變換之后的數據,最后進行反離散余弦變換,獲得原始圖像數據。
1.7.1 CPU的實踐和性能
對比測試的項目以每秒鐘jpeg圖片解碼然后重新編碼的性能為準,對單張圖片循環重復計算,單位為張數。CPU計算平臺采用的處理器為至強E3-1270,CPU計算平臺使用的轉碼軟件是imagemagic-6.8.6,多進程并發循環執行。下表給出CPU的性能數據。
1.7.2 GPU的實踐和性能
利用GPU進行圖片解碼和再編碼時,首先遇到了順序執行的問題。JPEG解碼里面的墑解碼器使用的是霍夫曼解碼。霍夫曼解碼在解碼圖像數據時候,依次處理一個個圖像塊,塊之間沒有分割標志,因此存在數據依賴關系,必須把前面圖像塊的數據解碼完成,才能處理下一個圖像塊。這種必須順序執行的計算部分GPU運行效率非常低,如果霍夫曼解碼在GPU里面完成,整體效率甚至不如CPU。我們和Nvidia公司的軟件團隊討論了這個問題,最后確定的方案是將霍夫曼解碼部分由CPU完成。使用GPU的異構編程應當被看做是CPU的計算輔助單元,共同和CPU完成計算任務,由于架構和配套資源的特點,很難把GPU當作完整的解決方案。
第二個重要的問題是內存的分配和管理。以操作系統的角度來看,異構編程其實是對外部設備的編程,軟件代碼可以分成兩部分,一部分在CPU上面執行,另一部分在GPU上執行。GPU的內存分配其實是對設備內存的分配,這種分配操作運行性能很低,嚴重影響整體計算的效率。在實際測試的代碼中,將所有的設備內存分配都在開始時候一次完成,避免實際使用時的內存分配。數據從CPU內存復制到GPU的設備內存后,才能進行高性能計算。因此需要減少內存的復制時間,盡量使復制過程和GPU的計算疊加起來,形成流水式的操作。
使用GPU的異構編程,需要時刻關注GPU的利用率指標。這個指標代表GPU的繁忙程度,如果利用率很低,說明沒有充分利用GPU內部的流處理器,存在流處理器較多時間空閑的情況,就需要調整和優化代碼結構。一般說來,GPU的利用率應該大于80%,這是才比較充分的利用了GPU的性能。
最終的測試是和Nvidia公司的軟件團隊共同完成,實驗性能得到了Nvdia公司的認同。下表給出GPU對JPEG圖像再編碼的性能數據。
1.7.3 FPGA的實踐和性能
FPGA是一個單獨的芯片,為了能在服務器環境使用FPGA芯片,需要設計一張FPGA計算卡,該卡使用PCIE接口和主機連接,卡上安裝一片xilinx v7-690T的芯片,同時具有2GB的DDR內存。FPGA編程沒有操作系統的支持和輔助,因此FPGA內部的資源必須用戶自己管理,比如FPGA訪問DDR內存的數據,必須通過硬件邏輯來實現,FPGA芯片和外部設備之間的數據交換也必須全部由使用者設計硬件邏輯實現功能,比如通過PCIE接口將主機內存復制到FPGA卡內的DDR內存等。
而且FPGA軟件設計的生態環境和CPU的環境不同,通常FPGA設計沒有大量可用的開源軟件。即使有一些公司提供商業的解決方案,費用一般也很昂貴。實際上在硬件設計領域,一般都流行商業授權的模式。商業公司通常將開發的硬件邏輯打包為IP(intelligence property),有購買IP意向的使用者和商業公司談判使用模式和費用,談判完成前一般不提供試用版本。
為了解決FPGA編程困難、生產效率低的問題,我們創建了一個FPGA編程框架,利用框架提供用戶需要的控制和數據,用戶只需要聚焦于算法和功能,而不必關心底層功能的實現。FPGA編程框架彌補了FPGA 沒有操作系統支持的弱點,其實質上類似一個小型的操作系統,實現對資源的管理和使用。如下圖所示:
上圖綠色部分展示了FPGA編程框架的構成,當前編程框架包含對PCIE接口協議的封裝、DDR內存的存取和多路仲裁、DMA功能、郵箱消息接口等。編程框架里面包含了下列的重要部件:
PCI設備配置空間:設置PCIE設備配置空間的信息,包括PCI設備ID,制造廠商和設備IO端口以及IO mem資源
DMA:啟動DMA功能,從主機內存物理地址復制數據到FPGA卡DDR內存的指定地址,或者從FPGA卡DDR內存的指定地址復制到主機內存物理地址。
DDR:從FPGA卡DDR內存指定地址讀數據,每次讀出64bit數據。或者往FPGA卡DDR內存指定地址寫數據,每次寫入64bit數據。
郵箱:郵箱提供主機和FPGA芯片之間的消息接口。
中斷資源:中斷接口管理和控制,通過接口可以觸發一個系統中斷。
在FPGA程序實現時最重要的問題就是資源利用率。FPGA和CPU不同,如果資源沒有限制,FPGA理論上可以部署足夠多的計算單元,性能可以達到無限。在圖片轉碼的實踐中,基于690T的芯片,實際上部署了30路的編解碼核心,這時資源利用率達到70%。下表給出FPGA的性能數據。
1.8 結論和綜述
從整個實踐的過程理解,FPGA&GPU芯片的使用是很復雜的過程。性能和性價比是否具有優勢需要針對業務的計算類型設計程序和優化,并進行實際的驗證得出結論。希望簡單和一致性的結論沒有可能,而廠商的結論多半并不可信,和實際驗證的數據差別巨大。現實是硬件、環境和算法資源都很缺乏,這可能是異構編程應用不廣泛和困難的原因。脫離了CPU的支持,使用GPU&FPGA等芯片的異構編程困難太多,如果像CPU集成GPU一樣,同樣在CPU內部集成FPGA功能,方便用戶的選擇和使用,將大大推動各種場景的實際應用。
評論
查看更多