CUDA程式設計(一)第一個CUDA程式

CUDA程式設計(一)

http://blog.csdn.net/sunmc1204953974/article/details/51000970

第一個CUDA程式 Kernel.cu

CUDA是什麼?

CUDA(Compute Unified Device Architecture),是顯示卡廠商NVIDIA推出的運算平臺。是一種通用平行計算架構,該架構使GPU能夠解決複雜的計算問題。說白了就是我們可以使用GPU來並行完成像神經網路、影象處理演算法這些在CPU上跑起來比較吃力的程式。通過GPU和高並行,我們可以大大提高這些演算法的執行速度。

有的同學可能知道,在CPU和GPU上跑同一個神經網路,由於其大量的浮點數權重計算以及可高並行化,其速度的差距往往在10倍左右,原本需要睡一覺才能看到的訓練結果也許看兩集動漫就OK了。

GPU並行在影象處理方面更是應用廣泛,大家知道影象處理實際上是對影象的二維矩陣進行處理,影象的尺寸都是幾百乘幾百的,很容易就是上萬個畫素的操作,隨便搞個什麼平滑演算法,匹配演算法等等的影象演算法在CPU上跑個幾十秒都是很正常的,對於影象處理,神經網路這種大矩陣計算,往往是可以並行化的,通過GPU並行化處理往往能夠成倍的加速。

綜上所述,去學習一下怎麼在GPU上開個幾千個執行緒過把優化癮還是一件很愜意的事情,更何況CUDA為我們提供了這麼優秀的計算平臺,可以直接使用C/C 寫出在顯示晶片上執行的程式,還是一件很讚的事情。

不過CUDA程式設計需要注意的點是很多的,有很多因素如果忽略了會大大降低速度,寫的不好的CUDA程式可能會比CPU程式還慢。所以優化和並行是一門很大的學問,需要我們去不斷學習與瞭解。

CUDA安裝

CUDA發展到現在說實話已經比較成熟了,當然在使用的時候偶爾會出現各種各樣的問題(充滿血與淚),但就談安裝來說已經很簡單了,這裡以VS2013和CUDA 7.0為例(現在已經到CUDA7.5了,我需要使用ZED攝像頭,而它只支援7.0,所以電腦上裝的7.0)。

首先我們隨便用搜尋引擎搜尋CUDA就會找到CUDA Toolkit的下載首頁:

https://developer.nvidia.com/cuda-downloads

之後選擇系統下載就好:

這裡寫圖片描述

下載結束之後一路安裝就好,注意:安裝選項那裡要選擇自定義然後把所有都勾選上:

這裡寫圖片描述

現在的CUDA安裝還是很簡單的,等安裝結束之後就會發現CUDA for Visual Studio已經安裝成功了,我們也不需要去新增什麼環境變數,這些工作安裝程式都幫我們做好了~之後我們開啟VS,也不需要繁瑣的各種引庫的過程了,我們只需要新建一個CUDA工程就可以了~

這裡寫圖片描述

建立好工程之後,會發現已經自帶了一個矩陣相乘的示例程式碼Kernel.cu,二話不說直接ctrl f5編譯執行,如果沒報什麼編譯錯誤執行成功那就恭喜同學你跑了你的第一個我CUDA程式~Kernel.cu

這裡寫圖片描述

注意:這裡我再多說幾句,我關於各種錯誤的解決經驗。CUDA還是會經常出現各式各樣的問題的,我自己就遇到過好幾個。

(1)首先最簡單的一個,你的工程路徑不能有中文。。。好多個版本了都沒解決這個問題。

(2)然後,還有一個很傻X的問題,如果你的C:\Users\****\AppData這個路徑,****部分因為你的Microsoft賬戶是中文的,有時候你裝完系統登入完賬號,這個資料夾會是中文的。。比如王尼瑪會有一個尼瑪資料夾。出現這種情況會出現一個什麼什麼Unicode的錯誤,然後基本上是沒救了,反正我最終沒能改掉那個資料夾的名字。。。。有知道怎麼改的同學一定要告訴我一下。。

(3)有時候還會出現下面這個錯誤,這個也很奇葩,我隔了一週沒寫CUDA程式,然後再寫的時候原來沒問題的程式都編譯不過了,周天就給我來了這麼個開門黑,重灌了各種版本的CUDA仍然不行,弄了兩天才莫名其妙的弄好,這個貌似是因為.net的問題,我在控制面板-解除安裝程式-啟用或關閉Windows功能 裡把.net4.5關了,開啟了.Net3.5 , 重啟,然後,還是不行,我已經準備要重灌電腦了,去吃了個晚飯回來,莫名其妙行了。

這裡寫圖片描述

(4)我還遇到過核函式進不去的情況,也是莫名其妙出現的,就是下面會講到的__global__函式,最後被迫重灌了遍CUDA,然後還是不行,重啟,結果行了。

總之大家看到我遇到的奇葩問題就知道了,這玩意有時候還是很脆弱的,什麼防毒軟體,系統更新啥的都可能隨時幹掉你的CUDA,所以防患於未然還是把這些玩意都關了吧。

我知道CUDA安裝的還是比較慢的,安裝的時候還是來看一下關於GPU和CUDA架構的一些基礎知識吧~

CPU&GPU

這裡寫圖片描述

