小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

 長(zhǎng)沙7喜 2018-04-08

作者: 葉 虎

編輯:李雪冬

前 言

2006年,NVIDIA公司發(fā)布了CUDA(http://docs./cuda/),CUDA是建立在NVIDIA的CPUs上的一個(gè)通用并行計(jì)算平臺(tái)和編程模型,基于CUDA編程可以利用GPUs的并行計(jì)算引擎來更加高效地解決比較復(fù)雜的計(jì)算難題。近年來,GPU最成功的一個(gè)應(yīng)用就是深度學(xué)習(xí)領(lǐng)域,基于GPU的并行計(jì)算已經(jīng)成為訓(xùn)練深度學(xué)習(xí)模型的標(biāo)配。目前,最新的CUDA版本為CUDA 9。

GPU并不是一個(gè)獨(dú)立運(yùn)行的計(jì)算平臺(tái),而需要與CPU協(xié)同工作,可以看成是CPU的協(xié)處理器,因此當(dāng)我們?cè)谡fGPU并行計(jì)算時(shí),其實(shí)是指的基于CPU+GPU的異構(gòu)計(jì)算架構(gòu)。在異構(gòu)計(jì)算架構(gòu)中,GPU與CPU通過PCIe總線連接在一起來協(xié)同工作,CPU所在位置稱為為主機(jī)端(host),而GPU所在位置稱為設(shè)備端(device),如下圖所示。

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

基于CPU+GPU的異構(gòu)計(jì)算. 來源:Preofessional CUDA? C Programming

可以看到GPU包括更多的運(yùn)算核心,其特別適合數(shù)據(jù)并行的計(jì)算密集型任務(wù),如大型矩陣運(yùn)算,而CPU的運(yùn)算核心較少,但是其可以實(shí)現(xiàn)復(fù)雜的邏輯運(yùn)算,因此其適合控制密集型任務(wù)。另外,CPU上的線程是重量級(jí)的,上下文切換開銷大,但是GPU由于存在很多核心,其線程是輕量級(jí)的。因此,基于CPU+GPU的異構(gòu)計(jì)算平臺(tái)可以優(yōu)勢(shì)互補(bǔ),CPU負(fù)責(zé)處理邏輯復(fù)雜的串行程序,而GPU重點(diǎn)處理數(shù)據(jù)密集型的并行計(jì)算程序,從而發(fā)揮最大功效。

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

基于CPU+GPU的異構(gòu)計(jì)算應(yīng)用執(zhí)行邏輯. 來源:Preofessional CUDA? C Programming

CUDA是NVIDIA公司所開發(fā)的GPU編程模型,它提供了GPU編程的簡(jiǎn)易接口,基于CUDA編程可以構(gòu)建基于GPU計(jì)算的應(yīng)用程序。CUDA提供了對(duì)其它編程語言的支持,如C/C++,Python,F(xiàn)ortran等語言,這里我們選擇CUDA C/C++接口對(duì)CUDA編程進(jìn)行講解。開發(fā)平臺(tái)為Windows 10 + VS 2013,Windows系統(tǒng)下的CUDA安裝教程可以參考這里http://docs./cuda/cuda-installation-guide-microsoft-windows/index.html

1、CUDA編程模型基礎(chǔ)

在給出CUDA的編程實(shí)例之前,這里先對(duì)CUDA編程模型中的一些概念及基礎(chǔ)知識(shí)做個(gè)簡(jiǎn)單介紹。CUDA編程模型是一個(gè)異構(gòu)模型,需要CPU和GPU協(xié)同工作。在CUDA中,host和device是兩個(gè)重要的概念,我們用host指代CPU及其內(nèi)存,而用device指代GPU及其內(nèi)存。CUDA程序中既包含host程序,又包含device程序,它們分別在CPU和GPU上運(yùn)行。同時(shí),host與device之間可以進(jìn)行通信,這樣它們之間可以進(jìn)行數(shù)據(jù)拷貝。典型的CUDA程序的執(zhí)行流程如下:

  1. 分配host內(nèi)存,并進(jìn)行數(shù)據(jù)初始化;

  2. 分配device內(nèi)存,并從host將數(shù)據(jù)拷貝到device上;

  3. 調(diào)用CUDA的核函數(shù)在device上完成指定的運(yùn)算;

  4. 將device上的運(yùn)算結(jié)果拷貝到host上;

  5. 釋放device和host上分配的內(nèi)存。

