天天看點

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

本節書摘來自華章計算機《cuda c程式設計權威指南》一書中的第3章,第3.2節,作者 [美] 馬克斯·格羅斯曼(max grossman),譯 顔成鋼 殷建 李亮,更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。

啟動核心時,從軟體的角度你看到了什麼?對于你來說,在核心中似乎所有的線程都是并行地運作的。在邏輯上這是正确的,但從硬體的角度來看,不是所有線程在實體上都可以同時并行地執行。本章已經提到了把32個線程劃分到一個執行單元中的概念:線程束。現在從硬體的角度來介紹線程束執行,并能夠獲得指導核心設計的方法。

線程束是sm中基本的執行單元。當一個線程塊的網格被啟動後,網格中的線程塊分布在sm中。一旦線程塊被排程到一個sm上,線程塊中的線程會被進一步劃分為線程束。一個線程束由32個連續的線程組成,在一個線程束中,所有的線程按照單指令多線程(simt)方式執行;也就是說,所有線程都執行相同的指令,每個線程在私有資料上進行操作。圖3-10展示了線程塊的邏輯視圖和硬體視圖之間的關系。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

然而,從硬體的角度來看,所有的線程都被組織成了一維的,線程塊可以被配置為一維、二維或三維的。在一個塊中,每個線程都有一個唯一的id。對于一維的線程塊,唯一的線程id被存儲在cuda的内置變量threadidx.x中,并且,threadidx.x中擁有連續值的線程被分組到線程束中。例如,一個有128個線程的一維線程塊被組織到4個線程束裡,如下所示:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

用x次元作為最内層的次元,y次元作為第二個次元,z作為最外層的次元,則二維或三維線程塊的邏輯布局可以轉化為一維實體布局。例如,對于一個給定的二維線程塊,在一個塊中每個線程的獨特辨別符都可以用内置變量threadidx和blockdim來計算:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

對于一個三維線程塊,計算如下:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

一個線程塊的線程束的數量可以根據下式确定:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

是以,硬體總是給一個線程塊配置設定一定數量的線程束。線程束不會在不同的線程塊之間分離。如果線程塊的大小不是線程束大小的偶數倍,那麼在最後的線程束裡有些線程就不會活躍。圖3-11是一個在x軸中有40個線程、在y軸中有2個線程的二維線程塊。從應用程式的角度來看,在一個二維網格中共有80個線程。

硬體為這個線程塊配置了3個線程束,使總共96個硬體線程去支援80個軟體線程。注意,最後半個線程束是不活躍的。即使這些線程未被使用,它們仍然消耗sm的資源,如寄存器。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質
《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

控制流是進階程式設計語言的基本構造中的一種。gpu支援傳統的、c風格的、顯式的控制流結構,例如,if…then…else、for和while。

cpu擁有複雜的硬體以執行分支預測,也就是在每個條件檢查中預測應用程式的控制流會使用哪個分支。如果預測正确,cpu中的分支隻需付出很小的性能代價。如果預測不正确,cpu可能會停止運作很多個周期,因為指令流水線被清空了。我們不必完全了解為什麼cpu擅長處理複雜的控制流。這個解釋隻是作為對比的背景。

gpu是相對簡單的裝置,它沒有複雜的分支預測機制。一個線程束中的所有線程在同一周期中必須執行相同的指令,如果一個線程執行一條指令,那麼線程束中的所有線程都必須執行該指令。如果在同一線程束中的線程使用不同的路徑通過同一個應用程式,這可能會産生問題。例如,思考下面的語句:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

假設在一個線程束中有16個線程執行這段代碼,cond為true,但對于其他16個來說cond為false。一半的線程束需要執行if語句塊中的指令,而另一半需要執行else語句塊中的指令。在同一線程束中的線程執行不同的指令,被稱為線程束分化。我們已經知道,在一個線程束中所有線程在每個周期中必須執行相同的指令,是以線程束分化似乎會産生一個悖論。

如果一個線程束中的線程産生分化,線程束将連續執行每一個分支路徑,而禁用不執行這一路徑的線程。線程束分化會導緻性能明顯地下降。在前面的例子中可以看到,線程束中并行線程的數量減少了一半:隻有16個線程同時活躍地執行,而其他16個被禁用了。條件分支越多,并行性削弱越嚴重。

注意,線程束分化隻發生在同一個線程束中。在不同的線程束中,不同的條件值不會引起線程束分化。

