看看这篇吧CUDA基础知识
以后有空再自己写。
了解以下几个概念:
线程 thread
线程块 thread block
线程格 thread grid
了解 如何创建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;
}
除了用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())
了解一些内建变量及对应的含义
了解线程的全局索引计算方式
/*
* 文件名 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;
}
了解线程同步函数
了解共享变量声明方式
/*
* 文件名 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;
}