上面流程中最重要的一個(gè)過程是調(diào)用CUDA的核函數(shù)來執(zhí)行并行計(jì)算,kernel(http://docs./cuda/cuda-c-programming-guide/index.html#kernels)是CUDA中一個(gè)重要的概念,kernel是在device上線程中并行執(zhí)行的函數(shù),核函數(shù)用__global__符號(hào)聲明,在調(diào)用時(shí)需要用<<<grid, block>>>來指定kernel要執(zhí)行的線程數(shù)量,在CUDA中,每一個(gè)線程都要執(zhí)行核函數(shù),并且每個(gè)線程會(huì)分配一個(gè)唯一的線程號(hào)thread ID,這個(gè)ID值可以通過核函數(shù)的內(nèi)置變量threadIdx來獲得。

由于GPU實(shí)際上是異構(gòu)模型,所以需要區(qū)分host和device上的代碼,在CUDA中是通過函數(shù)類型限定詞開區(qū)別host和device上的函數(shù),主要的三個(gè)函數(shù)類型限定詞如下:

  • __global__:在device上執(zhí)行,從host中調(diào)用(一些特定的GPU也可以從device上調(diào)用),返回類型必須是void,不支持可變參數(shù)參數(shù),不能成為類成員函數(shù)。注意用__global__定義的kernel是異步的,這意味著host不會(huì)等待kernel執(zhí)行完就執(zhí)行下一步。

  • __device__:在device上執(zhí)行,僅可以從device中調(diào)用,不可以和__global__同時(shí)用。

  • __host__:在host上執(zhí)行,僅可以從host上調(diào)用,一般省略不寫,不可以和__global__同時(shí)用,但可和__device__,此時(shí)函數(shù)會(huì)在device和host都編譯。

要深刻理解kernel,必須要對(duì)kernel的線程層次結(jié)構(gòu)有一個(gè)清晰的認(rèn)識(shí)。首先GPU上很多并行化的輕量級(jí)線程。kernel在device上執(zhí)行時(shí)實(shí)際上是啟動(dòng)很多線程,一個(gè)kernel所啟動(dòng)的所有線程稱為一個(gè)網(wǎng)格(grid),同一個(gè)網(wǎng)格上的線程共享相同的全局內(nèi)存空間,grid是線程結(jié)構(gòu)的第一層次,而網(wǎng)格又可以分為很多線程塊(block),一個(gè)線程塊里面包含很多線程,這是第二個(gè)層次。線程兩層組織結(jié)構(gòu)如下圖所示,這是一個(gè)gird和block均為2-dim的線程組織。grid和block都是定義為dim3類型的變量,dim3可以看成是包含三個(gè)無符號(hào)整數(shù)(x,y,z)成員的結(jié)構(gòu)體變量,在定義時(shí),缺省值初始化為1。因此grid和block可以靈活地定義為1-dim,2-dim以及3-dim結(jié)構(gòu),對(duì)于圖中結(jié)構(gòu)(主要水平方向?yàn)閤軸),定義的grid和block如下所示,kernel在調(diào)用時(shí)也必須通過執(zhí)行配置(http://docs./cuda/cuda-c-programming-guide/index.html#execution-configuration)<<<grid, block>>>來指定kernel所使用的線程數(shù)及結(jié)構(gòu)。

 dim3 grid(3, 2); dim3 block(4, 3); kernel_fun<<< grid, block >>>(prams...);

所以,一個(gè)線程需要兩個(gè)內(nèi)置的坐標(biāo)變量(blockIdx,threadIdx)來唯一標(biāo)識(shí),它們都是dim3類型變量,其中blockIdx指明線程所在grid中的位置,而threaIdx指明線程所在block中的位置,如圖中的Thread (1,1)滿足:

threadIdx.x = 1threadIdx.y = 1blockIdx.x = 1blockIdx.y = 1

一個(gè)線程塊上的線程是放在同一個(gè)流式多處理器(SM)上的,但是單個(gè)SM的資源有限,這導(dǎo)致線程塊中的線程數(shù)是有限制的,現(xiàn)代GPUs的線程塊可支持的線程數(shù)可達(dá)1024個(gè)。有時(shí)候,我們要知道一個(gè)線程在blcok中的全局ID,此時(shí)就必須還要知道block的組織結(jié)構(gòu),這是通過線程的內(nèi)置變量blockDim來獲得。它獲取線程塊各個(gè)維度的大小。對(duì)于一個(gè)2-dim的block,線程(x,y)的ID值為,如果是3-dim的block,線程(x,y,z)的ID值為。另外線程還有內(nèi)置變量gridDim,用于獲得網(wǎng)格塊各個(gè)維度的大小。

kernel的這種線程組織結(jié)構(gòu)天然適合vector,matrix等運(yùn)算,如我們將利用上圖2-dim結(jié)構(gòu)實(shí)現(xiàn)兩個(gè)矩陣的加法,每個(gè)線程負(fù)責(zé)處理每個(gè)位置的兩個(gè)元素相加,代碼如下所示。線程塊大小為(16, 16),然后將N*N大小的矩陣均分為不同的線程塊來執(zhí)行加法運(yùn)算。

// Kernel定義

__global__ void MatAdd(float A[N][N], float B[N][N], float C[N][N])

{

int i = blockIdx.x * blockDim.x + threadIdx.x;

int j = blockIdx.y * blockDim.y + threadIdx.y;

if (i < N && j < N)

C[i][j] = A[i][j] + B[i][j];

}

int main()

{ ...

// Kernel 線程配置

dim3 threadsPerBlock(16, 16);

dim3 numBlocks(N / threadsPerBlock.x, N / threadsPerBlock.y);

// kernel調(diào)用

MatAdd<<<numBlocks, threadsPerBlock>>>(A, B, C);

...

}

此外這里簡(jiǎn)單介紹一下CUDA的內(nèi)存模型,如下圖所示??梢钥吹?,每個(gè)線程有自己的私有本地內(nèi)存(Local Memory),而每個(gè)線程塊有包含共享內(nèi)存(Shared Memory),可以被線程塊中所有線程共享,其生命周期與線程塊一致。此外,所有的線程都可以訪問全局內(nèi)存(Global Memory)。還可以訪問一些只讀內(nèi)存塊:常量?jī)?nèi)存(Constant Memory)和紋理內(nèi)存(Texture Memory)。內(nèi)存結(jié)構(gòu)涉及到程序優(yōu)化,這里不深入探討它們。

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

