教你一步步寫一個cuda path tracer:cuda hello world

我們學任何一門語言和框架箱玷,都要從hello world學起怨规。cuda的hello world可以是:

#include <cstdio>
#include <algorithm>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>

__global__ void add(float *dx, float *dy)
{
    int id = blockIdx.x * blockDim.x + threadIdx.x;
    dy[id] += dx[id];
}

int main()
{
    int N = 1 << 20;

    float *hx, *hy, *dx, *dy;

    hx = new float[N];
    hy = new float[N];
    cudaMalloc(&dx, N * sizeof(float));
    cudaMalloc(&dy, N * sizeof(float));

    for (int i = 0; i < N; i++)
    {
        hx[i] = 1.0f;
        hy[i] = 2.0f;
    }

    cudaMemcpy(dx, hx, N * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(dy, hy, N * sizeof(float), cudaMemcpyHostToDevice);

    int threadNums = 256;
    int blockNums = (N + threadNums - 1) / threadNums;

    add << <blockNums, threadNums >> >(N, dx, dy);

    cudaDeviceSynchronize();

    cudaMemcpy(hy, dy, N * sizeof(float), cudaMemcpyDeviceToHost);

    float maxError = 0.0f;
    for (int i = 0; i < N; i++)
        maxError = std::max(maxError, std::abs(hy[i] - 3.0f));

    printf("Max error: %.6f", maxError);

    delete[] hx;
    delete[] hy;
    cudaFree(dx);
    cudaFree(dy);

    return 0;
}

首先要注意,這段代碼文件的后綴名是.cu锡足。cuda有兩種文件后綴名波丰,.cu和.cuh,相當于c++中.cpp和.h的關系舶得。

程序要做的就是兩個向量x掰烟、y的相加:y += x。向量長度為2^20沐批。下面我將逐行分析這段代碼:

1.引入c++頭文件

#include <cstdio>
#include <algorithm>

2.引入cuda頭文件

#include <cuda_runtime.h>
#include <device_launch_parameters.h>

3.向量相加核函數(shù)(kernel)

__global__ void add(float *dx, float *dy)
{
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    dy[idx] += dx[idx];
}

核函數(shù)是運行在gpu上的函數(shù)纫骑,能在很多線程上并行運行。

n是向量的長度九孩,dx和dy分別是指向向量的指針先馆。blockIdx和threadIdx分別是當前block和thread的下標,gridDim是block的數(shù)量捻撑,blockDim是一個block里thread的數(shù)量磨隘。blockIdx、threadIdx顾患、gridDim番捂、blockDim的關系如下圖所示(gridDim沒有畫出,grid和block的關系其實就相當于block和thread的關系):

block/thread關系.png

id定位到具體的thread下標江解,正好也等于數(shù)組的下標(每個線程對應一個數(shù)組的元素)设预。

4.main函數(shù)入口

int main()

5.初始化向量數(shù)據(jù)

int N = 1 << 20;

float *hx, *hy, *dx, *dy;

hx = new float[N];
hy = new float[N];
cudaMalloc(&dx, N * sizeof(float));
cudaMalloc(&dy, N * sizeof(float));

for (int i = 0; i < N; i++)
{
    hx[i] = 1.0f;
    hy[i] = 2.0f;
}

cudaMemcpy(dx, hx, N * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(dy, hy, N * sizeof(float), cudaMemcpyHostToDevice);

N為向量長度。hx犁河、hy為host鳖枕,即cpu端的數(shù)據(jù)魄梯;dx、dy為device宾符,即gpu端的數(shù)據(jù)酿秸。host數(shù)據(jù)使用new分配,device數(shù)據(jù)使用cudaMalloc分配魏烫。device數(shù)據(jù)分配完畢之后辣苏,需要使用cudaMemcpy將host數(shù)據(jù)拷貝到device數(shù)據(jù)上。

int threadNums = 256;
int blockNums = (N + threadNums - 1) / threadNums;

add << <blockNums, threadNums >> >(N, dx, dy);

threadNums指的是一個block中有多少thread哄褒,即上面add函數(shù)中的blockDim稀蟋;blockNums指的是block的數(shù)量,對應gridDim呐赡。

add << <blockNums, threadNums >> >(N, dx, dy);一句調(diào)用了add核函數(shù)退客,通過<<<>>>符號傳入核函數(shù)運行配置參數(shù)。核函數(shù)的運行配置參數(shù)的完整形式為<<<gridDim, blockDim, Ns, S>>>链嘀,其中Ns和S一般使用默認值萌狂。

gridDim和blockDim都是Dim類型,可以是Dim1怀泊、Dim2和Dim3粥脚,即可以是一維、二維和三維包个。gridDim每個維度最大可以是65535,blockDim的值最大可以是1024冤留。所以理論上我們可以配置的最大線程數(shù)量為6553565535655351024 = 2.91017個碧囊。需要注意的是,這里的線程指的是軟件的線程纤怒,硬件上真正能并行的線程數(shù)要遠遠小于這個天文數(shù)字糯而,一般來說只有104的數(shù)量級。

6.同步gpu線程

cudaDeviceSynchronize();

其實沒有必要泊窘,詳見stackoverflow的解釋熄驼。

7.計算并打印出最大的誤差值

float maxError = 0.0f;
for (int i = 0; i < N; i++)
    maxError = std::max(maxError, std::abs(hy[i] - 3.0f));

printf("Max error: %.6f", maxError);

8.釋放資源

delete[] hx;
delete[] hy;
cudaFree(dx);
cudaFree(dy);

最后打印的結(jié)果當然不出所料,是0烘豹。

我們可以寫一段對應的c++程序:

#include <cstdio>
#include <algorithm>

int main()
{
    int N = 1 << 20;
    float *x, *y;
    x = new float[N];
    y = new float[N];

    for (int i = 0; i < N; i++)
    {
        x[i] = 1.0f;
        y[i] = 2.0f;
    }

    for (int i = 0; i < N; i++)
    {
        y[i] += x[i];
    }

    float maxError = 0.0f;
    for (int i = 0; i < N; i++)
        maxError = std::max(maxError, std::abs(y[i] - 3.0f));

    printf("Max error: %.6f", maxError);

    delete[] x;
    delete[] y;

    return 0;
}

最后可以做一個簡單的profile(使用c++11的chrono計時器):

**cuda:

std::chrono::time_point<std::chrono::system_clock> begin = std::chrono::system_clock::now();

add << <blockNums, threadNums >> > (N, dx, dy);

std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsedTime = end - begin;
printf("Time: %.6lfs\n", elapsedTime.count());

結(jié)果:

cuda運行結(jié)果.png

c++:

std::chrono::time_point<std::chrono::system_clock> begin = std::chrono::system_clock::now();

for (int i = 0; i < N; i++)
{
    y[i] += x[i];
}

std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsedTime = end - begin;
printf("Time: %.6lfs\n", elapsedTime.count());

結(jié)果:

c++運行結(jié)果.png

可以看到瓜贾,在這個例子中,cuda比c++快了近兩個數(shù)量級携悯!

feel the power of cuda!

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末祭芦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子憔鬼,更是在濱河造成了極大的恐慌龟劲,老刑警劉巖胃夏,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異昌跌,居然都是意外死亡仰禀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門蚕愤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來答恶,“玉大人,你說我怎么就攤上這事审胸『ニ蓿” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵砂沛,是天一觀的道長烫扼。 經(jīng)常有香客問我,道長碍庵,這世上最難降的妖魔是什么映企? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮静浴,結(jié)果婚禮上堰氓,老公的妹妹穿的比我還像新娘。我一直安慰自己苹享,他們只是感情好双絮,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著得问,像睡著了一般囤攀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宫纬,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天焚挠,我揣著相機與錄音,去河邊找鬼漓骚。 笑死蝌衔,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蝌蹂。 我是一名探鬼主播噩斟,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼孤个!你這毒婦竟也來了亩冬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎硅急,沒想到半個月后覆享,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡营袜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年撒顿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荚板。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡凤壁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跪另,到底是詐尸還是另有隱情拧抖,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布免绿,位于F島的核電站唧席,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嘲驾。R本人自食惡果不足惜淌哟,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辽故。 院中可真熱鬧徒仓,春花似錦、人聲如沸誊垢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喂走。三九已至狰晚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缴啡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工瓷们, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留业栅,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓谬晕,卻偏偏與公主長得像碘裕,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子攒钳,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容