CUDA 教程(三)CUDA C 编程简介
前言 上一章我们学会了 CUDA 编程的必备基础知识,本章会为大家简单讲解 CUDA C 编程中的简单的内存管理,线程操作,如何编写核函数,使用 Thrust 库,并行计算,性能分析工具,我们会结合代码详细为大家讲解 CUDA C 编程,更为复杂的操作期待后续章节。
本教程禁止转载。同时,本教程来自知识星球【CV技术指南】更多技术教程,可加入星球学习。
欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读、CV招聘信息。
一 获取 GPU 信息
CUDA 提供了几种获取 GPU 信息的方法,这里介绍一下通过调用 cuda_runtime.h
中的 API 得到 GPU 的一些属性。
在编写 CUDA C 程序时, 要将文件命名为 *.cu
,一般使用 nvcc 命令编译运行,为 CUDA程序文件,支持 C/C++ 语法。
#include<iostream>
#include<cuda.h>
#include<cuda_runtime.h>
int main() {
int dev = 0;
cudaDeviceProp devProp;
cudaGetDeviceProperties(&devProp, dev);
std::cout << "GPU Device Name" << dev << ": " << devProp.name << std::endl;
std::cout << "SM Count: " << devProp.multiProcessorCount << std::endl;
std::cout << "Shared Memory Size per Thread Block: " << devProp.sharedMemPerBlock / 1024.0 << " KB" << std::endl;
std::cout << "Threads per Thread Block: " << devProp.maxThreadsPerBlock << std::endl;
std::cout << "Threads per SM: " << devProp.maxThreadsPerMultiProcessor << std::endl;
std::cout << "Warps per SM: " << devProp.maxThreadsPerMultiProcessor / 32 << std::endl;
return 0;
}
编译命令
nvcc checkDeviceInfor.cu -o checkDeviceInfor
输出如下,SM 数量为 30,每个线程块的共享内存为 48KB,每个线程块有 1024 个线程,每个 SM 有 1536 个线程,每个 SM 有 48 个线程束
GPU Device Name0: NVIDIA GeForce RTX 3060 Laptop GPU
SM Count: 30
Shared Memory Size per Thread Block: 48 KB
Threads per Thread Block: 1024
Threads per SM: 1536
Warps per SM: 48
二 初步内存管理
主机和设备各自拥有独立的内存,C 拥有标准库可以管理主机的内存,CUDA 提供的 API 管理设备的内存,下面是 C 和 CUDA 的部分内存管理函数
C | CUDA | 功能 |
---|---|---|
malloc | cudaMalloc | 分配内存 |
memcpy | cudaMemcpy | 复制内存 |
memset | cudaMemset | 设置内存 |
free | cudaFree | 释放内存 |
主机与设备的数据拷贝
下面的程序举例了如何使用进行主机与设备的数据拷贝,使用了 cudaMalloc
,cudaMemcpy
和 cudaFree
函数,函数形参如下
__host__ cudaError_t cudaMalloc (void** devPtr, size_t size)
devPtr
: 开辟数据的首指针size
: 开辟的设备内存空间长度
__host__ cudaError_t cudaMemcpy(void* dst, const void* src, size_t count, cudaMemcpyKind kind)
dst
: 目的数据内存首指针src
: 源数据首指针count
: 数据长度kind
: 拷贝类型,cudaMemcpyDeviceToHost
: 从设备向主机拷贝 |cudaMemcpyDeviceToHost
: 从主机向设备拷贝 |cudaMemcpyHostToHost
: 从主机向主机拷贝 |cudaMemcpyDeviceToDevice
: 从设备向设备拷贝__host__ cudaError_t cudaFree (void* devPtr)
devPtr
: 设备变量指针
上述函数的返回值类型都是 cudaError_t
,以枚举形式保存各种错误类型
更多运行时函数详解见官方文档
#include <cuda.h>
#include <cuda_runtime.h>
#include <vector>
#include <iostream>
#include <math.h>
int main() {
float dets[6][4] = {
{23, 34, 56, 76},
{11, 23, 45, 45},
{12, 22, 47, 47},
{9, 45, 56, 65},
{20, 37, 55, 75},
};
// copy data to gpu
std::cout << sizeof(dets) << std::endl;
float *dev_dets;
cudaError_t err = cudaSuccess;
err = cudaMalloc((void **)&dev_dets, sizeof(dets));
if (err != cudaSuccess) {
printf("cudaMalloc failed!");
return 1;
}
cudaMemcpy(dev_dets, dets, sizeof(dets), cudaMemcpyHostToDevice);
std::cout << "Copied data to GPU.\n";
// get back copied cuda data
float host_dets[sizeof(dets)/sizeof(float)];
cudaMemcpy(&host_dets, dev_dets, sizeof(dets), cudaMemcpyDeviceToHost);
std::cout << "Copied from cuda back to host.\n";
std::cout << "host_dets size: " << sizeof(host_dets) << std::endl;
for (int i=0;i<sizeof(dets)/sizeof(float);i++) {
std::cout << host_dets[i] << " ";
}
std::cout << std::endl;
cudaFree(dev_dets