第一个CUDA程式

第一�� CUDA 程式
发表时间:2009-01-09
CUDA 目前有�煞N不同的 API:Runtime API 和 Driver API,�煞N API 各有其�m用的���。由於 runtime API �^容易使用,一�_始我����以 runetime API �橹鳌�
CUDA 目前有�煞N不同的 API:Runtime API 和 Driver API,�煞N API 各有其�m用的���。由於 runtime API �^容易使用,一�_始我����以 runetime API �橹鳌�
CUDA 的初始化
首先,先建立一���n案 first_cuda.cu。如果是使用 Visual Studio 的�,�t�先按照 �@�e 的�O定方式�O定 project。
要使用 runtime API 的�r候,需要 include cuda_runtime.h。所以,在程式的最前面,加上
#include <stdio.h>
#include <cuda_runtime.h>
接下�硎且�� InitCUDA 函式,��呼叫 runtime API 中,有�P初始化 CUDA 的功能:
bool InitCUDA()
{
    int count;
    cudaGetDeviceCount(&count);
    if(count == 0) {
        fprintf(stderr, "There is no device.\n");
        return false;
    }
    int i;
    for(i = 0; i < count; i++) {
        cudaDeviceProp prop;
        if(cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
            if(prop.major >= 1) {
                break;
            }
        }
    }
    if(i == count) {
        fprintf(stderr, "There is no device supporting CUDA 1.x.\n");
        return false;
    }
    cudaSetDevice(i);
    return true;
}
�@��函式��先呼叫 cudaGetDeviceCount 函式,取得支援 CUDA 的�b置的�的俊H绻�系�y上�]有支援 CUDA 的�b置,�t它���骰� 1,而 device 0 ��是一��模�M的�b置,但不支援 CUDA 1.0 以上的功能。所以,要�_定系�y上是否有支援 CUDA 的�b置,需要�γ�� device 呼叫 cudaGetDeviceProperties 函式,取得�b置的各��Y料,�K判�嘌b置支援的 CUDA 版本(prop.major 和 prop.minor 分�e代表�b置支援的版本��a,例如 1.0 �t prop.major �� 1 而 prop.minor �� 0)。
透�^ cudaGetDeviceProperties 函式可以取得�S多�Y料,除了�b置支援的 CUDA 版本之外,�有�b置的名�Q、����w的大小、最大的 thread �的俊�绦�卧�的�r�}等等。�情可�⒖� NVIDIA 的 CUDA Programming Guide。
在找到支援 CUDA 1.0 以上的�b置之後,就可以呼叫 cudaSetDevice 函式,把它�O�槟壳耙�使用的�b置。
最後是 main 函式。在 main 函式中我��直接呼叫��才的 InitCUDA 函式,�K�@示�m��的�息:
int main()
{
    if(!InitCUDA()) {
        return 0;
    }
    printf("CUDA initialized.\n");
    return 0;
}
�@�泳涂梢岳�用 nvcc �� compile �@��程式了。使用 Visual Studio 的�,若按照先前的�O定方式,可以直接 Build Project �K�绦小�
nvcc 是 CUDA 的 compile 工具,它���� .cu �n拆解出在 GPU 上�绦械牟糠荩�及在 host 上�绦械牟糠荩��K呼叫�m��的程式�M行 compile �幼鳌T� GPU �绦械牟糠��透�^ NVIDIA 提供的 compiler ��g成中介�a,而 host �绦械牟糠�t��透�^系�y上的 C++ compiler ��g(在 Windows 上使用 Visual C++ 而在 Linux 上使用 gcc)。
��g後的程式,�绦�r如果系�y上有支援 CUDA 的�b置,������@示 CUDA initialized. 的�息,否�t���@示相�P的�e�`�息。
利用 CUDA �M行�\算
到目前�橹梗�我��的程式�K�]有做什�N有用的工作。所以,�F在我��加入一����蔚�幼鳎�就是把一大堆�底郑��算出它的平方和。
首先,把程式最前面的 include 部份改成:
#include <stdio.h>
#include <stdlib.h>
#include <cuda_runtime.h>
#define DATA_SIZE 1048576
int data[DATA_SIZE];
�K加入一��新函式 GenerateNumbers:
void GenerateNumbers(int *number, int size)
{
    for(int i = 0; i < size; i++) {
        number[i] = rand() % 10;
    }
}
�@��函式���a生一大堆 0 ~ 9 之�g的�y�怠�
要利用 CUDA �M行�算之前,要先把�Y料�}�u到�@示����w中,才能��@示晶片使用。因此,需要取得一�K�m��大小的�@示����w,再把�a生好的�Y料�}�u�M去。在 main 函式中加入:
    GenerateNumbers(data, DATA_SIZE);
    int* gpudata, *result;
    cudaMalloc((void**) &gpudata, sizeof(int) * DATA_SIZE);
    cudaMalloc((void**) &result, sizeof(int));
    cudaMemcpy(gpudata, data, sizeof(int) * DATA_SIZE,
        cudaMemcpyHostToDevice);
上面�@段程式��先呼叫 GenerateNumbers �a生�y�担��K呼叫 cudaMalloc 取得一�K�@示����w(result �t是用�泶嫒∮�算�Y果,在稍後��用到),�K透�^ cudaMemcpy ��a生的�y�笛}�u到�@示����w中。cudaMalloc 和 cudaMemcpy 的用法和一般的 malloc 及 memcpy �似,不�^ cudaMemcpy �t多出一����担�指示�}�u����w的方向。在�@�e因�槭�闹饔����w�}�u到�@示����w,所以使用 cudaMemcpyHostToDevice。如果是�娘@示����w到主����w,�t使用 cudaMemcpyDeviceToHost。�@在之後��用到。
接下�硎且���在�@示晶片上�绦械某淌健T� CUDA 中,在函式前面加上 __global__ 表示�@��函式是要在�@示晶片上�绦械摹R虼耍�加入以下的函式:
__global__ static void sumOfSquares(int *num, int* result)
{
    int sum = 0;
    int i;
    for(i = 0; i < DATA_SIZE; i++) {
        sum += num[i] * num[i];
    }
    *result = sum;
}
在�@示晶片上�绦械某淌接幸恍┫拗疲�例如它不能有�骰刂怠F渌�的限制��在之後提到。
接下�硎且�� CUDA �绦羞@��函式。在 CUDA 中,要�绦幸��函式,使用以下的�Z法:
    函式名�Q<<<block �的�, thread �的�, shared memory 大小>>>(���...);
呼叫完後,�要把�Y果�娘@示晶片�}�u回主����w上。在 main 函式中加入以下的程式:
    sumOfSquares<<<1, 1, 0>>>(gpudata, result);
    int sum;
    cudaMemcpy(&sum, result, sizeof(int), cudaMemcpyDeviceToHost);
    cudaFree(gpudata);
    cudaFree(result);
    printf("sum: %d\n", sum);
因�檫@��程式只使用一�� thread,所以 block �的俊�thread �的慷际� 1。我��也�]有使用到任何 shared memory,所以�O�� 0。��g後�绦校����可以看到�绦械慕Y果。
�榱舜_定�绦械慕Y果正�_,我��可以加上一段以 CPU �绦械某淌酱a,�眚��C�Y果:
    sum = 0;
    for(int i = 0; i < DATA_SIZE; i++) {
        sum += data[i] * data[i];
    }
    printf("sum (CPU): %d\n", sum);
��g後�绦校��_�J����Y果相同。
�算�绦�r�g
CUDA 提供了一�� clock 函式,可以取得目前的 timestamp,很�m合用�砼�嘁欢纬淌�绦兴�花�M的�r�g(�挝�� GPU �绦�卧�的�r�})。�@�Τ淌降淖罴鸦�也相��有用。要在我��的程式中���r�g,把 sumOfSquares 函式改成:
__global__ static void sumOfSquares(int *num, int* result,
    clock_t* time)
{
    int sum = 0;
    int i;
    clock_t start = clock();
    for(i = 0; i < DATA_SIZE; i++) {
        sum += num[i] * num[i];
    }
    *result = sum;
    *time = clock() - start;
}
把 main 函式中�g部份改成:
    int* gpudata, *result;
    clock_t* time;
    cudaMalloc((void**) &gpudata, sizeof(int) * DATA_SIZE);
    cudaMalloc((void**) &result, sizeof(int));
    cudaMalloc((void**) &time, sizeof(clock_t));
    cudaMemcpy(gpudata, data, sizeof(int) * DATA_SIZE,
        cudaMemcpyHostToDevice);
sumOfSquares<<<1, 1, 0>>>(gpudata, result, time);
    int sum;
    clock_t time_used;
    cudaMemcpy(&sum, result, sizeof(int), cudaMemcpyDeviceToHost);
    cudaMemcpy(&time_used, time, sizeof(clock_t),
        cudaMemcpyDeviceToHost);
    cudaFree(gpudata);
    cudaFree(result);
    printf("sum: %d time: %d\n", sum, time_used);
��g後�绦校�就可以看到�绦兴�花�M的�r�g了。
如果�算���H�绦�r�g的�,可能��注意到它的�绦行�率�K不好。�@是因�槲��的程式�K�]有利用到 CUDA 的主要的���荩�即平行化�绦小T谙乱欢挝恼轮校�����如何�M行最佳化的�幼鳌�
原文

你可能感兴趣的:(CUDA,职场,休闲,程式)