VS2017 CUDA编程学习1:CUDA编程两变量加法运算
VS2017 CUDA编程学习2:在GPU上执行线程
VS2017 CUDA编程学习3:CUDA获取设备上属性信息
VS2017 CUDA编程学习4:CUDA并行处理初探 - 向量加法实现
VS2017 CUDA编程学习5:CUDA并行执行-线程
VS2017 CUDA编程学习6: GPU存储器架构
前言
1. CUDA线程同步-共享内存理解
在之前的学习中,CUDA多线程执行都是单独执行的,没有进行交互,也就是我们常说的数据交流,但是实际上我们往往需要多线程交互,这时就可以用到共享内存来完成,但是多线程交互必须要注意同步问题,这和CPU上多线程同步问题一样。
当然,多线程交互不是必须使用共享内存,也可以使用全局内存,只是共享内存速度快。
共享内存位于芯片内部,比全局内存快,延迟大约低100倍(相比于没有缓存的全局内存访问)。之前的博客中有介绍GPU存储器架构,不同块中线程所见到的共享内存数据内容是不同的。
同步问题可以使用__syncthreads()指令完成,该指令保证块中所有线程都将到达该代码行,然后等待所有其他线程执行。
2. C++ CUDA代码实现
这里以求数组均值为例,使用VS2017 C++调用CUDA库实现例子程序。
例子程序主要介绍CUDA库中的新的函数__syncthreads()和共享内存关键字__shared__的使用方法。
#include <iostream>
#include <stdio.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
__global__ void gpu_shared_memory(float* d_a)
{
int i;
int index = threadIdx.x;
float average = 0.0f, sum = 0.0f;
//定义共享内存 shared memory, 共享内存数组大小一般为每个block的线程数
//这个例子中内核参数配置为<<<1,10>>>, 1个block,每个block有10个线程
__shared__ float sh_arr[10];
sh_arr[index] = d_a[index];
//保证所有线程都执行完成才继续执行下面的代码行
__syncthreads();
for (i = 0; i <= index; i++)
{
sum += sh_arr[i];
}
printf("threadIdx.x=%d, 累加和=%f\n", index, sum);
average = sum / (index + 1.0f);
d_a[index] = average;
}
int main()
{
float h_a[10];
float* d_a;
//初始化主机(CPU)数组
for (int i = 0; i < 10; i++)
{
h_a[i] = i;
}
//分配设备(GPU)内存
cudaMalloc(&d_a, 10 * sizeof(float));
//将主机数组数据拷贝到设备内存中
cudaMemcpy(d_a, h_a, 10 * sizeof(float), cudaMemcpyHostToDevice);
//调用内核函数, 使用GPU设备中1个block, 每个block启动10个线程
gpu_shared_memory << <1, 10 >> > (d_a);
//将设备内存中数据拷贝回主机数组中
cudaMemcpy(h_a, d_a, 10 * sizeof(float), cudaMemcpyDeviceToHost);
printf("\nGPU上共享内存使用:\n");
for (int i = 0; i < 10; i++)
{
printf("前%d个元素的平均值是:%f\n", i, h_a[i]);
}
//释放设备内存
cudaFree(d_a);
system("pause");
return 0;
}
3. 运行结果
总结
线程同步是一个必须考虑的问题,不论是在CPU还是GPU中,否则会产生不可预期的结果。
学习资料
《基于GPU加速的计算机视觉编程》