VS2017 CUDA编程学习1:CUDA编程两变量加法运算
VS2017 CUDA编程学习2:在GPU上执行线程
VS2017 CUDA编程学习3:CUDA获取设备上属性信息
VS2017 CUDA编程学习4:CUDA并行处理初探 - 向量加法实现
VS2017 CUDA编程学习5:CUDA并行执行-线程
VS2017 CUDA编程学习6: GPU存储器架构
VS2017 CUDA编程学习7:线程同步-共享内存
VS2017 CUDA编程学习8:线程同步-原子操作
VS2017 CUDA编程学习9:常量内存
VS2017 CUDA编程学习10:纹理内存
前言
这里继续跟大家分享CUDA编程的知识,这次就不介绍新知识了,主要是应用之前学的CUDA编程知识实现一个小例子:向量点乘运算
1. 向量点乘
这里有两个向量, v 1 = [ a 1 , a 2 , . . . , a n ] v_{1}=[a_{1}, a_{2},..., a_{n}] v1=[a1,a2,...,an]和 v 2 = [ b 1 , b 2 , . . . , b n ] v_{2}=[b_{1}, b_{2},..., b_{n}] v2=[b1,b2,...,bn],向量点乘的计算公式如下:
r e s u l t = ∑ i = 1 n ( a i ∗ b i ) result=\sum_{i=1}^{n}(a_{i}*b_{i}) result=∑i=1n(ai∗bi)
2. C++ CUDA实现向量点乘
这里使用共享内存实现向量点乘,主要是因为共享内存的访问速度比全局内存快一个数量级,使用多块多线程来加速计算过程,在源码中通过注释来解读实现过程。
详细代码如下所示:
#include <iostream>
#include <stdio.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#define N 1024
#define THREADS_PER_BLOCK 512
__global__ void gpu_dot(float* d_a, float* d_b, float* d_c)
{
//为每个块定义共享内存,不同块的共享内存没法通信
__shared__ float partial_sum[THREADS_PER_BLOCK];
//整个GPU设备的线程索引
int tid = threadIdx.x + blockIdx.x * blockDim.x;
//当前块的线程索引
int index = threadIdx.x;
//每个线程进行一个元素乘法操作,
//比如v1=[a1,a2,a3], v2=[b1,b2,b3]
//线程t1: a1*b1, 线程t2:a2*b2, ...
float sum = 0;
while (tid < N)
{
//sum += d_a[tid] * d_b[tid];//多块多线程运行时,这里会造成计算结果出错
sum = d_a[tid] * d_b[tid];
tid += blockDim.x + gridDim.x;
}
//将上述部分和保存到共享内存中
partial_sum[index] = sum;
//线程同步,等待块中所有线程都执行完上述代码行
__syncthreads();
//使用当前块的共享内存中保存的数据循环计算部分和,
//(1)将共享内存数组中数据二分,后面的数据累加到前面的数据中,
//(2)然后舍弃后面的数据,在前面的数据中继续(1)的过程
int i = blockDim.x / 2;
while (i != 0)
{
if (index < i)//每次仅在二分线程中的前半部分线程数进行累加
{
partial_sum[index] += partial_sum[index + i];
}
//等待所有线程执行完上述过程
__syncthreads();
i /= 2;
}
//将当前块的部分和计算结果保存到全局内存中
if (index == 0)
{
d_c[blockIdx.x] = partial_sum[0];
}
}
int main()
{
float* h_a, *h_b, h_c, *partial_sum;
float *d_a, *d_b, *d_partial_sum;
//计算块的数量和线程数量
int block_calc = (N + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK;
int blocksPerGrid = block_calc > 32 ? 32 : block_calc;
//在CPU上分配内存
h_a = (float*)malloc(N * sizeof(float));
h_b = (float*)malloc(N * sizeof(float));
partial_sum = (float*)malloc(blocksPerGrid * sizeof(float));
//在GPU上分配内存
cudaMalloc(&d_a, N * sizeof(float));
cudaMalloc(&d_b, N * sizeof(float));
cudaMalloc(&d_partial_sum, blocksPerGrid * sizeof(float));
//填充数据
for (int i = 0; i < N; i++)
{
h_a[i] = 1;
h_b[i] = 2;
}
//将数据从CPU拷贝到GPU上
cudaMemcpy(d_a, h_a, N * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(d_b, h_b, N * sizeof(float), cudaMemcpyHostToDevice);
//调用内核函数,模块数为blocksPerGrid, 每个模块的线程数为THREADS_PER_BLOCK
gpu_dot << <blocksPerGrid, THREADS_PER_BLOCK >> > (d_a, d_b, d_partial_sum);
//将结果从GPU上拷贝到CPU上
cudaMemcpy(partial_sum, d_partial_sum, blocksPerGrid * sizeof(float), cudaMemcpyDeviceToHost);
//计算最终的向量点乘结果:累加每个块的计算结果
h_c = 0;
for (int i = 0; i < blocksPerGrid; i++)
{
h_c += partial_sum[i];
}
printf("GPU向量点乘计算结果为:%f\n", h_c);
//释放GPU内存
cudaFree(d_a);
cudaFree(d_b);
cudaFree(d_partial_sum);
//释放GPU内存
free(h_a);
free(h_b);
free(partial_sum);
system("pause");
return 0;
}
3. 执行结果
上述例子中两个输入向量一个为全1值,另一个为全2值,向量长度为1024,所以执行结果如下所示:
总结
这里并没有给出GPU上加速效果,大家如果感兴趣可以自行实现在CPU上执行的速度,因本人还是菜鸟一枚,上述过程如有错误,还请大神指正,谢谢!
学习资料
《基于GPU加速的计算机视觉编程》