1. 内存分配和数据移动 API 函数

CUDA编程模型是一个异构模型,需要CPU和GPU协同工作。在CUDA中,hostdevic e 是两个重要的概念,我们用host指代CPU及其内存,而用device指代GPU及其内存。

CUDA程序中既包含host程序,又包含device程序,它们分别在CPU和GPU上运行。同时,host与device之间可以进行通信,这样它们之间可以进行数据拷贝。典型的CUDA程序的执行流程如下:

  1. 分配host内存,并进行数据初始化;
  2. 分配device内存,并从host将数据拷贝到device上;
  3. 调用CUDA的核函数在device上完成指定的运算;
  4. 将device上的运算结果拷贝到host上;
  5. 释放device和host上分配的内存。
#include <cuda.h>
void vecAdd(float *h_A, float *h_B, float *h_C, int n){
int size = n* sizeof(float);
float *d_A, *d_B, *d_C;
// Part 1
// 为 A,B,C 分配 device 内存
// copy A 和 B 到 device 内存 // Part 2
// 内核启动代码 - 设备执行实际的矢量加法 // Part 3
// 从 device 内存 copy C
}

Device 代码可以:

  • 写/读 per-thread register
  • 写/读 all-shared global memory

Host 代码可以:

  • 将数据传输到每个 grid global memory / 从每个 grid global memory 传输数据

1.1 Device Memory 管理 API

cudaMalloc()

  • 在 device global memory 中分配一个对象

  • 两个参数

    • 分配对象指针的地址
    • 分配对象的大小(以字节为单位)

cudaFree()

  • 从 device global memory 中释放对象
  • 一个参数
    • 释放对象的指针

1.2 Host-Device 数据传输 API

cudaMemCpy()

  • 内存数据传输

  • 4个参数

    • 目的地指针
    • 源指针
    • 复制的字节数
    • 传输类型/方向
  • 与 host 同步向 device 传输数据

1.3 矢量加法-显式内存管理

1.4 统一内存(Unified Memory)

cudaMallocManaged(void** ptr, size_t size)

  • 统一内存是 CPU 和 GPU 共享的相同内存空间。这意味着,CPU 和 GPU 使用相同的地址空间,无需像传统的 CUDA 编程那样手动管理内存传输(如cudaMemcpy())。

    • 它维持数据的单一副本,即不再需要独立的主机内存和设备内存副本。
  • CUDA-managed 数据

    • 其中 ptr 是指向分配内存的指针,size 是所需内存的大小。
    • 按需页面迁移:在GPU执行代码时,所需的数据会按需从主机(Host)迁移到设备(Device)。
  • cudaMalloc()cudaFree() 兼容

  • 可优化

    • cudaMemAdvise(): 给CUDA提供建议,提示内存的使用模式(例如,数据主要在哪个处理器上使用)。
    • cudaMemPrefetchAsync(): 允许程序员预取数据到设备或主机,减少内存访问延迟。
    • cudaMemcpyAsync(): 在异步传输数据时,可以优化性能。

1.5 矢量加法-统一内存

1.6 检查 host 代码中的 API 错误

cudaError_t err = cudaMalloc((void **) &d_A, size);
if(err != cudaSuccess){
printf("%s in %s at line %d\n", cudaGetErrorString(err), __FILE__, __LINE__);
exit(EXIT_FAILURE);
}

2. 线程和内核函数

2.1 Kernel 线程层次结构

首先GPU上很多并行化的轻量级线程,kernel 在 device 上执行时实际上是启动很多线程。

一个 kernel 所启动的所有线程称为一个网格(grid),同一个网格上的线程共享相同的 global memory,grid 是线程结构的第一层次,而网格又可以分为很多 线程块(block),一个线程块里面包含很多线程,这是第二个层次。

2.2 数据并行 - 向量加法示例

2.3 CUDA 执行模型

  • 异构 host(CPU)+ device(GPU)应用 C 程序

    • host C 代码中的串行部件
    • device SPMD 内核代码中的并行部分

2.4 并行 thread 阵列

  • CUDA kernel 由 threads 组成的一个 grid(array)执行

    • grid 中的所有 thread 运行相同的 kernel 代码(单程序多数据)
    • 每个 thread 都有索引,用于计算内存地址和做出控制决定

2.4.1 线程块:可扩展的协作

  • 将 thread array 划分为多个 block

    • block 内的 thread 通过 共享内存(shared memory)原子操作(atomic operations)障碍同步(barrier synchronization)进行协作
    • 不同 block 中的 thread 不会相互影响

2.4.2 block 索引和 thread 索引

  • 每个 thread 使用索引来决定处理哪些数据

    • blockIdx:1D, 2D or 3D (CUDA 4.0)
    • threadIdx:1D, 2D or 3D
  • 处理多维数据时简化内存寻址

    • 图像处理
    • 解决体积上的 PDEs