圖3-12顯示了線程束分化。在一個線程束中所有的線程必須采用if…then兩個分支來表述。如果線程的條件為true,它将執行if子句;否則,當等待執行完成時,線程停止。

為了獲得最佳的性能,應該避免在同一線程束中有不同的執行路徑。請記住,在一個線程塊中,線程的線程束配置設定是确定的。是以,以這樣的方式對資料進行分區是可行的(盡管不是微不足道的,但取決于算法),以確定同一個線程束中的所有線程在一個應用程式中使用同一個控制路徑。

例如,假設有兩個分支,下面展示了簡單的算術核心示例。我們可以用一個偶數和奇數線程方法來模拟一個簡單的資料分區,目的是導緻線程束分化。該條件(tid%2==0)使偶數編号的線程執行if子句,奇數編号的線程執行else子句。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質
《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

如果使用線程束方法(而不是線程方法)來交叉存取資料,可以避免線程束分化,并且裝置的使用率可達到100%。條件(tid/warpsize)%2==0使分支粒度是線程束大小的倍數;偶數編号的線程執行if子句,奇數編号的線程執行else子句。這個核函數産生相同的輸出,但是順序不同。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

現在,使用代碼清單3-1中的代碼可以測量這兩個核函數的性能。也可以從wrox.com中下載下傳simpledivergence.cu檔案。因為在裝置上第一次運作可能會增加間接開銷,并且在此處測量的性能是非常精細的,是以,添加了一個額外的核心啟動(warmingup,與mathkernel2一樣)來去除這一間接開銷。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質
《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質
《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

使用下面的指令編譯這段代碼:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

在fermi m2070 gpu上運作simpledivergence,輸出報告如下。兩個核心的運作時間很相近。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

通過使用nvprof分析器,可以從gpu中獲得名額,進而可以直接觀察到線程束分化。

在這裡,nvprof的branch_efficiency名額是用來計算simpledivergence的樣本執行的:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

下面的結果是由nvprof報告的。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

分支效率被定義為未分化的分支與全部分支之比,可以使用以下公式來計算:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

奇怪的是,沒有報告顯示出有分支分化(即分支效率是100%)。這個奇怪的現象是cuda編譯器優化導緻的結果,它将短的、有條件的代碼段的斷定指令取代了分支指令(導緻分化的實際控制流指令)。

在分支預測中,根據條件,把每個線程中的一個斷定變量設定為1或0。這兩種條件流路徑被完全執行,但隻有斷定為1的指令被執行。斷定為0的指令不被執行,但相應的線程也不會停止。這和實際的分支指令之間的差別是微妙的,但了解它很重要。隻有在條件語句的指令數小于某個門檻值時,編譯器才用斷定指令替換分支指令。是以,一段很長的代碼路徑肯定會導緻線程束分化。

如下所示,重寫mathkernel1核函數,使核心代碼的分支預測直接顯示:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質
《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

添加mathkernel3,再次編譯和運作檔案simpledivergence.cu,會報告下列性能:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

使用下面的指令,可以強制cuda編譯器不利用分支預測去優化核心:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

如下所示,可以用nvprof再次檢查沒有被優化的核心分化:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

結果總結如下:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

另外,可以用nvprof獲得分支和分化分支的事件計數器,如下所示:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

結果如下:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

cuda的nvcc編譯器仍然是在mathkernel1和mathkernel3上執行有限的優化,以保持分支效率在50%以上。注意,mathkernel2不報告分支分化的唯一原因是它的分支粒度是線程束大小的倍數。此外,把mathkernel1中的if. . .else語句分離為mathkernel3的多個if語句,可以使分化分支的數量翻倍。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

線程束的本地執行上下文主要由以下資源組成:

程式計數器

寄存器

共享記憶體

由sm處理的每個線程束的執行上下文,在整個線程束的生存期中是儲存在晶片内的。是以,從一個執行上下文切換到另一個執行上下文沒有損失。

每個sm都有32位的寄存器組,它存儲在寄存器檔案中,并且可以線上程中進行配置設定,同時固定數量的共享記憶體用來線上程塊中進行配置設定。對于一個給定的核心,同時存在于同一個sm中的線程塊和線程束的數量取決于在sm中可用的且核心所需的寄存器和共享記憶體的數量。

圖3-13顯示了若每個線程消耗的寄存器越多,則可以放在一個sm中的線程束就越少。如果可以減少核心消耗寄存器的數量,那麼就可以同時處理更多的線程束。如圖3-14所示,若一個線程塊消耗的共享記憶體越多,則在一個sm中可以被同時處理的線程塊就會變少。如果每個線程塊使用的共享記憶體數量變少,那麼可以同時處理更多的線程塊。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