上圖是CPU與GPU的對比圖,對於浮點數操作能力,CPU與GPU的能力相差在GPU更適用於計算強度高,多並行的計算中。因此,GPU擁有更多電晶體,而不是像CPU一樣的資料Cache和流程控制器。這樣的設計是因為多平行計算的時候每個資料單元執行相同程式,不需要那麼繁瑣的流程控制,而更需要高計算能力,這也不需要大cache。但也因此,每個GPU的計算單元的結構是十分簡單的,因此對程式的可並行性的要求也是十分苛刻的。

這裡我們再介紹一下使用GPU計算的優缺點(摘自《深入淺出談CUDA》,所以舉的例子稍微老了一點,但不影響意思哈):

使用顯示晶片來進行運算工作,和使用 CPU 相比,主要有幾個好處:

  1. 顯示晶片通常具有更大的記憶體頻寬。例如,NVIDIA 的 GeForce 8800GTX 具有超過50GB/s 的記憶體頻寬,而目前高階 CPU 的記憶體頻寬則在 10GB/s 左右。

  2. 顯示晶片具有更大量的執行單元。例如 GeForce 8800GTX 具有 128 個 “stream processors”,頻率為 1.35GHz。CPU 頻率通常較高,但是執行單元的數目則要少得多。

  3. 和高階 CPU 相比,顯示卡的價格較為低廉。例如一張 GeForce 8800GT 包括512MB 記憶體的價格,和一顆 2.4GHz 四核心 CPU 的價格相若。

當然,使用顯示晶片也有它的一些缺點:

  1. 顯示晶片的運算單元數量很多,因此對於不能高度並行化的工作,所能帶來的幫助就不大。

  2. 顯示晶片目前通常只支援 32 bits 浮點數,且多半不能完全支援 IEEE 754 規格, 有些運算的精確度可能較低。目前許多顯示晶片並沒有分開的整數運算單元,因此整數運算的效率較差。

  3. 顯示晶片通常不具有分支預測等複雜的流程控制單元,因此對於具有高度分支的程式,效率會比較差。

  4. 目前 GPGPU 的程式模型仍不成熟,也還沒有公認的標準。例如 NVIDIA 和AMD/ATI 就有各自不同的程式模型。

CUDA架構

host 和 kernel:

這裡寫圖片描述

在 CUDA 的架構下,一個程式分為兩個部份:host 端和 device 端。Host 端是指在 CPU 上執行的部份,而 device 端則是在顯示晶片上執行的部份。Device 端的程式又稱為 “kernel”。通常 host 端程式會將資料準備好後,複製到顯示卡的記憶體中,再由顯示晶片執行 device 端程式,完成後再由 host 端程式將結果從顯示卡的記憶體中取回。

由於 CPU 存取顯示卡記憶體時只能透過 PCI Express 介面,因此速度較慢(PCI Express x16 的理論頻寬是雙向各 4GB/s),因此不能太常進行這類動作,以免降低效率。

thread-block-grid 結構:

這裡寫圖片描述

在 CUDA 架構下,顯示晶片執行時的最小單位是thread。數個 thread 可以組成一個block。一個 block 中的 thread 能存取同一塊共享的記憶體,而且可以快速進行同步的動作。

每一個 block 所能包含的 thread 數目是有限的。不過,執行相同程式的 block,可以組成grid。不同 block 中的 thread 無法存取同一個共享的記憶體,因此無法直接互通或進行同步。因此,不同 block 中的 thread 能合作的程度是比較低的。不過,利用這個模式,可以讓程式不用擔心顯示晶片實際上能同時執行的 thread 數目限制。例如,一個具有很少量執行單元的顯示晶片,可能會把各個 block 中的 thread 順序執行,而非同時執行。不同的 grid 則可以執行不同的程式(即
kernel)。

每個 thread 都有自己的一份 register 和 local memory 的空間。同一個 block 中的每個thread 則有共享的一份 share memory。此外,所有的 thread(包括不同 block 的 thread)都共享一份 global memory、constant memory、和 texture memory。不同的 grid 則有各自的 global memory、constant memory 和 texture memory。

執行模式:

由於顯示晶片大量平行計算的特性,它處理一些問題的方式,和一般 CPU 是不同的。主要的特點包括:

  1. 記憶體存取 latency 的問題:CPU 通常使用 cache 來減少存取主記憶體的次數,以避免記憶體 latency 影響到執行效率。顯示晶片則多半沒有 cache(或很小),而利用並行化執行的方式來隱藏記憶體的 latency(即,當第一個 thread 需要等待記憶體讀取結果時,則開始執行第二個 thread,依此類推)。

  2. 分支指令的問題:CPU 通常利用分支預測等方式來減少分支指令造成的 pipeline bubble。顯示晶片則多半使用類似處理記憶體 latency 的方式。不過,通常顯示晶片處理分支的效率會比較差。

因此,最適合利用 CUDA 處理的問題,是可以大量並行化的問題,才能有效隱藏記憶體的latency,並有效利用顯示晶片上的大量執行單元。使用 CUDA 時,同時有上千個 thread 在執行是很正常的。因此,如果不能大量並行化的問題,使用 CUDA 就沒辦法達到最好的效率了。

總結:

再寫下去篇幅就太長了,本篇部落格主要還是介紹了CUDA的安裝以及一些基本的CUDA的架構,大家趁著CUDA安裝的空可以仔細看一下CUDA的結構,這對後面的程式設計還是很重要的,下面我會從一個很小的程式寫起,不斷地把上面介紹到的東西都加進去,希望能幫助到大家的學習。

參考資料:《深入淺出談CUDA》