对于一个 2-dim 的 \(block(D_x,D_y)\),线程 \((x,y)\) 的 ID 值为 \((x+y∗D_x)\) ,如果是 3-dim 的 \(block(D_x,D_y,D_z)\),线程 \((x,y,z)\) 的ID值为 \((x+y∗D_x+z∗D_x∗D_y)\) 。另外线程还有内置变量 gridDim,用于获得网格块各个维度的大小。

3. CUDA 工具包简介

3.1 NVCC Compiler

  • 英伟达(NVIDIA)提供 CUDA-C 编译器,简单的编译命令为:
nvcc vectorAdd.cu -o vectorAdd

这将使用默认参数编译 vectorAdd.cu,并输出一个叫 vectorAdd 的可执行文件。

  • nvcc 编译设备代码,然后将代码转发给 host 编译器(如 g++)。

  • 可用于编译和链接 host 应用程序

两种源文件:

对于 CUDA 程序,我们通常有:

  1. .cu 文件:包含 CUDA C/C++ 代码,包括主程序代码和 CUDA kernels,需要 nvcc 编译。
  2. .cpp/.c 文件:只包含主程序代码,不含 CUDA kernels,可以用 gcc/g++ 编译。

编译 CUDA 程序的一般步骤是:

  1. nvcc 编译 .cu 文件,生成 .o 文件。
  2. g++ 编译 .cpp 文件,也生成 .o 文件。
  3. 再用 g++/nvcc 链接所有 .o 文件生成最终可执行文件。
nvcc -c demo.cu -o demo.o
g++ -c main.cpp -o main.o
nvcc demo.o main.o -o demo -lcudart

3.2 调试工具(Debugger)

  • NVIDIA 提供的调试工具

    • Nsight:这是一个用于 GPU 编程调试和优化的工具,专门设计用来分析 GPU 代码的性能问题,并进行代码调试。支持对 CUDA 和 OpenGL 等代码进行深入的分析。
    • Nsight Systems:一个面向系统级性能分析的工具,可以帮助开发者查找瓶颈,了解程序在 CPU 和 GPU 之间的负载分布。
    • CUDA-GDB:这是一个基于 GDB 的调试器,专门用于 CUDA 编程的调试工作,支持查看 GPU 上的内存状态、线程执行情况等。
    • CUDA MEMCHECK:用于检测 CUDA 代码中的内存错误,包括非法内存访问、未初始化内存使用等,类似于 CPU 上的 valgrind 工具。
  • 第三方调试工具
    • ARM Forge:支持跨平台的高性能计算(HPC)调试工具,适用于调试大规模并行程序,包括CUDA应用。
    • TotalView:另一个广泛使用的高性能并行调试器,适用于调试复杂的CUDA程序,提供对多核、多线程代码的深入支持。

3.3 性能分析工具(Profilers)

  • NVIDIA 提供的性能分析工具

    • NSIGHT:与调试工具一样,Nsight 还可用于性能分析,帮助开发者分析 GPU 代码的瓶颈、内存使用、指令执行效率等。它支持 CUDA、OpenGL 和 Vulkan 等多种 API。
    • NVVP(NVIDIA Visual Profiler):专门为 CUDA 程序设计的图形化性能分析工具,提供可视化的性能瓶颈分析,帮助开发者定位代码中导致性能下降的具体部分。
    • NVPROF:是一个命令行性能分析工具,专注于 CUDA 程序的性能数据收集。它可以通过收集时间信息、内存传输、计算效率等指标,提供详细的性能分析结果。
  • 第三方性能分析工具

    • TAU:一款适用于并行程序的全面性能分析工具,支持多种硬件架构和 API,帮助用户从全局上评估程序的性能表现。
    • VampirTrace:一个可视化的性能分析工具,用于追踪并行程序的执行情况,帮助开发者分析程序的时间线、通信延迟等。

4. Nsight 计算和 Nsight 系统

  • Phasing Out(逐步淘汰的工具)

    • NVPROF
    • NVVP(NVIDIA Visual Profiler)
  • Current(当前主流的工具)

    • Nsight Compute
    • Nsight Systems

参考文献

[1] CUDA编程入门极简教程 - 知乎 (zhihu.com)

[2] nvcc 编译 .cu 源代码的基本用法_多个文件 nvcc怎么编译-CSDN博客