CUDA內(nèi)存模型

還有重要一點(diǎn),你需要對(duì)GPU的硬件實(shí)現(xiàn)有一個(gè)基本的認(rèn)識(shí)。上面說到了kernel的線程組織層次,那么一個(gè)kernel實(shí)際上會(huì)啟動(dòng)很多線程,這些線程是邏輯上并行的,但是在物理層卻并不一定。這其實(shí)和CPU的多線程有類似之處,多線程如果沒有多核支持,在物理層也是無法實(shí)現(xiàn)并行的。但是好在GPU存在很多CUDA核心,充分利用CUDA核心可以充分發(fā)揮GPU的并行計(jì)算能力。GPU硬件的一個(gè)核心組件是SM,前面已經(jīng)說過,SM是英文名是 Streaming Multiprocessor,翻譯過來就是流式多處理器。SM的核心組件包括CUDA核心,共享內(nèi)存,寄存器等,SM可以并發(fā)地執(zhí)行數(shù)百個(gè)線程,并發(fā)能力就取決于SM所擁有的資源數(shù)。當(dāng)一個(gè)kernel被執(zhí)行時(shí),它的gird中的線程塊被分配到SM上,一個(gè)線程塊只能在一個(gè)SM上被調(diào)度。SM一般可以調(diào)度多個(gè)線程塊,這要看SM本身的能力。那么有可能一個(gè)kernel的各個(gè)線程塊被分配多個(gè)SM,所以grid只是邏輯層,而SM才是執(zhí)行的物理層。SM采用的是SIMT(鏈接:http://docs./cuda/cuda-c-programming-guide/index.html#simt-architecture)(Single-Instruction, Multiple-Thread,單指令多線程)架構(gòu),基本的執(zhí)行單元是線程束(wraps),線程束包含32個(gè)線程,這些線程同時(shí)執(zhí)行相同的指令,但是每個(gè)線程都包含自己的指令地址計(jì)數(shù)器和寄存器狀態(tài),也有自己獨(dú)立的執(zhí)行路徑。所以盡管線程束中的線程同時(shí)從同一程序地址執(zhí)行,但是可能具有不同的行為,比如遇到了分支結(jié)構(gòu),一些線程可能進(jìn)入這個(gè)分支,但是另外一些有可能不執(zhí)行,它們只能死等,因?yàn)镚PU規(guī)定線程束中所有線程在同一周期執(zhí)行相同的指令,線程束分化會(huì)導(dǎo)致性能下降。當(dāng)線程塊被劃分到某個(gè)SM上時(shí),它將進(jìn)一步劃分為多個(gè)線程束,因?yàn)檫@才是SM的基本執(zhí)行單元,但是一個(gè)SM同時(shí)并發(fā)的線程束數(shù)是有限的。這是因?yàn)橘Y源限制,SM要為每個(gè)線程塊分配共享內(nèi)存,而也要為每個(gè)線程束中的線程分配獨(dú)立的寄存器。所以SM的配置會(huì)影響其所支持的線程塊和線程束并發(fā)數(shù)量??傊褪蔷W(wǎng)格和線程塊只是邏輯劃分,一個(gè)kernel的所有線程其實(shí)在物理層是不一定同時(shí)并發(fā)的。所以kernel的grid和block的配置不同,性能會(huì)出現(xiàn)差異,這點(diǎn)是要特別注意的。還有,由于SM的基本執(zhí)行單元是包含32個(gè)線程的線程束,所以block大小一般要設(shè)置為32的倍數(shù)。

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

CUDA編程的邏輯層和物理層

在進(jìn)行CUDA編程前,可以先檢查一下自己的GPU的硬件配置,這樣才可以有的放矢,可以通過下面的程序獲得GPU的配置屬性:

int dev = 0;

cudaDeviceProp devProp;

CHECK(cudaGetDeviceProperties(&devProp, dev));

std::cout << "使用GPU device " << dev << ": " << devProp.name << std::endl;

std::cout << "SM的數(shù)量:" << devProp.multiProcessorCount << std::endl;

std::cout << "每個(gè)線程塊的共享內(nèi)存大?。? << devProp.sharedMemPerBlock / 1024.0 << " KB" << std::endl;

std::cout << "每個(gè)線程塊的最大線程數(shù):" << devProp.maxThreadsPerBlock << std::endl;

std::cout << "每個(gè)EM的最大線程數(shù):" << devProp.maxThreadsPerMultiProcessor << std::endl;

std::cout << "每個(gè)EM的最大線程束數(shù):" << devProp.maxThreadsPerMultiProcessor / 32 << std::endl;

// 輸出如下

使用GPU device 0: GeForce GT 730

SM的數(shù)量:2

每個(gè)線程塊的共享內(nèi)存大小:48 KB

每個(gè)線程塊的最大線程數(shù):1024

每個(gè)EM的最大線程數(shù):2048

每個(gè)EM的最大線程束數(shù):64

好吧,GT 730顯卡確實(shí)有點(diǎn)渣,只有2個(gè)SM,嗚嗚......

2、向量加法實(shí)例

知道了CUDA編程基礎(chǔ),我們就來個(gè)簡(jiǎn)單的實(shí)戰(zhàn),利用CUDA編程實(shí)現(xiàn)兩個(gè)向量的加法,在實(shí)現(xiàn)之前,先簡(jiǎn)單介紹一下CUDA編程中內(nèi)存管理API。首先是在device上分配內(nèi)存的cudaMalloc函數(shù):

cudaError_t cudaMalloc(void** devPtr, size_t size);

這個(gè)函數(shù)和C語言中的malloc類似,但是在device上申請(qǐng)一定字節(jié)大小的顯存,其中devPtr是指向所分配內(nèi)存的指針。同時(shí)要釋放分配的內(nèi)存使用cudaFree函數(shù),這和C語言中的free函數(shù)對(duì)應(yīng)。另外一個(gè)重要的函數(shù)是負(fù)責(zé)host和device之間數(shù)據(jù)通信的cudaMemcpy函數(shù):

cudaError_t cudaMalloc(void** devPtr, size_t size);

其中src指向數(shù)據(jù)源,而dst是目標(biāo)區(qū)域,count是復(fù)制的字節(jié)數(shù),其中kind控制復(fù)制的方向:cudaMemcpyHostToHost, cudaMemcpyHostToDevice,

cudaMemcpyDeviceToHost及cudaMemcpyDeviceToDevice,如cudaMemcpyHostToDevice將host上數(shù)據(jù)拷貝到device上。

現(xiàn)在我們來實(shí)現(xiàn)一個(gè)向量加法的實(shí)例,這里grid和block都設(shè)計(jì)為1-dim,首先定義kernel如下:

// 兩個(gè)向量加法kernel,grid和block均為一維

__global__ void add(float* x, float * y, float* z, int n)

{

// 獲取全局索引

int index = threadIdx.x + blockIdx.x * blockDim.x;

// 步長(zhǎng)

int stride = blockDim.x * gridDim.x;

for (int i = index; i < n; i += stride)

{

z[i] = x[i] + y[i];

}

}

其中stride是整個(gè)grid的線程數(shù),有時(shí)候向量的元素?cái)?shù)很多,這時(shí)候可以將在每個(gè)線程實(shí)現(xiàn)多個(gè)元素(元素總數(shù)/線程總數(shù))的加法,相當(dāng)于使用了多個(gè)grid來處理,這是一種grid-stride loop(鏈接:https://devblogs./cuda-pro-tip-write-flexible-kernels-grid-stride-loops/)方式,不過下面的例子一個(gè)線程只處理一個(gè)元素,所以kernel里面的循環(huán)是不執(zhí)行的。下面我們具體實(shí)現(xiàn)向量加法:

int main()

{

int N = 1 << 20;

int nBytes = N * sizeof(float);

// 申請(qǐng)host內(nèi)存

float *x, *y, *z;

x = (float*)malloc(nBytes);

y = (float*)malloc(nBytes);

z = (float*)malloc(nBytes);

// 初始化數(shù)據(jù)

for (int i = 0; i < N; ++i)

{

x[i] = 10.0;

y[i] = 20.0;

}

// 申請(qǐng)device內(nèi)存

float *d_x, *d_y, *d_z;

cudaMalloc((void**)&d_x, nBytes);

cudaMalloc((void**)&d_y, nBytes);

cudaMalloc((void**)&d_z, nBytes);

// 將host數(shù)據(jù)拷貝到device

cudaMemcpy((void*)d_x, (void*)x, nBytes, cudaMemcpyHostToDevice);

cudaMemcpy((void*)d_y, (void*)y, nBytes, cudaMemcpyHostToDevice);

// 定義kernel的執(zhí)行配置

dim3 blockSize(256);

dim3 gridSize((N + blockSize.x - 1) / blockSize.x);

// 執(zhí)行kernel

add << < gridSize, blockSize >> >(d_x, d_y, d_z, N);

// 將device得到的結(jié)果拷貝到host

cudaMemcpy((void*)z, (void*)d_z, nBytes, cudaMemcpyHostToDevice);

// 檢查執(zhí)行結(jié)果

float maxError = 0.0;

for (int i = 0; i < N; i++)

maxError = fmax(maxError, fabs(z[i] - 30.0));

std::cout << "最大誤差: " << maxError << std::endl;

// 釋放device內(nèi)存

cudaFree(d_x);

cudaFree(d_y);

cudaFree(d_z);

// 釋放host內(nèi)存

free(x);

free(y);

free(z);

return0;

}

這里我們的向量大小為1<<20,而block大小為256,那么grid大小是4096,kernel的線程層級(jí)結(jié)構(gòu)如下圖所示:

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

kernel的線程層次結(jié)構(gòu). 來源:https://devblogs./even-easier-introduction-cuda/

使用nvprof工具可以分析kernel運(yùn)行情況,結(jié)果如下所示,可以看到kernel函數(shù)費(fèi)時(shí)約1.5ms。

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

你調(diào)整block的大小,對(duì)比不同配置下的kernel運(yùn)行情況,我這里測(cè)試的是當(dāng)block為128時(shí),kernel費(fèi)時(shí)約1.6ms,而block為512時(shí)kernel費(fèi)時(shí)約1.7ms,當(dāng)block為64時(shí),kernel費(fèi)時(shí)約2.3ms??磥聿皇莃lock越大越好,而要適當(dāng)選擇。

在上面的實(shí)現(xiàn)中,我們需要單獨(dú)在host和device上進(jìn)行內(nèi)存分配,并且要進(jìn)行數(shù)據(jù)拷貝,這是很容易出錯(cuò)的。好在CUDA 6.0引入統(tǒng)一內(nèi)存(Unified Memory)(鏈接:http://docs./cuda/cuda-c-programming-guide/index.html#um-unified-memory-programming-hd)來避免這種麻煩,簡(jiǎn)單來說就是統(tǒng)一內(nèi)存使用一個(gè)托管內(nèi)存來共同管理host和device中的內(nèi)存,并且自動(dòng)在host和device中進(jìn)行數(shù)據(jù)傳輸。CUDA中使用cudaMallocManaged函數(shù)分配托管內(nèi)存:

cudaError_t cudaMallocManaged(void **devPtr, size_t size, unsigned int flag=0);

利用統(tǒng)一內(nèi)存,可以將上面的程序簡(jiǎn)化如下:

int main()

{

int N = 1 << 20;

int nBytes = N * sizeof(float);

// 申請(qǐng)托管內(nèi)存

float *x, *y, *z;

cudaMallocManaged((void**)&x, nBytes);

cudaMallocManaged((void**)&y, nBytes);

cudaMallocManaged((void**)&z, nBytes);

// 初始化數(shù)據(jù)

for (int i = 0; i < N; ++i)

{

x[i] = 10.0;

y[i] = 20.0;

}

// 定義kernel的執(zhí)行配置

dim3 blockSize(256);

dim3 gridSize((N + blockSize.x - 1) / blockSize.x);

// 執(zhí)行kernel

add << < gridSize, blockSize >> >(x, y, z, N);

// 同步device 保證結(jié)果能正確訪問

cudaDeviceSynchronize();

// 檢查執(zhí)行結(jié)果

float maxError = 0.0;

for (int i = 0; i < N; i++)

maxError = fmax(maxError, fabs(z[i] - 30.0));

std::cout << "最大誤差: " << maxError << std::endl;

// 釋放內(nèi)存

cudaFree(x);

cudaFree(y);

cudaFree(z);

return0;

}

相比之前的代碼,使用統(tǒng)一內(nèi)存更簡(jiǎn)潔了,值得注意的是kernel執(zhí)行是與host異步的,由于托管內(nèi)存自動(dòng)進(jìn)行數(shù)據(jù)傳輸,這里要用cudaDeviceSynchronize()函數(shù)保證device和host同步,這樣后面才可以正確訪問kernel計(jì)算的結(jié)果。

3、矩陣乘法實(shí)例

最后我們?cè)賹?shí)現(xiàn)一個(gè)稍微復(fù)雜一些的例子,就是兩個(gè)矩陣的乘法,設(shè)輸入矩陣為A和B,要得到C=A*B。實(shí)現(xiàn)思路是每個(gè)線程計(jì)算C的一個(gè)元素值:

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

