cuda學習

CUDA學習系列

首先我們從最簡單的hello開始

cuda是英偉達公司提出的一種並行計算框架,在大數據的今天,數據計算顯得尤其重要,尤其是機器學習領域的深度學習,更是離不開高效率的並行計算,如果說使用一般的cpu進行計算需要幾天,而使用gpu計算僅僅需要幾個小時!!!可見並行計算的前途無量。

以前接觸過一段時間cuda,現在需要做筆記,將學習的歷程記錄下來,以便以後來參考,也給新人一點幫助,歡迎吐槽。。。首先是“hello world”…

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

__global__ void Kernel(void)
{

}

int main()
{
    Kernel << <1, 1 >> >();

    printf("hello world");

return 0;
}

首先使用__global__聲明一個類型,表示該函數應該在設備也就是顯卡處編譯,爲什麼會這麼奇怪,我也沒有看出來。。。

簡單加法

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>



__global__ void add(int a, int b,int *c)
{
    *c = a + b; //實現加法
}

int main()
{
    int c;
    int *dev_c;

    cudaMalloc((void**)&dev_c, sizeof(int)); //在顯卡處分配內存空間

    add << <1, 1 >> >(1,2,dev_c); //調用函數

    cudaMemcpy(&c, dev_c, sizeof(int), cudaMemcpyDeviceToHost); //將顯卡的結果copy到本地

    printf("1+2=%d\n", c);

    cudaFree(dev_c); //釋放顯卡內存

    return 0;
}

如果我們想知道一臺計算機上gpu的個數,可以使用下面的代碼:

int count;

cudaGetDeviceCount(&count);

printf("%d", count);

比如在我的電腦上就是1個,gt635M。

在cuda中,有一個結構體專門來記錄顯卡的信息的,就是cudaDeviceProp,使用方法如下:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>

int main()
{   

    cudaDeviceProp prop;

    cudaGetDeviceProperties(&prop, 0);

    printf("Name  %s\n", prop.name);

    printf("Computer capability:%d.%d\n", prop.major, prop.minor);

    printf("%d\n", prop.totalGlobalMem/(1024*1024));

    printf("%f\n", prop.totalConstMem/(1024.0));

    return 0;
}

當然還有很多的信息,需要的話,藉助於cuda文檔了。

總結

通過前面的學習我們學會了使用關鍵字__global__來表示在gpu上執行。也學會了類似於c語言的mallocmencpyfreeAPI等。我們gpu計算已經入門,下面我們來學習高效使用gpu進行計算。

並行編程

首先我們來看一下最簡單的向量加法運算,C實現:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>


#define N 10

void add(int *a, int *b, int *c)
{
    int tid = 0;
    while (tid < N){
        c[tid] = a[tid] + b[tid];
        tid++;
    }
}

int main()
{   
    int a[N], b[N], c[N];
    for (int i = 0; i < N; i++){
        a[i] = -i;
        b[i] = i*i;
    }
    add(a, b, c);

    for (int i = 0; i < N; i++)
    {
        printf("%d + %d = %d\n", a[i], b[i], c[i]);
    }

    return 0;
}

使用cuda c的並行計算代碼如下:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
#define N 10

__global__ void add(int *a, int *b, int *c)
{
    int tid = blockIdx.x;
    if (tid < N)
    {
        c[tid] = a[tid] + b[tid];
    }
}

int main()
{   
    int a[N], b[N], c[N];
    int *dev_a, *dev_b, *dev_c;

    cudaMalloc((void**)&dev_a, N*sizeof(int));
    cudaMalloc((void**)&dev_b, N*sizeof(int));
    cudaMalloc((void**)&dev_c, N*sizeof(int));

    for (int i = 0; i < N; i++)
    {
        a[i] = -i;
        b[i] = i*i;
    }

    cudaMemcpy(dev_a, a, N*sizeof(int), cudaMemcpyHostToDevice);
    cudaMemcpy(dev_b, b, N*sizeof(int), cudaMemcpyHostToDevice);

    add << <N, 1 >> >(dev_a,dev_b,dev_c);


    cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost);

    for (int i = 0; i < 10; i++)
    {
        printf("%d+%d=%d\n", a[i], b[i], c[i]);
    }

    cudaFree(dev_a);
    cudaFree(dev_b);
    cudaFree(dev_c);


    return 0;
}

