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語言的malloc
、mencpy
、free
API等。我們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語言,但最大的問題就是如何協成合作解決一個問題。這就是線程合作的重要性。通過下面的學習,你將會:
- 學會
CUDA C
喚醒線程 - 學會不同線程之間的通信
- 學會在不同線程之間的通過機制
在之前的例題中,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編程,如有不對,歡迎吐槽。。。