,對(duì)于矩陣運(yùn)算,應(yīng)該選用grid和block為2-D的。首先定義矩陣的結(jié)構(gòu)體:

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

矩陣乘法實(shí)現(xiàn)模式

然后實(shí)現(xiàn)矩陣乘法的核函數(shù),這里我們定義了兩個(gè)輔助的__device__函數(shù)分別用于獲取矩陣的元素值和為矩陣元素賦值,具體代碼如下:

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

最后我們采用統(tǒng)一內(nèi)存編寫矩陣相乘的測(cè)試實(shí)例:

int main()

{

int width = 1 << 10;

int height = 1 << 10;

Matrix A(width, height, NULL);

Matrix B(width, height, NULL);

Matrix C(width, height, NULL);

int nBytes = width * height * sizeof(float);

// 申請(qǐng)托管內(nèi)存

cudaMallocManaged((void**)&A.elements, nBytes);

cudaMallocManaged((void**)&B.elements, nBytes);

cudaMallocManaged((void**)&C.elements, nBytes);

// 初始化數(shù)據(jù)

for (int i = 0; i < width * height; ++i)

{

A.elements[i] = 1.0;

B.elements[i] = 2.0;

}

// 定義kernel的執(zhí)行配置

dim3 blockSize(32, 32);

dim3 gridSize((width + blockSize.x - 1) / blockSize.x,

(height + blockSize.y - 1) / blockSize.y);

// 執(zhí)行kernel

matMulKernel << < gridSize, blockSize >> >(A, B, C);

// 同步device 保證結(jié)果能正確訪問

cudaDeviceSynchronize();

// 檢查執(zhí)行結(jié)果

float maxError = 0.0;

for (int i = 0; i < width * height; i++)

maxError = fmax(maxError, fabs(C.elements[i] - 2 * width));

std::cout << C.elements[0] << std::endl;

std::cout << "最大誤差: " << maxError << std::endl;

return0;

}

這里矩陣大小為1024*1024,設(shè)計(jì)的線程的block大小為(32, 32),那么grid大小為(32, 32),最終測(cè)試結(jié)果如下:

快來操縱你的GPU| CUDA編程入門極簡(jiǎn)教程

當(dāng)然,這不是最高效的實(shí)現(xiàn),后面可以繼續(xù)優(yōu)化...

小結(jié)

最后只有一句話:CUDA入門容易,但是深入難!希望不是從入門到放棄.

參考資料

  1. John Cheng, Max Grossman, Ty McKercher. Professional CUDA C Programming, 2014.(http://www./WileyCDA/WroxTitle/Professional-CUDA-C-Programming.productCd-1118739329.html)

  2. CUDA docs.(http://docs./cuda/)

  3. An Even Easier Introduction to CUDA(https://devblogs./even-easier-introduction-cuda/)

加燕哥微信:TonyJeemy520,拉你進(jìn)算法愛好者群共同學(xué)習(xí)

下載學(xué)習(xí)資料,可以加QQ群:651616387

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多