本文來(lái)自 XILINX 產(chǎn)品應(yīng)用工程師 Yang Chen
在開(kāi)發(fā)一個(gè)加速程序的之前,有一個(gè)很重要的步驟:正確設(shè)計(jì)程序架構(gòu)。開(kāi)發(fā)人員需要明確軟件應(yīng)用程序中哪一部分是需要硬件加速的,并且它多少的并行量,以保證硬件加速器件(FPGA)能完美發(fā)揮其作用。
本文將分為5個(gè)步驟來(lái)介紹:
1. 基準(zhǔn)和建立目標(biāo)
2. 確定加速部分
3. 確定FPGA硬件加速并行量
4. 確定軟件部分并行量
5. 微調(diào)架構(gòu)細(xì)節(jié)。
1. 基準(zhǔn)和建立目標(biāo)
首先要測(cè)試應(yīng)用程序的運(yùn)行時(shí)間和吞吐量,來(lái)確定當(dāng)前應(yīng)用程序在現(xiàn)有平臺(tái)的的基準(zhǔn)性能。這些數(shù)據(jù)應(yīng)涵蓋整個(gè)應(yīng)用程序(起始到結(jié)束)的性能和各個(gè)主要函數(shù)的性能。通常使用valgrind,callgrind和GNU gprof這些測(cè)試軟件來(lái)獲得應(yīng)用程序的性能數(shù)據(jù),它們會(huì)顯示應(yīng)用程序中所有的函數(shù)數(shù)量以及各個(gè)函數(shù)的執(zhí)行時(shí)間。通過(guò)這些數(shù)據(jù),我們可以找到耗時(shí)最長(zhǎng)的部分,然后放到FPGA上進(jìn)行加速。
評(píng)估運(yùn)行時(shí)間
測(cè)試運(yùn)行時(shí)間是軟件開(kāi)發(fā)的基本流程,可以使用一些常用的測(cè)試軟件,或者插入計(jì)時(shí)器和性能計(jì)數(shù)器來(lái)完成此項(xiàng)操作。以gprof為例,可以得到類似如下圖結(jié)果,具體gprof的用法可以參考:http://sourceware.org/binutils/docs-2.17/gprof/index.html
評(píng)估吞吐量
這里的吞吐量是指數(shù)據(jù)被處理的速率。對(duì)于計(jì)算給定函數(shù)的吞吐量,具體公式為函數(shù)處理的數(shù)據(jù)除以函數(shù)處理的時(shí)間,如下:
TSW= max (VINPUT, VOUTPUT) / Running Time
如果是處理固定的數(shù)據(jù)量,只要簡(jiǎn)單的檢查代碼就能知道吞吐量的大小。但在一些情況下,數(shù)據(jù)是可變的,那么插入計(jì)數(shù)器來(lái)測(cè)量吞吐量的大小是比較實(shí)用的。
確定最大可實(shí)現(xiàn)的吞吐量
在大多數(shù)加速系統(tǒng)中,最大可實(shí)現(xiàn)吞吐量受PCIe總線的限制。PCIe總線受很多因素的影響,例如母板,驅(qū)動(dòng),目標(biāo)板卡和發(fā)送數(shù)據(jù)大小等等。運(yùn)行DMA測(cè)試能夠測(cè)試PCIe發(fā)送的有效吞吐量,從而確定加速性能潛力的上限。在安裝Alveo板卡后,我們可以使用xbutil dmatest命令來(lái)測(cè)試板卡的PCIe性能。
建立總體加速目標(biāo)
在開(kāi)發(fā)過(guò)程中盡早確定加速目標(biāo)是非常有必要的,基于基準(zhǔn)性能的加速目標(biāo)會(huì)決定分析和決策的走向。加速目標(biāo)可以是硬性的也可以是軟性的。例如,實(shí)時(shí)視頻應(yīng)用程序有每秒處理60幀的嚴(yán)格硬性目標(biāo),而數(shù)據(jù)科學(xué)應(yīng)用程序的軟性目標(biāo)是比其他可代替實(shí)現(xiàn)方法快10倍。所以無(wú)論哪種方式,領(lǐng)域?qū)I(yè)知識(shí)對(duì)于設(shè)置可實(shí)現(xiàn)的加速目標(biāo)都很重要。
2. 確定加速部分
評(píng)估基準(zhǔn)性能后,下一步就是確定哪一個(gè)函數(shù)需要在FPGA上加速。當(dāng)選擇哪個(gè)函數(shù)用于加速時(shí),有兩個(gè)方面需要考慮到:
性能瓶頸:應(yīng)用程序中有哪些函數(shù)需要著重關(guān)注
加速潛力:這些函數(shù)是否有加速的潛力
確定性能瓶頸
在一個(gè)純粹的順序進(jìn)行的應(yīng)用程序中,可以通過(guò)解析報(bào)告很容易甄別到性能瓶頸。然而,大多數(shù)現(xiàn)實(shí)中的應(yīng)用程序都是多進(jìn)程,因此在尋找性能瓶頸的時(shí)候考慮并行性很重要。一個(gè)很簡(jiǎn)單的例子:
如上圖中是一個(gè)應(yīng)用程序中兩條并行的路徑,長(zhǎng)度表示它們運(yùn)行消耗時(shí)間。從這里我們看出,僅僅加速A,B進(jìn)程的某一個(gè)并不能提高應(yīng)用程序的整體性能。即使你將A2加速100倍,該應(yīng)用程序的性能還是被A1和B進(jìn)程鉗制。所以考慮加速對(duì)象時(shí),要考慮整個(gè)應(yīng)用程序的性能,而不是單個(gè)函數(shù)的性能。
確定加速潛力
作為軟件程序中的瓶頸函數(shù)不一定具有加速的潛力,通常需要進(jìn)行詳細(xì)分析才能準(zhǔn)確判斷給定函數(shù)的實(shí)際加速潛力。但是,有時(shí)候一些簡(jiǎn)單的指導(dǎo)方法也能確定一個(gè)函數(shù)是否有加速潛力:
1. 選擇運(yùn)算復(fù)雜度比較大的,相比于順序計(jì)算來(lái)說(shuō),它可以在FPGA上可以使用并行,流水線來(lái)提高效率。
2. 相對(duì)于輸入輸出來(lái)說(shuō)的,選擇運(yùn)算強(qiáng)度比較大的,因?yàn)檫@樣數(shù)據(jù)搬移時(shí)間開(kāi)銷占用整個(gè)加速時(shí)間比率來(lái)說(shuō)會(huì)低一些。
3. 選擇那些能夠數(shù)據(jù)重用,對(duì)內(nèi)存訪問(wèn)比較少的,因?yàn)檫@可以是數(shù)據(jù)更容易在加速器中緩存,減少對(duì)全局內(nèi)存的訪問(wèn)。
4. 對(duì)比函數(shù)吞吐量和FPGA吞吐量的比值,以確定最大可加速的倍數(shù)。
3. 確定FPGA硬件加速并行量
在前面的步驟中確定哪個(gè)函數(shù)用于加速之后,接下來(lái)就要確定使用多少的并行量來(lái)達(dá)到這一目標(biāo)。內(nèi)核(kernel)的并行性可以分為大致兩種,一種是流水線形式,即是輸入和處理數(shù)據(jù)同時(shí)進(jìn)行;另一種是同時(shí)處理多個(gè)任務(wù),即是擁有多個(gè)輸入,多個(gè)任務(wù)并行處理。
評(píng)估硬件吞吐量(非并行)
沒(méi)有進(jìn)行并行化的內(nèi)核(kernel)吞吐量可以近似為:
THW = Frequency(頻率) / Computational Intensity(計(jì)算強(qiáng)度) = Frequency * max(VINPUT,VOUTPUT) / VOPS
頻率就是kernel的時(shí)鐘頻率。這個(gè)值是由特定的平臺(tái)決定,比如,Alveo U200的最大kernel時(shí)鐘是300Mhz。VINPUT,VOUTPUT是輸入輸出數(shù)據(jù),VOPS是操作總數(shù)。由此可以看出,大量的操作數(shù)和少量的數(shù)據(jù)的函數(shù)更適合加速。
確定所需的并行量
經(jīng)過(guò)上述計(jì)算后,可以估算出初始的HW/SW性能比:
Speed-up = THW/TSW = Fmax * Running Time /VOPS
沒(méi)有使用并行運(yùn)算,則初始的加速(speed-up)通常會(huì)小于1。
接下來(lái)就要計(jì)算多少并行量可以滿足性能目標(biāo):
Parallelism Needed = TGoal / THW = TGoal * Vops / (Fmax * max(VINPUT, VOUTPUT))
并行方式可以通過(guò)多種方式實(shí)現(xiàn):拓展數(shù)據(jù)路徑,使用多個(gè)計(jì)算引擎,使用多個(gè)kernel實(shí)例,開(kāi)發(fā)人員應(yīng)根據(jù)他們的需求和應(yīng)用程序的特點(diǎn)確定最佳組合方式。
確定數(shù)據(jù)路徑應(yīng)并行處理多少個(gè)樣本
一種可能性是通過(guò)創(chuàng)建更寬的數(shù)據(jù)路徑(數(shù)據(jù)的輸入和輸出的過(guò)程)然后并行處理更多數(shù)據(jù)以便加快計(jì)算速度。有些算法很適合這種方法,而有些則不適用。重要的是要了解這個(gè)算法的本質(zhì),確定這種方法是否可運(yùn)用。如果可運(yùn)用,那么并行處理多少數(shù)據(jù)才能滿足性能目標(biāo)也是需要考慮的。
運(yùn)用更寬的數(shù)據(jù)路徑、并行處理更多數(shù)據(jù)這些方法,本質(zhì)是通過(guò)減少加速函數(shù)等待時(shí)間(運(yùn)行時(shí)間)來(lái)實(shí)現(xiàn)提高性能的。
確定在FPGA中可以(應(yīng)該)實(shí)例化多少個(gè)kernel
如果數(shù)據(jù)路徑無(wú)法并行化(或不夠充分),則請(qǐng)考慮添加更多kernel實(shí)例,這通常被稱為使用多個(gè)計(jì)算單元(CU)。添加更多的kernel實(shí)例的本質(zhì)是允許加速函數(shù)更多的調(diào)用,從而提高應(yīng)用程序的性能,如下所示。多個(gè)數(shù)據(jù)集由不同的實(shí)例并發(fā)處理。只要主機(jī)應(yīng)用程序可以保持kernel繁忙,應(yīng)用程序的性能就會(huì)隨著實(shí)例數(shù)的增加而線性增加。
在Vitis中,很容易通過(guò)添加額外的kernel實(shí)例來(lái)提高加速性能,不需要過(guò)多的代碼調(diào)整。在這一點(diǎn)上,開(kāi)發(fā)人員應(yīng)該充分了解硬件中滿足性能目標(biāo)所需的并行度,結(jié)合數(shù)據(jù)路徑寬度和kernel實(shí)例來(lái)達(dá)到預(yù)期的目標(biāo)。
4. 確定軟件部分并行量
雖然FPGA及其kernel旨在提供潛在的并行性,但是必須對(duì)軟件應(yīng)用程序進(jìn)行設(shè)計(jì)以便利用這種潛在的并行性。
軟件應(yīng)用程序中的并行性主要是以下幾方面:
?最大限度地減少空閑時(shí)間,并在kernel運(yùn)行時(shí)執(zhí)行其他任務(wù)。
?保持kernel處于活動(dòng)狀態(tài),以便盡早并經(jīng)常執(zhí)行新的計(jì)算。
?優(yōu)化與FPGA之間的數(shù)據(jù)傳輸。
如上圖所示,host程序總是處于繁忙狀態(tài)并且計(jì)劃執(zhí)行下一步的操作,而kernel端是處理當(dāng)前的任務(wù)。所以,host程序必須統(tǒng)籌與kernel的數(shù)據(jù)傳輸,并且向kernel端發(fā)送請(qǐng)求,不然再多的kernel也是沒(méi)有效果的。
在kernel運(yùn)行時(shí)最大程度地減少CPU空閑時(shí)間
FPGA加速是將某些計(jì)算從主機(jī)處理器轉(zhuǎn)移到FPGA的kernel中,在純順序模型中,應(yīng)用程序?qū)㈤e置地等待結(jié)果,準(zhǔn)備并回復(fù)處理。設(shè)計(jì)軟件應(yīng)用程序以避免此類空閑周期,首先是確定不依賴kernel結(jié)果的應(yīng)用程序部分,然后重新設(shè)計(jì),以便這些函數(shù)可以在主機(jī)處理器上與FPGA中運(yùn)行的kernel同時(shí)運(yùn)行處理。
保持kernel利用率
Kernel是在FPGA中的,僅在應(yīng)用程序請(qǐng)求它們時(shí)才運(yùn)行。為了最大程度地提高性能,應(yīng)使kernel一致處于繁忙(工作)狀態(tài)。從概念上講,這是通過(guò)在當(dāng)前請(qǐng)求完成之前發(fā)出下一個(gè)請(qǐng)求來(lái)實(shí)現(xiàn)的。這可以實(shí)現(xiàn)流水線式執(zhí)行和重復(fù)執(zhí)行,使kernel得到最佳利用。
在上圖這個(gè)例子中,原始的應(yīng)用程序重復(fù)的調(diào)用 func1,func2和func3。針對(duì)這個(gè)應(yīng)用程序?qū)?yīng)創(chuàng)建了三個(gè)kernel是K1,K2和K3。最平庸的實(shí)現(xiàn)是將三個(gè)kernel按順序運(yùn)行,就像原始的應(yīng)用程序一樣。但是,這意味著每個(gè)kernel只有三分之一的時(shí)間處于工作狀態(tài)。更好的方法是重構(gòu)軟件應(yīng)用程序,以便它可以向kernel發(fā)出流水線請(qǐng)求。這允許K1在K2處理K1的輸出的同時(shí)開(kāi)始處理新的數(shù)據(jù)集。通過(guò)這個(gè)方法,三個(gè)kernel以最大化的利用率不斷運(yùn)行。
優(yōu)化與FPGA之間的數(shù)據(jù)傳輸
在加速的應(yīng)用程序中,必須將數(shù)據(jù)從主機(jī)傳輸?shù)紽PGA,尤其是基于PCIe的應(yīng)用程序中。這就引入了延遲,對(duì)于應(yīng)用程序的整體性能而言,可能是非常昂貴的。數(shù)據(jù)需要在正確的時(shí)間被傳輸,如果kernel的運(yùn)行需要等待數(shù)據(jù),那么應(yīng)用程序的性能會(huì)收到負(fù)面影響。因此,重要的是在kernel需要數(shù)據(jù)時(shí)提前傳輸數(shù)據(jù)。這可以通過(guò)重復(fù)數(shù)據(jù)傳輸、kernel執(zhí)行來(lái)實(shí)現(xiàn),這可以隱藏?cái)?shù)據(jù)傳輸?shù)牡却龝r(shí)間開(kāi)銷,并避免kernel等待數(shù)據(jù)的情況。
優(yōu)化數(shù)據(jù)傳輸?shù)牧硪环N方法是傳輸最佳大小的緩沖區(qū)。如下圖所示,有效的PCIe吞吐量根據(jù)傳輸?shù)木彌_區(qū)大小而有很大的差異。緩沖區(qū)越大,吞吐量越好,從而確保加速器始終具有可操作的數(shù)據(jù)而不會(huì)浪費(fèi)時(shí)間。通常來(lái)說(shuō),最好進(jìn)行1MB或更大的數(shù)據(jù)傳輸。預(yù)先運(yùn)行DMA測(cè)試對(duì)于找到最佳緩沖區(qū)大小可能很有用。同樣,在確定最佳緩沖區(qū)大小時(shí),請(qǐng)考慮大緩沖區(qū)對(duì)資源利用率和傳輸延遲的影響。
Xilinx建議在一個(gè)公共緩沖區(qū)內(nèi)對(duì)多組數(shù)據(jù)進(jìn)行分組,以實(shí)現(xiàn)最大可能的吞吐量。
概念化應(yīng)用程序時(shí)間線
開(kāi)發(fā)人員現(xiàn)在應(yīng)該對(duì)哪些函數(shù)需要加速,需要什么并行性才能達(dá)到性能目標(biāo)以及如何交付應(yīng)用程序有很好的了解。在這一點(diǎn)上,以應(yīng)用程序時(shí)間表的形式總結(jié)信息是非常有用的。應(yīng)用程序時(shí)間軸序列(例如“保持Kernels使用率”中所示的序列)是應(yīng)用程序在運(yùn)行時(shí)表現(xiàn)性能和并行化非常有效的方法。它們可以展示應(yīng)用程序如何調(diào)動(dòng)體系結(jié)構(gòu)中潛在的并行性。
Vitis軟件平臺(tái)會(huì)從實(shí)際應(yīng)用程序運(yùn)行中生成時(shí)間軸視圖。如果開(kāi)發(fā)人員設(shè)計(jì)了預(yù)期的時(shí)間表,則可以將其與實(shí)際結(jié)果進(jìn)行比較,從而確定潛在的問(wèn)題,然后迭代并收斂到最佳結(jié)果,如上圖所示。
5. 微調(diào)架構(gòu)細(xì)節(jié)
在正式編寫(xiě)應(yīng)用程序及其kernel之前,還有最后一步:從頂層決策中細(xì)化和提煉次級(jí)體系架構(gòu)的細(xì)節(jié)。
確定最終kernel邊界
之前已經(jīng)有過(guò)討論,通過(guò)創(chuàng)建多個(gè)kernel的示例可以提高性能。然而,增加CU(compute unit)會(huì)對(duì)IO端口,帶寬和資源有額外地消耗。
在Vitis軟件平臺(tái)流程中,kernel端口的最大寬度為512,并且FPGA在資源方面也具有固定的成本,并不是無(wú)限消耗。重要的是,目標(biāo)平臺(tái)也對(duì)可使用的最大端口設(shè)置了限制。所以我們要注意這些限制,以最佳方式充分使用這些端口及其帶寬。
使用多個(gè)CU進(jìn)行擴(kuò)展的另一種方法是通過(guò)在內(nèi)核中添加多個(gè)引擎(engine)進(jìn)行擴(kuò)展。與添加更多CU的方式來(lái)提高性能一樣,此方法就是用在內(nèi)核中的不同engine同時(shí)處理多個(gè)數(shù)據(jù)集。
將多個(gè)engine放置在同一kernel中可充分利用kernel I / O端口的帶寬。如果數(shù)據(jù)路徑engine不需要端口的全部寬度,則在kernel中添加其他engine比在其中創(chuàng)建具有單個(gè)engine的多個(gè)CU效率更高。
在kernel中放置多個(gè)engine還可以減少端口數(shù)量和事務(wù)數(shù)量到需要仲裁的全局內(nèi)存中,從而提高了有效帶寬。另一方面,采用這種方法需要在開(kāi)發(fā)kernel時(shí)考慮I / O多路復(fù)用行為,盡可能地減少全局內(nèi)存的訪問(wèn)。這是開(kāi)發(fā)人員需要做出的權(quán)衡。
確定kernel的位置和連接性
確定kernel邊界后,開(kāi)發(fā)人員要明確實(shí)例kernel的數(shù)量和連接到全局內(nèi)存資源的端口數(shù)量。在這一點(diǎn)上,了解目標(biāo)平臺(tái)的功能以及哪些全局內(nèi)存資源可用很重要。例如,Alveo?U200數(shù)據(jù)中心加速卡具有分布在三個(gè)超級(jí)邏輯區(qū)域(SLR)中的4 x 16 GB DDR4存儲(chǔ)區(qū)和3 x 128 KB的PLRAM存儲(chǔ)區(qū)。有關(guān)更多信息,請(qǐng)參閱《 Vitis Software Platform Release Notes》。
如果kernel是工廠,則全局內(nèi)存是貨物往返工廠的倉(cāng)庫(kù)。SLR就像獨(dú)特的工業(yè)區(qū),可以在其中建立倉(cāng)庫(kù)和工廠。雖然可以將貨物從一個(gè)區(qū)域的倉(cāng)庫(kù)轉(zhuǎn)移到另一個(gè)區(qū)域的工廠,但這會(huì)增加延遲和復(fù)雜性。
使用多個(gè)DDR有助于平衡數(shù)據(jù)傳輸負(fù)載并提高性能。但是,這也會(huì)帶來(lái)成本,因?yàn)槊總€(gè)DDR控制器都會(huì)消耗FPGA資源。在決定如何將kernel端口連接到內(nèi)存庫(kù)時(shí),請(qǐng)均衡這些考慮因素。
在完善了這些架構(gòu)細(xì)節(jié)之后,開(kāi)發(fā)人員就應(yīng)該已經(jīng)掌握kernel以及整個(gè)應(yīng)用程序所需的所有信息了。
審核編輯:湯梓紅
評(píng)論
查看更多