【CUDA编程】一:从简单的实例入门

前提

  • 拥有NVIDIA的显卡-
  • 配置好cuda
  • 有C/C++基础
  • Linux系统

基础知识

看看这篇吧CUDA基础知识
以后有空再自己写。
了解以下几个概念:
线程 thread
线程块 thread block
线程格 thread grid

代码1:简单加法

了解 如何创建GPU调用的函数
了解 一些基础函数

/*
* 文件名 helloworld.cu
* 编译: nvcc -o helloworld helloworld.cu
*/
#include 
#include 
/*
* __global__ 用于修饰在gpu上运行且由cpu调用的函数
* 类似的,__device__ 用于修饰在gpu上运行且由gpu调用的函数,本例不涉及
*/
__global__ void k_add (int a, int b, int* c ){
    *c = a+b;
}
/*
* __host__用于修饰在cpu上运行的函数,一般缺省
*/
int main(void){
    int h_c;//h前缀表示host,指cpu的变量
    int *d_c;//d前缀表示device,指gpu的变量
    /*
    * cudaMalloc功能类似malloc
    * 第一个参数是指向地址的地址变量(&d_c),第二个参数是分配显存的大小
    * 函数会修改&d_c指向的变量,完成对d_c的赋值
    * (void**)是为了让函数无需关心d_c的类型
    */
    cudaMalloc((void**)&d_c, sizeof(int));
    /*
    * gpu函数调用时有<<< >>>
    * 其中第一个变量表示使用几个线程块,第二个变量表示一个线程块使用几个线程
    */
    k_add<<<1,1>>>(2,7,d_c);
    /*
    * cudaMemcpy功能类似memcpy
    * 在cpu中不能解引用gpu变量,必须先将变量从显存复制到内存上才能访问
    * 最后一个参数指定复制的方向
    */
    cudaMemcpy(&h_c, d_c,sizeof(int),cudaMemcpyDeviceToHost);
    /*
    * cudaFree功能类似free
    */
    cudaFree(d_c)
    printf("%d\n",h_c);
    return 0;

}

代码1*:简单加法(Python)

除了用C写,当然,也可以安装pycuda,用python写cuda,会更简便一点。pycuda语法参考官网,这里给出上述代码的python版本:

"""
pycuda.autoinit用于自动管理cuda变量,这样就不用手动回收了
"""
import pycuda.autoinit
import pycuda.driver as drv
"""
SourceModule用于编译C风格的gpu函数(核函数)
"""
from pycuda.compiler import SourceModule

"""
numpy是常用的科学计算库。CPU上的变量一般用numpy的ndarray表示。
"""
import numpy

"""
字符串即为cu代码
"""
mod = SourceModule("""
__global__ void k_add(float *a, float *b, float *c)
{
  *c = *a+*b;
}
""")

k_add = mod.get_function("k_add")

a = numpy.asarray((2,)).astype(numpy.float32)
b = numpy.asarray((7,)).astype(numpy.float32)
c = numpy.empty_like(a)
"""
pycuda提供提供三个参数处理器用来简化cpu和gpu之间变量的拷贝
pycuda.driver.In:在调用核函数前,先把该变量拷贝到gpu
pycuda.driver.Out:在执行完核函数后,把该变量拷贝到cpu
pycuda.driver.InOut:在调用核函数前,先把该变量拷贝到gpu去,并且在执行完核函数后,把该变量拷贝回cpu
"""
k_add(drv.In(a), drv.In(b),drv.InOut(c),block=(1,1,1), grid=(1,1,1))

print(c)

上面是c风格的代码,还可以更简化:

import pycuda.gpuarray as gpuarray
a = numpy.asarray((2,)).astype(numpy.float32)
b = numpy.asarray((7,)).astype(numpy.float32)
d_a = gpuarray.to_gpu(a)
d_b = gpuarray.to_gpu(b)
d_c = d_a + d_b
print(d_c.get())

代码2:矢量加法

了解一些内建变量及对应的含义
了解线程的全局索引计算方式

/*
* 文件名 helloworld.cu
* 编译: nvcc -o helloworld helloworld.cu
*/
#include 
#include 

#define N 65536
__global__ void k_add (int* a, int *b, int *c){
    /*
    * threadIdx表示当前线程号
    * blockIdx表示当前线程块号
    * blockDim表示一个线程块拥有的线程数
    * gridDim表示一个线程格拥有的线程块数
    * 线程格和线程块支持多维索引,本例中只用一维,故只用x维
    * 上述变量都是内置变量
    */
    int tid = threadIdx.x + blockIdx.x*blockDim.x;
    while (tid<N){
        c[tid] = a[tid]+b[tid];
        tid += blockDim.x*gridDim.x;
    }
}