資源可用性通常會限制sm中常駐線程塊的數量。每個sm中寄存器和共享記憶體的數量因裝置擁有不同的計算能力而不同。如果每個sm沒有足夠的寄存器或共享記憶體去處理至少一個塊,那麼核心将無法啟動。一些關鍵的限度如表3-2所示。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

當計算資源(如寄存器和共享記憶體)已配置設定給線程塊時,線程塊被稱為活躍的塊。它所包含的線程束被稱為活躍的線程束。活躍的線程束可以進一步被分為以下3種類型:

標明的線程束

阻塞的線程束

符合條件的線程束

一個sm上的線程束排程器在每個周期都選擇活躍的線程束,然後把它們排程到執行單元。活躍執行的線程束被稱為標明的線程束。如果一個活躍的線程束準備執行但尚未執行,它是一個符合條件的線程束。如果一個線程束沒有做好執行的準備,它是一個阻塞的線程束。如果同時滿足以下兩個條件則線程束符合執行條件。

32個cuda核心可用于執行

目前指令中所有的參數都已就緒

例如,kepler sm上活躍的線程束數量,從啟動到完成在任何時候都必須小于或等于64個并發線程束的架構限度。在任何周期中,標明的線程束數量都小于或等于4。如果線程束阻塞,線程束排程器會令一個符合條件的線程束代替它去執行。由于計算資源是線上程束之間進行配置設定的,而且線上程束的整個生存期中都保持在晶片内,是以線程束上下文的切換是非常快的。在下面幾節中,你将會認識到為了隐藏由線程束阻塞造成的延遲,需要讓大量的線程束保持活躍。

在cuda程式設計中需要特别關注計算資源配置設定:計算資源限制了活躍的線程束的數量。是以必須了解由硬體産生的限制和核心用到的資源。為了最大程度地利用gpu,需要最大化活躍的線程束數量。

sm依賴線程級并行,以最大化功能單元的使用率,是以,使用率與常駐線程束的數量直接相關。在指令發出和完成之間的時鐘周期被定義為指令延遲。當每個時鐘周期中所有的線程排程器都有一個符合條件的線程束時,可以達到計算資源的完全利用。這就可以保證,通過在其他常駐線程束中釋出其他指令,可以隐藏每個指令的延遲。

與在cpu上用c語言程式設計相比,延遲隐藏在cuda程式設計中尤為重要。cpu核心是為同時最小化延遲一個或兩個線程而設計的,而gpu則是為處理大量并發和輕量級線程以最大化吞吐量而設計的。gpu的指令延遲被其他線程束的計算隐藏。

考慮到指令延遲,指令可以被分為兩種基本類型:

算術指令

記憶體指令

算術指令延遲是一個算術操作從開始到它産生輸出之間的時間。記憶體指令延遲是指發送出的加載或存儲操作和資料到達目的地之間的時間。對于每種情況,相應的延遲大約為:

算術操作為10~20個周期

全局記憶體通路為400~800個周期

圖3-15表示線程束0阻塞執行流水線的一個示例。線程束排程器選取其他線程束執行,當線程束0符合條件時再執行它。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質
《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質
《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

對于算術運算來說,其所需的并行可以表示成隐藏算術延遲所需要的操作數量。表3-3列出了fermi和kepler裝置所需的操作數量。示例中的算術運算是一個32位的浮點數乘加運算(a+b×c),表示在每個sm中每個時鐘周期内的操作數量。吞吐量因不同的算術指令而不同。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

吞吐量由sm中每個周期内的操作數量确定,而執行一條指令的一個線程束對應32個操作。是以,為保持計算資源的充分利用,對于fermi gpu而言,每個sm中所需的線程束數量通過計算為640÷32=20個線程束。是以,算術運算所需的并行可以用操作的數量或線程束的數量來表示。這個簡單的機關轉換表明,有兩種方法可以提高并行:

指令級并行(ilp):一個線程中有很多獨立的指令

線程級并行(tlp):很多并發地符合條件的線程

對記憶體操作來說,其所需的并行可以表示為在每個周期内隐藏記憶體延遲所需的位元組數。表3-4列出了fermi和kepler架構的名額。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

因為記憶體吞吐量通常表示為每秒千兆位元組數,是以首先需要用對應的記憶體頻率将吞吐量轉換為每周期千兆位元組數。可以使用下面的指令檢測裝置的記憶體頻率:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