這段代碼與我們之前的不一樣,彆着急,慢慢道來。

首先是類似於kernel<<<N,1>>>,第一個數字表示需要gpu並行計算的塊,比如例子中的N=10,就是10個並行計算的塊。但是帶來一個問題,在一段時間內,哪一塊執行呢?這樣帶來了第二個問題。

這就是blockIdx.x的作用了。這個變量是集成在cuda框架中,表示了當前執行的塊。但爲什麼是blockIdx.x,而不是blockIdx

在cuda中,允許定義2維,如矩陣與圖像處理。而現在只需要一維,所以不要激動不懂。

線程合作

在之前的學習中,我們學會了設計並行計算的c語言,但最大的問題就是如何協成合作解決一個問題。這就是線程合作的重要性。通過下面的學習,你將會:

  1. 學會CUDA C喚醒線程
  2. 學會不同線程之間的通信
  3. 學會在不同線程之間的通過機制

在之前的例題中,add << <N, 1 >> >(dev_a,dev_b,dev_c)第二個參數表示在每一塊中(block)我們想創建線程的個數

example 向量加法

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>

#define N 10
__global__ void add(int *a, int *b, int *c)
{
    int tid = threadIdx.x;
    if (tid < N)
        c[tid] = a[tid] + b[tid];

}

int main()
{

    int a[N], b[N], c[N];
    int *dev_a, *dev_b, *dev_c;

    cudaMalloc((void**)&dev_a, N*sizeof(int));
    cudaMalloc((void**)&dev_b, N*sizeof(int));
    cudaMalloc((void**)&dev_c, N*sizeof(int));

    for (int i = 0; i < N; i++)
    {
        a[i] = -i;
        b[i] = i*i;
    }

    cudaMemcpy(dev_a, a, N*sizeof(int), cudaMemcpyHostToDevice);
    cudaMemcpy(dev_b, b, N*sizeof(int), cudaMemcpyHostToDevice);

    add << <N,1 >> >(dev_a, dev_b, dev_c);


    cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost);

    for (int i = 0; i < N; i++)
    {
        printf("%d+%d=%d\n", a[i], b[i], c[i]);
    }

    cudaFree(dev_a);
    cudaFree(dev_b);
    cudaFree(dev_c);


    return 0;
}

如果我們同時擁有多線程和多個塊,怎麼辦呢?

tid = threadIdx.x + blockIdx.x + blockDim.x;

這段代碼使用了新的內置變量 blockDim

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <stdio.h>

#define N (33*1024)

__global__ void add(int *a, int *b, int *c)
{
    int tid = threadIdx.x + blockIdx.x * blockDim.x;
    while (tid < N)
    {
        c[tid] = a[tid] + b[tid];
        tid += blockDim.x * gridDim.x;
    }
}

int main()
{


    int a[N], b[N], c[N];
    int *dev_a, *dev_b, *dev_c;

    cudaMalloc((void**)&dev_a, N*sizeof(int));
    cudaMalloc((void**)&dev_b, N*sizeof(int));
    cudaMalloc((void**)&dev_c, N*sizeof(int));

    for (int i = 0; i < N; i++)
    {
        a[i] = -i;
        b[i] = i*i;
    }

    cudaMemcpy(dev_a, a, N*sizeof(int), cudaMemcpyHostToDevice);
    cudaMemcpy(dev_b, b, N*sizeof(int), cudaMemcpyHostToDevice);

    add << <128,128 >> >(dev_a, dev_b, dev_c);


    cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost);


    bool success = true;



    for (int i = 0; i < N; i++)
    {
        if ((a[i] + b[i] != c[i]))
        {
            printf("%d + %d = %d\n", a[i], b[i], c[i]);
            success = false;
        }
    }

    if (success)
        printf("we did it");

    cudaFree(dev_a);
    cudaFree(dev_b);
    cudaFree(dev_c);

    return 0;
}

這就是使用了多線程多塊的並行計算模型。
寒假在家沒有事做,於是就學習了cuda c編程,如有不對,歡迎吐槽。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章