CUDA编程学习 (1)——CUDA C介绍的更多相关文章

  1. CUDA编程学习笔记1

    CUDA编程模型是一个异构模型,需要CPU和GPU协同工作. host和device host和device是两个重要的概念 host指代CPU及其内存 device指代GPU及其内存 __globa ...

  2. CUDA编程学习相关

    1. CUDA编程之快速入门:https://www.cnblogs.com/skyfsm/p/9673960.html 2. CUDA编程入门极简教程:https://blog.csdn.net/x ...

  3. CUDA编程学习(一)

    /****c code****/ #include<stdio.h> int main() { printf("Hello world!\n); ; } /****CUDA co ...

  4. cuda编程学习6——点积dot

    __shared__ float cache[threadPerBlock];//声明共享内存缓冲区,__shared__ __syncthreads();//对线程块中的线程进行同步,只有都完成前面 ...

  5. cuda编程学习5——波纹ripple

    /共有DIM×DIM个像素,每个像素对应一个线程dim3 blocks(DIM/16,DIM/16);//2维dim3 threads(16,16);//2维kernel<<<blo ...

  6. cuda编程学习4——Julia

    书上的例子编译会有错误,修改一下行即可. __device__ cuComplex(float a,float b):r(a),i(b){} /* ========================== ...

  7. cuda编程学习3——VectorSum

    这个程序是把两个向量相加 add<<<N,1>>>(dev_a,dev_b,dev_c);//<N,1>,第一个参数N代表block的数量,第二个参数1 ...

  8. cuda编程学习2——add

    cudaMalloc()分配的指针有使用限制,设备指针的使用限制总结如下: 1.可以将其传递给在设备上执行的函数 2.可以在设备代码中使用其进行内存的读写操作 3.可以将其传递给在主机上执行的函数 4 ...

  9. cuda编程学习1——hello world!

    将c程序最简单的hello world用cuda编写在GPU上执行,以下为代码: #include<iostream>using namespace std;__global__ void ...

  10. CUDA编程学习笔记2

    第二章 cuda代码写在.cu/.cuh里面 cuda 7.0 / 9.0开始,NVCC就支持c++11 / 14里面绝大部分的语言特性了. Dim3 __host__ __device__ dim3 ...

随机推荐

  1. 1000T的文件怎么能快速从南京传到北京?最佳方案你肯定想不到

    今天刷面试题看到一个有意思的面试题, 1000T的文件怎么能以最快速度从南京传到北京? 网络传输 首先我们考虑通过网络传输,需要多长时间. 我特地咨询了在运营商工作的同学,目前带宽: 家庭宽带下行最大 ...

  2. SimpleRAG:基于WPF与Semantic Kernel实现的一个简单的RAG应用

    SimpleRAG介绍 SimpleRAG是基于WPF与Semantic Kernel实现的一个简单的RAG应用,可用于学习与理解如何使用Semantic Kernel构建RAG应用. GitHub地 ...

  3. String究竟能存储多少字符?

    能存储多少字符,通过以下步骤来看 首先String的length方法返回是int.所以理论上长度一定不会超过int的最大值. 编译器对字符串字面量长度的限制源自Java编译器(如javac)在处理常量 ...

  4. Python开发中,日期时间的相关处理

    在Python开发中,日期和时间处理是一个常见的需求.Python提供了多种模块和方法来处理日期和时间,以下是一些常用的模块和操作.通过介绍一些系统的Python类库以及第三方的类库,我们可以快速的实 ...

  5. LaTeX 编译 acmart 文档报错:No country present for an affiliation.

    在编译一篇从 arXiv 下载的文档时遇到如下错误: Class acmart Error: No country present for an affiliation. 有两种解决方案: 将错误降级 ...

  6. Drools决策表实践运用

    Drools 决策表的使用与说明 Drools决策表的使用 官方文档决策表说明 决策表使用方式 执行drl代码及结果 Drools决策表的使用 官方文档决策表说明 Drools 决策表的使用 16.7 ...

  7. in notin exists not exists 性能优化算法总结

    in notin exists not exists 性能优化算法总结 1.1. in 和 exists 区别 1.2. not in 能不能走索引 1.3. not in 和 join 的关系 1. ...

  8. 升讯威在线客服系统如何高性能同时支持 MySQL 和 SQL Server

    升讯威在线客服与营销系统是基于 .net core / WPF 开发的一款在线客服软件,宗旨是: 开放.开源.共享.努力打造 .net 社区的一款优秀开源产品. 前段时间我发表了一系列文章,开始介绍基 ...

  9. 加入 Flutter Engage,Pick 您的专属 Dash 形象!

    Flutter Engage 活动精彩来袭 对 Flutter 团队的开发者们来说,交流的重要性不言而喻,和您一样,我们也希望开发者们能够在不同的情境下进行互动分享.于是我们为您准备了一场特别的线上活 ...

  10. USB type-c CC管脚如何做到正反接检测功能

    USB Type-C 连接器的 CC (Configuration Channel) 管脚用于实现插头方向检测和电源管理.具体来说,USB Type-C 连接器具有两个 CC 管脚:CC1 和 CC2 ...