例如,fermi的記憶體頻率(在tesla c2070上測量得到)是1.566 ghz。kepler的記憶體頻率(在tesla k20上測量得到)是2.6 ghz。因為1 hz被定義為每秒一個周期,是以可以把帶寬從每秒千兆位元組數轉換為每周期千兆位元組數,公式如下所示:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

用記憶體延遲乘以每周期位元組數,可以得到fermi記憶體操作所需的并行,接近74kb的記憶體i/o運作,用以實作充分的利用。這個值是對于整個裝置,而不是對于每個sm來說的,因為記憶體帶寬是對于整個裝置而言的。

利用應用程式,把這些值與線程束或線程數量關聯起來。假設每個線程都把一浮點資料(4個位元組)從全局記憶體移動到sm中用于計算,則在fermi gpu上,總共需要18 500個線程或579個線程束來隐藏所有記憶體延遲,具體運算如下所示:

74 kb÷4位元組/線程≌18 500個線程

18 500個線程÷32個線程/線程束≌579個線程束

fermi架構有16個sm。是以,需要579個線程束÷16個sm=36個線程束/sm,以隐藏所有的記憶體延遲。如果每個線程執行多個獨立的4位元組加載,隐藏記憶體延遲需要的線程就可以更少。

與指令延遲很像,通過在每個線程/線程束中建立更多獨立的記憶體操作,或建立更多并發地活躍的線程/線程束,可以增加可用的并行。

延遲隐藏取決于每個sm中活躍線程束的數量,這一數量由執行配置和資源限制隐式決定(一個核心中寄存器和共享記憶體的使用情況)。選擇一個最優執行配置的關鍵是在延遲隐藏和資源利用之間找到一種平衡。下一節将會更加詳細地研究這個問題。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

在每個cuda核心裡指令是順序執行的。當一個線程束阻塞時,sm切換執行其他符合條件的線程束。理想情況下,我們想要有足夠的線程束占用裝置的核心。占用率是每個sm中活躍的線程束占最大線程束數量的比值。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

使用下述函數,可以檢測裝置中每個sm的最大線程束數量:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

來自裝置的各種統計資料在cudadeviceprop結構中被傳回。每個sm中線程數量的最大值在以下變量中傳回:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

用maxthreadspermultiprocessor除以32,可以得到最大線程束數量。代碼清單3-2展示了如何使用cudagetdeviceproperties獲得gpu的配置資訊。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

從wrox.com中可以下載下傳simpledevicequery.cu檔案。使用以下指令編譯并運作這個示例:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

tesla m2070的輸出結果顯示如下。每個sm中線程數量的最大值是1 536。是以,每個sm中線程束數量的最大值是48。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

cuda工具包包含了一個電子表格,它被稱為cuda占用率電腦,有助于選擇網格和塊的維數以使一個核心的占用率最大化。圖3-17展示了占用率電腦的一個截圖。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

占用率電腦包含幾個部分。首先,必須提供gpu的計算能力和核心的資源使用情況的資訊。

在确定gpu的計算能力後,實體限制部分的資料是自動填充的。接下來,需要輸入以下核心資源資訊:

每個塊的線程(執行配置)

每個線程的寄存器(資源使用情況)

每個塊的共享記憶體(資源使用情況)

每個線程的寄存器和每個塊的共享記憶體資源的使用情況可以從nvcc中用以下編譯器标志獲得:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

一旦進入這個資料,核心占用率便會顯示在gpu占用率資料段。其他部分提供必要的資訊,來調整執行配置和資源使用情況,以獲得更好的裝置占用率。

核心使用的寄存器數量會對常駐線程束數量産生顯著的影響。寄存器的使用可以用下面的nvcc标志手動控制。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

-maxrregcount選項告訴編譯器每個線程使用的寄存器數量不能超過num個。使用這個編譯器标志,可以得到占用率電腦推薦的寄存器數量,同時使用這個數值可以改善應用程式的性能。

為了提高占用率,還需要調整線程塊配置或重新調整資源的使用情況,以允許更多的線程束同時處于活躍狀态和提高計算資源的使用率。極端地操縱線程塊會限制資源的利用:

小線程塊:每個塊中線程太少,會在所有資源被充分利用之前導緻硬體達到每個sm的線程束數量的限制。

大線程塊:每個塊中有太多的線程,會導緻在每個sm中每個線程可用的硬體資源較少。

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