int main(void){
   int a[N], b[N], c[N];
    int *d_a, *d_b, *d_c;
    cudaMalloc((void**)&d_a,N*sizeof(int));
    cudaMalloc((void**)&d_b,N*sizeof(int));
    cudaMalloc((void**)&d_c,N*sizeof(int));
    /*
    *随便初始化两个数组
    */
    for (int i=0;i<N;i++)
    {
        a[i]=i;
        b[i]=i*i;
    }
    cudaMemcpy(d_a,a,N*sizeof(int),cudaMemcpyHostToDevice);
    cudaMemcpy(d_b,b,N*sizeof(int),cudaMemcpyHostToDevice);
    cudaMemcpy(d_c,c,N*sizeof(int),cudaMemcpyHostToDevice);
    /*
    * 这里使用128个线程块,每个线程块包含128个线程
    */
    k_add<<<128,128>>>(d_a,d_b,d_c);
    cudaMemcpy(c,d_c,N*sizeof(int),cudaMemcpyDeviceToHost);
    cudaFree(d_a);
    cudaFree(d_b);
    cudaFree(d_c);
    /*
    * 检查结果是否正确
    */
    for(int i=0;i<N;i++)
    {
        if(a[i]+b[i]!=c[i])
        {
            printf("fail\n");
            return 0;
        }
    }
    printf("success\n");
    return 0;

}

代码3:矢量点积

了解线程同步函数
了解共享变量声明方式

/*
* 文件名 helloworld.cu
* 编译: nvcc -o helloworld helloworld.cu
*/
#include 
#include 

#define N 65536
const int threadsPerBlock = 128;
const int blocksPerGrid = 128;

__global__ void k_dot (int* a, int *b, int *c){
    /*
    * __shared__ 声明线程块中的共享变量,可被线程块中的所有线程读写。
    */
    __shared__ float cache[threadsPerBlock];
    int tid = threadIdx.x + blockIdx.x*blockDim.x;
    int cacheIndex=threadIdx.x;
    float temp = 0;
    while (tid<N){
        temp += a[tid]*b[tid];
        tid += blockDim.x*gridDim.x;
    }
    cache[cacheIndex] = temp;
    /*
    * __syncthreads() 用于线程块内的线程同步。只有当所有线程执行到该语句,线程才会继续执行
    */
    __syncthreads();
    /*
    * 归约求和
    */
    int i = blockDim.x/2;
    while(i>0){
        if(cacheIndex<i)
            cache[cacheIndex] += cache[cacheIndex+i];
        __syncthreads();
        i /= 2;
    }
    /*
    * 让随便一个线程写入该线程块的点积结果
    */
    if (cacheIndex ==0){
        c[blockIdx.x] = cache[0];
    }
}

int main(void){
int a[N], b[N], c, partial_c[blocksPerGrid];
    int *d_a, *d_b, *d_partial_c;
    cudaMalloc((void**)&d_a,N*sizeof(int));
    cudaMalloc((void**)&d_b,N*sizeof(int));
    cudaMalloc((void**)&d_partial_c,blocksPerGrid*sizeof(int));
    for( int i=0;i<N;i++)
    {
        a[i]=1;
        b[i]=2;
    }
    cudaMemcpy(d_a,a,N*sizeof(int),cudaMemcpyHostToDevice);
    cudaMemcpy(d_b,b,N*sizeof(int),cudaMemcpyHostToDevice);
    k_dot<<<blocksPerGrid,threadsPerBlock>>>(d_a,d_b,d_partial_c);
    cudaMemcpy(partial_c,d_partial_c,blocksPerGrid*sizeof(int),cudaMemcpyDeviceToHost);
    /*
    * 将所有线程块的点积结果相加即为最终结果
    */
    c=0;
    for(int i=0;i<blocksPerGrid;i++)
    {
        c += partial_c[i];
    }
    cudaFree(d_a);
    cudaFree(d_b);
    cudaFree(d_partial_c);
    /*
    * 检查结果是否正确
    */
    if(c==2*N) printf("success\n");
    else printf("fail\n");
    return 0;
}

你可能感兴趣的:(CUDA编程,c++,多线程,c语言,cuda,nvidia)