<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      遇見C++ AMP:在GPU上做并行計算

      遇見C++ AMP:在GPU上做并行計算

       

      Written by Allen Lee

       

      I see all the young believers, your target audience. I see all the old deceivers; we all just sing their song.
      – Marilyn Manson, Target Audience (Narcissus Narcosis)

       

      從CPU到GPU

            在《遇見C++ PPL:C++的并行和異步》里,我們介紹了如何使用C++ PPL在CPU上做并行計算,這次,我們會把舞臺換成GPU,介紹如何使用C++ AMP在上面做并行計算。

            為什么選擇在GPU上做并行計算呢?現在的多核CPU一般都是雙核或四核的,如果把超線程技術考慮進來,可以把它們看作四個或八個邏輯核,但現在的GPU動則就上百個核,比如中端的NVIDIA GTX 560 SE就有288個核,頂級的NVIDIA GTX 690更有多達3072個核,這些超多核(many-core)GPU非常適合大規模并行計算。

            接下來,我們將會在《遇見C++ PPL:C++的并行和異步》的基礎上,對并行計算正弦值的代碼進行一番改造,使之可以在GPU上運行。如果你沒讀過那篇文章,我建議你先去讀一讀它的第一節。此外,本文也假設你對C++ Lambda有所了解,否則,我建議你先去讀一讀《遇見C++ Lambda》

       

      并行計算正弦值

            首先,包含/引用相關的頭文件/命名空間,如代碼1所示。amp.h是C++ AMP的頭文件,包含了相關的函數和類,它們位于concurrency命名空間之內。amp_math.h包含了常用的數學函數,如sin函數,concurrency::fast_math命名空間里的函數只支持單精度浮點數,而concurrency::precise_math命名空間里的函數則對單精度浮點數和雙精度浮點數均提供支持。

      代碼 1

            把浮點數的類型從double改成float,如代碼2所示,這樣做是因為并非所有GPU都支持雙精度浮點數的運算。另外,std和concurrency兩個命名空間都有一個array類,為了消除歧義,我們需要在array前面加上"std::"前綴,以便告知編譯器我們使用的是STL的array類。

      代碼 2

            接著,創建一個array_view對象,把前面創建的array對象包裝起來,如代碼3所示。array_view對象只是一個包裝器,本身不能包含任何數據,必須和真正的容器搭配使用,如C風格的數組、STL的array對象或vector對象。當我們創建array_view對象時,需要通過類型參數指定array_view對象里的元素的類型以及它的維度,并通過構造函數的參數指定對應維度的長度以及包含實際數據的容器。

      代碼 3

            代碼3創建了一個一維的array_view對象,這個維度的長度和前面的array對象的長度一樣,這個包裝看起來有點多余,為什么要這樣做?這是因為在GPU上運行的代碼無法直接訪問系統內存里的數據,需要array_view對象出來充當一個橋梁的角色,使得在GPU上運行的代碼可以通過它間接訪問系統內存里的數據。事實上,在GPU上運行的代碼訪問的并非系統內存里的數據,而是復制到顯存的副本,而負責把這些數據從系統內存復制到顯存的正是array_view對象,這個過程是自動的,無需我們干預。

            有了前面這些準備,我們就可以著手編寫在GPU上運行的代碼了,如代碼4所示。parallel_for_each函數可以看作C++ AMP的入口點,我們通過extent對象告訴它創建多少個GPU線程,通過Lambda告訴它這些GPU線程運行什么代碼,我們通常把這個代碼稱作Kernel。

      代碼 4

            我們希望每個GPU線程可以完成和結果集里的某個元素對應的一組操作,比如說,我們需要計算10個浮點數的正弦值,那么,我們希望創建10個GPU線程,每個線程依次完成讀取浮點數、計算正弦值和保存正弦值三個操作。但是,每個GPU線程運行的代碼都是一樣的,如何區分不同的GPU線程,并定位需要處理的數據呢?

            這個時候就輪到index對象出場了,我們的array_view對象是一維的,因此index對象的類型是index<1>,這個維度的長度是10,因此將會產生從0到9的10個index對象,每個GPU線程對應其中一個index對象。這個index對象將會通過Lambda的參數傳給我們,而我們將會在Kernel里通過這個index對象找到當前GPU線程需要處理的數據。

            既然Lambda的參數只傳遞index對象,那Kernel又是如何與外界交換數據的呢?我們可以通過閉包捕獲當前上下文的變量,這使我們可以靈活地操作多個數據源和結果集,因此沒有必要提供返回值。從這個角度來看,C++ AMP的parallel_for_each函數在用法上類似于C++ PPL的parallel_for函數,如代碼5所示,我們傳給前者的extent對象代替了我們傳給后者的起止索引值。

      代碼 5

            那么,Kernel右邊的restrict(amp)修飾符又是怎么一回事呢?Kernel最終是在GPU上運行的,不管以什么樣的形式,restrict(amp)修飾符正是用來告訴編譯器這點的。當編譯器看到restrict(amp)修飾符時,它會檢查Kernel是否使用了不支持的語言特性,如果有,編譯過程中止,并列出錯誤,否則,Kernel會被編譯成HLSL,并交給DirectCompute運行。Kernel可以調用其他函數,但這些函數必須添加restrict(amp)修飾符,比如代碼4的sin函數

            計算完畢之后,我們可以通過一個for循環輸出array_view對象的數據,如代碼6所示。當我們在CPU上首次通過索引器訪問array_view對象時,它會把數據從顯存復制回系統內存,這個過程是自動的,無需我們干預。

      代碼 6

            哇,不知不覺已經講了這么多,其實,使用C++ AMP一般只涉及到以下三步:

      1. 創建array_view對象。
      2. 調用parallel_for_each函數。
      3. 通過array_view對象訪問計算結果。

      其他的事情,如顯存的分配和釋放、GPU線程的規劃和管理,C++ AMP會幫我們處理的。

       

      并行計算矩陣之和

            上一節我們通過一個簡單的示例了解C++ AMP的使用步驟,接下來我們將會通過另一個示例深入了解array_view、extent和index在二維場景里的用法。

            假設我們現在要計算兩個100 x 100的矩陣之和,首先定義矩陣的行和列,然后通過create_matrix函數創建兩個vector對象,接著創建一個vector對象用于存放矩陣之和,如代碼7所示。

      代碼 7

            create_matrix函數的實現很簡單,它接受矩陣的總容量(行和列之積)作為參數,然后創建并返回一個包含100以內的隨機數的vector對象,如代碼8所示。

      代碼 8

            值得提醒的是,當create_matrix函數執行"return matrix;"時,會把vector對象拷貝到一個臨時對象,并把這個臨時對象返回給調用方,而原來的vector對象則會因為超出作用域而自動銷毀,但我們可以通過編譯器的Named Return Value Optimization對此進行優化,因此不必擔心按值返回會帶來性能問題。

            雖然我們通過行和列等二維概念定義矩陣,但它的實現是通過vector對象模擬的,因此在使用的時候我們需要做一下索引變換,矩陣的第m行第n列元素對應的vector對象的索引是m * columns + n(m、n均從0開始計算)。假設我們要用vector對象模擬一個3 x 3的矩陣,如圖1所示,那么,要訪問矩陣的第2行第0列元素,應該使用索引6(2 * 3 + 0)訪問vector對象。

      圖 1

            接下來,我們需要創建三個array_view對象,分別包裝前面創建的三個vector對象,創建的時候先指定行的大小,再指定列的大小,如代碼9所示。

      代碼 9

            因為我們創建的是二維的array_view對象,所以我們可以直接使用二維索引訪問矩陣的元素,而不必像前面那樣計算對應的索引。還是以3 x 3的矩陣為例,如圖2所示,vector對象會被分成三段,每段包含三個元素,第一段對應array_view對象的第一行,第二段對應第二行,如此類推。如果我們想訪問矩陣的第2行第0列的元素,可以直接使用索引 (2, 0) 訪問array_view對象,這個索引對應vector對象的索引6。

      圖 2

            考慮到第一、二個array_view對象的數據流動方向是從系統內存到顯存,我們可以把它們的第一個類型參數改為const int,如代碼10所示,表示它們在Kernel里是只讀的,不會對它包裝的vector對象產生任何影響。至于第三個array_view對象,由于它只是用來輸出計算結果,我們可以在調用parallel_for_each函數之前調用array_view對象的discard_data成員函數,表明我們對它包裝的vector對象的數據不感興趣,不必把它們從系統內存復制到顯存。

      代碼 10

            有了這些準備,我們就可以著手編寫Kernel了,如代碼11所示。我們把第三個array_view對象的extent傳給parallel_for_each函數,由于這個矩陣是100 x 100的,parallel_for_each函數會創建10,000個GPU線程,每個GPU線程計算這個矩陣的一個元素。由于我們訪問的array_view對象是二維的,索引的類型也要改為相應的index<2>。

      代碼 11

            看到這里,你可能會問,GPU真能創建這么多個線程嗎?這取決于具體的GPU,比如說,NVIDIA GTX 690有16個多處理器(Kepler架構,每個多處理器有192個CUDA核),每個多處理器的最大線程數是2048,因此可以同時容納最多32,768個線程;而NVIDIA GTX 560 SE擁有9個多處理器(Fermi架構,每個多處理器有32個CUDA核),每個多處理器的最大線程數是1536,因此可以同時容納最多13,824個線程。

            計算完畢之后,我們可以在CPU上通過索引器訪問計算結果,代碼12向控制臺輸出結果矩陣的第14行12列元素。

      代碼 12

       

      async + continuation

            掌握了C++ AMP的基本用法之后,我們很自然就想知道parallel_for_each函數會否阻塞當前CPU線程。parallel_for_each函數本身是同步的,它負責發起Kernel的運行,但不會等到Kernel的運行結束才返回。以代碼13為例,當parallel_for_each函數返回時,即使Kernel的運行還沒結束,checkpoint 1位置的代碼也會照常運行,從這個角度來看,parallel_for_each函數是異步的。但是,當我們通過array_view對象訪問計算結果時,如果Kernel的運行還沒結束,checkpoint 2位置的代碼會卡住,直到Kernel的運行結束,array_view對象把數據從顯存復制到系統內存為止。

      代碼 13

            既然Kernel的運行是異步的,我們很自然就會希望C++ AMP能夠提供類似C++ PPL的continuation。幸運的是,array_view對象提供一個synchronize_async成員函數,它返回一個concurrency::completion_future對象,我們可以通過這個對象的then成員函數實現continuation,如代碼14所示。事實上,這個then成員函數就是通過C++ PPL的task對象實現的。

      代碼 14

       

      你可能會問的問題

            1. 開發C++ AMP程序需要什么條件?

            你需要Visual Studio 2012以及一塊支持DirectX 11的顯卡,Visual C++ 2012 Express應該也可以,如果你想做GPU調試,你還需要Windows 8操作系統。運行C++ AMP程序需要Windows 7/Windows 8以及一塊支持DirectX 11的顯卡,部署的時候需要把C++ AMP的運行時(vcamp110.dll)放在程序可以找到的目錄里,或者在目標機器上安裝Visual C++ 2012 Redistributable Package

            2. C++ AMP是否支持其他語言?

            C++ AMP只能在C++里使用,其他語言可以通過相關機制間接調用你的C++ AMP代碼:

            3. C++ AMP是否支持其他平臺?

            目前C++ AMP只支持Windows平臺,不過,微軟發布了C++ AMP開放標準,支持任何人在任何平臺上實現它。如果你希望在其他平臺上利用GPU做并行計算,你可以考慮其他技術,比如NVIDIA的CUDA(只支持NVIDIA的顯卡),或者OpenCL,它們都支持多個平臺。

            4. 能否推薦一些C++ AMP的學習資料?

            目前還沒有C++ AMP的書,Kate Gregory和Ade Miller正在寫一本關于C++ AMP的書,希望很快能夠看到它。下面推薦一些在線學習資料:

       

      *聲明:本文已經首發于InfoQ中文站,版權所有,《遇見C++ AMP:在GPU上做并行計算》,如需轉載,請務必附帶本聲明,謝謝。

      posted @ 2012-08-15 18:43  Allen Lee  閱讀(22063)  評論(8)    收藏  舉報
      主站蜘蛛池模板: 国产精品色内内在线播放| 性欧美暴力猛交69hd| 97免费公开在线视频| 免费无遮挡无码视频网站| 人妻中文字幕精品系列| 亚洲熟妇自偷自拍另欧美| 亚洲AV日韩AV激情亚洲 | 亚洲天堂男人天堂女人天堂| 爽爽精品dvd蜜桃成熟时电影院| 大同县| 熟女人妻视频| 性欧美vr高清极品| 日韩成人福利视频在线观看 | 亚洲精品一区久久久久一品av| 国产毛片子一区二区三区| 亚洲av中文一区二区| 熟女系列丰满熟妇AV| 天天做天天躁天天躁| 少妇久久久被弄到高潮| 99国产精品欧美一区二区三区| 国产精品国产三级国产专| 美女把尿囗扒开让男人添| 关岭| 天天影视色香欲综合久久| 国产在线拍揄自揄视频网试看| 国产内射性高湖| 亚洲深夜精品在线观看| 香格里拉县| 国产强奷在线播放免费| 亚洲日韩中文字幕在线播放| 亚洲熟妇熟女久久精品综合| 亚洲综合精品成人| 亚洲中文字幕一区二区| 在线国产极品尤物你懂的| 国产高清av首播原创麻豆| 免费观看日本污污ww网站69| 国产精品亚洲二区在线播放 | 国产国产精品人体在线视| 日本一卡2卡3卡四卡精品网站| 99热久久这里只有精品| 天堂а√在线中文在线|