盡管在每種情況下會遇到不同的硬體限制,但它們都會導緻計算資源未被充分利用,阻礙隐藏指令和記憶體延遲的并行的建立。占用率唯一注重的是在每個sm中并發線程或線程束的數量。然而,充分的占用率不是性能優化的唯一目标。核心一旦達到一定級别的占用率,進一步增加占用率可能不會改進性能。為了提高性能,可以調整很多其他因素。在後續章節中将詳細介紹這些内容。

栅欄同步是一個原語,它在許多并行程式設計語言中都很常見。在cuda中,同步可以在兩個級别執行:

系統級:等待主機和裝置完成所有的工作

塊級:在裝置執行過程中等待一個線程塊中所有線程到達同一點

對于主機來說,由于許多cuda api調用和所有的核心啟動不是同步的,cudadevicesyn-chronize函數可以用來阻塞主機應用程式,直到所有的cuda操作(複制、核函數等)完成:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

這個函數可能會從先前的異步cuda操作傳回錯誤。

因為在一個線程塊中線程束以一個未定義的順序被執行,cuda提供了一個使用塊局部栅欄來同步它們的執行的功能。使用下述函數在核心中标記同步點:

《CUDA C程式設計權威指南》——3.2 了解線程束執行的本質

當__syncthreads被調用時,在同一個線程塊中每個線程都必須等待直至該線程塊中所有其他線程都已經達到這個同步點。在栅欄之前所有線程産生的所有全局記憶體和共享記憶體通路,将會在栅欄後對線程塊中所有其他的線程可見。該函數可以協調同一個塊中線程之間的通信,但它強制線程束空閑,進而可能對性能産生負面影響。

線程塊中的線程可以通過共享記憶體和寄存器來共享資料。當線程之間共享資料時,要避免競争條件。競争條件或危險,是指多個線程無序地通路相同的記憶體位置。例如,當一個位置的無序讀發生在寫操作之後時,寫後讀競争條件發生。因為讀和寫之間沒有順序,是以讀應該在寫前還是在寫後加載值是未定義的。其他競争條件的例子有讀後寫或寫後寫。當線程塊中的線程在邏輯上并行運作時,在實體上并不是所有的線程都可以在同一時間執行。如果線程a試圖讀取由線程b在不同的線程束中寫的資料,若使用了适當的同步,隻需确定線程b已經寫完就可以了。否則,會出現競争條件。第4章會更深入地研究同步問題。

在不同的塊之間沒有線程同步。塊間同步,唯一安全的方法是在每個核心執行結束端使用全局同步點;也就是說,在全局同步之後,終止目前的核函數,開始執行新的核函數。

不同塊中的線程不允許互相同步,是以gpu可以以任意順序執行塊。這使得cuda程式在大規模并行gpu上是可擴充的。

對于任何并行應用程式而言,可擴充性是一個理想的特性。可擴充性意味着為并行應用程式提供了額外的硬體資源,相對于增加的資源,并行應用程式會産生加速。例如,若一個cuda程式在兩個sm中是可擴充的,則與在一個sm中運作相比,在兩個sm中運作會使運作時間減半。一個可擴充的并行程式可以高效地使用所有的計算資源以提高性能。可擴充性意味着增加的計算核心可以提高性能。串行代碼本身是不可擴充的,因為在成千上萬的核心上運作一個串行單線程應用程式,對性能是沒有影響的。并行代碼有可擴充的潛能,但真正的可擴充性取決于算法設計和硬體特性。

能夠在可變數量的計算核心上執行相同的應用程式代碼的能力被稱為透明可擴充性。一個透明的可擴充平台拓寬了現有應用程式的應用範圍,并減少了開發人員的負擔,因為它們可以避免新的或不同的硬體産生的變化。可擴充性比效率更重要。一個可擴充但效率很低的系統可以通過簡單新增硬體核心來處理更大的工作負載。一個效率很高但不可擴充的系統可能很快會達到可實作性能的上限。

cuda核心啟動時,線程塊分布在多個sm中。網格中的線程塊以并行或連續或任意的順序被執行。這種獨立性使得cuda程式在任意數量的計算核心間可以擴充。

圖3-18展示了cuda架構可擴充性的一個例子。左側的gpu有兩個sm,可以同時執行兩個塊;右側的gpu有4個sm,可以同時執行4個塊。不修改任何代碼,一個應用程式可以在不同的gpu配置上運作,并且所需的執行時間根據可用的資源而改變。

繼續閱讀