CUDA 程序的优化(3) 任务划分

2023-05-16

4.3.1任务划分原则

首先,需要将要处理的任务划分为几个连续的步骤,并将其划分为CPU端程序和GPU端程序。划分时需要考虑的原则有:

列出每个步骤的所有可以选择的算法,并比较不同算法在效率和计算复杂度上的差异。
能够并行实现的算法并不一定比串行算法快。在问题规模较小时,计算复杂度阶数更高的算法也有可能比计算复杂度阶数较低的算法耗费更短的时间。根据问题规模,选择适当算法,将任务中耗费时间最多的大规模数据并行、高计算密集度步骤映射到GPU上。

在CPU上可以并行实现的算法不一定适用于GPU。CPU程序主要考虑的是指令间并行和粗粒度的软件线程并行,在每个CPU线程内还是串行的。由于CPU线程粒度往往太大,因此尽量不要将CPU线程直接映射为GPU线程。每个GPU线程完成的任务更加类似于CPU的多轮循环中的一轮。但也不是所有的循环都能映射为一个内核程序,因为有的循环中每一轮运算都依赖上一轮的结果,而GPU的线程之间是并行的。此时,需要采取其他方式对任务进行分解。

在两次主机一设备通信之间进行尽量多的计算。由于主机与设备间的数据传输带宽远低于显存带宽,因此最好在两次通信之间让GPU进行尽可能多的运算。如果在两次大规模数据并行运算之间存在少量的串行运算,有时即使是在GPU上以较低的效率进行这些串行运算也比增加两次主机一设备通信要划算。在GPU进行运算的同时,如果可能,也可以让CPU进行一些计算,比如准备下一次计算需要的数据。

应该考虑使用流运算隐藏主机一设备通信时间,以及通过pinned memory, zero-copy,write-combined memory等手段提高实际传输带宽。在集群中使用CUDA,还需要考虑节点之间的任务分配与通信问题。

对每个并行步骤进行划分,从不同角度分析有不同的划分方式。

从对显存的访问方向来说,可以按照输入划分或者按照输出划分。如果每个block中输入和输出的数据的比例和位置是固定的,并且能够比较容易地满足合并访问要求,那么这种划分方式既是按照输入划分的,也是按照输出划分的。这种情况是最理想的,通过shared memory和指针类型转换,大多数输入输出都能够很好地满足合并访问条件。

但如果block的输入和输出的数据不相同,或者输入和输出无法同时满足合并访问要求,就必须设法使可用带宽最大化,只按照输入或者只按照输出划分。
按照输入划分的情况有:

  1. 输入参数很多而输出结果很少,如规约、直方图。
  2. 输入满足合并访问条件,但是输出位置随机,或者输出时需要进行显存原子操作,在流体力学、分子动力学仿真中可能遇到这种情况。

按照输出划分的情况有:

  1. 输入参数很少而输出结果很多,如随机数发生器。Block内每个线程的输入与其他线程共用,比如卷积、滤波中,每个线程的输入与周围线程的输入有公共部分,此时应该先按照合并访问的形式将一块数据读入shared memory,再由每个线程计算一定数量的输出,可以参考SDK中与滤波和卷积有关的几个例子。
  2. 输入数据在存储器中的位置是随机的,而输出数据时可以满足合并访问条件的情况,大多数使用纹理的应用,以及一些需要查表的运算都属于这种情况。

从显存访问的形式来说,在一个block内可以进行一维的带状划分、二维的棋盘划分或者三维的域划分。如果要处理的任务不需要线程间通信,并且对显存的访问都能满足合并访问,那么采用棋盘划分还是带状划分对性能影响并不大。不过,应该尽量使每个block中的线程数量是犯的整数倍,并根据任务的具体情况确定每个维度上的大小,以减少计算访存地址时的整数除法和求模运算。

如果需要使用纹理的特殊功能进行图像处理,使用二维棋盘划分是比较自然的。

如果问题在一个或者几个维度方向上有局部性,可以利用shared memory提高性能或者必须在某几个维度内进行线程间通信,那么block的维度应该与需要通信的维度一致。比如本章4.7.1节的矩阵乘法例子中,既可以进行一维带状划分,也可以按照二维棋盘划分,但二维划分的算法利用了shared memory,有效减小了访存次数,并且满足合并访问条件。

对一个block的任务进行划分后,再按照block的维度和尺寸要求对grid进行划分。此时需要考虑的问题是:

  1. 考虑分区冲突问题,使每个block的访存要求均匀分布在显存的各个分区中,例如4.7.3节中介绍的矩阵转置,在解决分支冲突问题后,性能有了几倍的提升。
  2. Block间负载可以存在一定程度的不均衡,按照block为单位分支性能损失也很小比如,对网络中的数据进行分析时,可以由一个grid处理其中缓冲中的多个包,再由每个SM处理长度和内容都不同的包。

4.3.2 grid和block维度设计

按照CUDA的执行模型,grid中的各个block会被分配到GPU的各个SM中执行。下面的一些建议能够帮助读者确定合适的Grid与block尺寸。在设计时,应该优先考虑block的尺寸,而grid的尺寸一般来说越大越好。

由3.2.2.3小节可知,在Tesla架构GPU的每个SM中,至少要有6个active warp才能有效地隐藏流水线延迟。此外,如果所有的active warp都来自同一block,当这个block中的线程进行存储器访问或者同步时,执行单元就会闲置。基于以上原因,最好让每个SM上拥有至少2个active block。
一个SM上的active warp和active block数量计算方法如下:

(1)确定每个SM使用的资源数量。
使用nvcc的–keep编译选项,或者在.cu编译规则(cuda build rule)中选择保留中间文件(keep preprocessed files),得到.cubin文件。用写字板打开.cubin文件,在每个内核函数的开始部分,可以看到以下几行:

lmem = 0 
smem = 256 
reg   = 8 

其中,lmem和reg分别代表内核函数中每个线程使用的local memory数量和寄存器数量,smem代表每个block使用的shared memory数量。以上数据告诉我们:这个内核函数的每个线程使用了 0 Byte local memory, 8个寄存器文件(每个寄存器文件的大小是32bit);每个block使用了256Byte的shared memory.

(2)根据硬件确定SM上的可用资源。
可以用SDK中的deviceQuery获得每个SM中的资源。要注意的是,在程序编译时,要使目标代码和目标硬件版本与实际使用的硬件一致(使用一arch、-gencode和一code编译选项)。
在G80和GT200架构上,这些限制如表4-1所示。

在这里插入图片描述

此外,每个block中的线程数量不能超过512个。

(3)计算每个block使用的资源,并确定active block和active warp数量。
假设每个block中有64个线程,每个block使用256 Byte shared memory, 8个寄存器文件,那么就有:

  1. 每个block使用的shared memory是:256Byte
  2. 每个block使用的寄存器文件数量:8X64=512
  3. 每个block中的warp数量:64/32=2

然后,根据每个block使用的资源,就可以计算出由每个因素限制的最大active block数量。这里,假设在G80/G92 GPU中运行这个内核程序:

  1. .由shared memory数量限制的active block数量:16384/256=64
  2. 由寄存器数量限制的active block数量:8192/512 = 16
  3. 由warp数量限制的active block数量:24/2=12
  4. 每个SM中的最大active block数量:8

注意,在计算每个因素限制的active block数量时如果发现有除不尽的情况,应该只取结果的整数部分。取上述计算结果中的最小值,可以知道每个SM的active block数量为8

NVIDIA在CUDA SDK中提供的CUDA occupancy calculator也可以完成上面的计算。
CUDA occupancy calculator是一个Excel文件,存储在 SDK的tools目录下。只要在这个Excel表格中输入目标硬件的架构,以及每个block中的线程数量、每个block使用的shared memory数量和每个thread使用的寄存器数量,就可以自动计算出SM的资源占用率,并以图表的形式显示,十分方便。

block的尺寸与数据划分紧密相关,在上一节己经进行了一些探讨。较小的block使用的资源较少,一般在一个SM中能够有更多的active block;而较大的block中有更多的线程可以进行通信,可以获得更高的指令流效率。为了有效利用执行单元,应该让每个block中的线程数量是32的整数倍,最好让线程数量保持在64^-256之间。此时,SM中通常还有足够多的资源来执行至少两个active block。

block的维度和每个维度上的尺寸的主要作用是避免做整数除法和求模运算,对执行单元效率没有什么显著影响。在使用中,读者可以按照问题的具体情况自行确定。如果问题的规模对划分方式并不敏感,应该让blockDim.x为16或者16的整数倍,提高访问global memory和shared memory的效率。

确定block的尺寸和维度以后,就可以按照问题的规模确定grid中的block数量。通常,使用下面的方法来计算grid在某个维度上的block数量(以x轴为例):

grid 在 x 轴上的 block 数量=(问题在 x 轴上的尺寸+每个 block 在 x 轴上的尺寸-1/每个 block 在 x 轴上的尺寸 

加上(每个block在x轴上的尺寸-1)是因为整数除法只会取结果的整数部分,这样可
能使问题的边界得不到处理,引起错误。按照这种方法计算block数量,实际的线程数量就会比需要的线程数量更多。因此,在内核程序中,也要对边界部分进行判断,让边界外的线程不参与计算。使用与线性存储器绑定的纹理时,访问边界外时的返回值是0,可以用来简化边界处理。

理想情况下,grid中的block数量应该至少要比GPU的SM数量X每个SM中active block数量大几倍。为了让程序在未来的GPU上运行也能获得高效率,应该让block的数量尽可能得大。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CUDA 程序的优化(3) 任务划分 的相关文章

  • vscode查看代码更新历史

    开源代码推出新版本后 xff0c 如何查看代码更改信息 1 首先打开vscode xff0c 点击左侧的插件管理器 xff0c 进入插件面板 xff0c 搜索Git Graph并安装 2 点击下图图标 xff0c 即可进入Git Graph
  • git更新代码

    一 git一般有很多分支 xff0c 我们clone到本地的时候一般都是master分支 xff0c 那么如何切换到其他分支呢 xff1f 主要命令如下 xff1a 1 查看本地分支文件信息 xff0c 确保更新时不产生冲突 span cl
  • linux---硬链接和软链接

    文件系统 磁盘上文件读写存储与查找系统 xff08 管理 xff09 就是文件系统 xff0c 在每一个分区都会存在自己的文件系统 在这里我们有swap交换分区和文件分区 xff0c 我们这里只介绍文件分区 在文件分区都会有上图中的分块管理
  • char类型数组

    字符数组 xff08 一维 二维 xff09 字符数组是数组元素为char类型的一种数组 凡是适合数组的定义和赋值 xff0c 也都适合于字符数组 由于C语言没有提供字符串类型 xff0c 字符串一般用一维字符数组来存放 xff0c 而二维
  • ubuntu18.04 安装腾讯会议

    腾讯会议现在以及上线了Linux版本 xff0c 可以直接在腾讯会议官网下载linux 版本 xff0c 在官网点击免费下载 xff0c 可以直接下载Linux版本 腾讯会议下载链接 选择Linux版本 xff0c x86 64版本 xff
  • 2.树莓派系统备份

    树莓派使用SD卡来装载系统 xff0c 如果SD卡丢失或者损坏 xff0c 那么树莓派上的数据都会丢失 xff0c 所以一定要备份好SD卡 这篇文章可以帮你备份你的树莓派系统 主要内容为备份SD卡 xff0c 制作树莓派系统镜像以及在需要的
  • ic_gvins编译及环境配置问题解决

    RTK VIO松组合 对惯导精度要求较高 1 环境配置和编译 安装依赖项 span class token comment gcc 8 span span class token function sudo span span class
  • EVO画图设置

    一 绘图设置 1 更改背景色和网格 span class token comment 白色网格 span evo config span class token builtin class name set span plot seabor
  • GINS_OB环境配置

    1 程序简介 武大开源GNSS INS松组合IMU预积分有考虑地球自传和不考虑两种形式可以灵活设置GNSS中断时间IMU可以和里程计进行融合 2 环境配置 span class token comment gcc 8 g 43 43 8 s
  • OB_GINS程序框架

    1 程序运行 span class token builtin class name cd span OB GINS span class token comment 编译好的可执行文件 xff1a bin ob gins xff0c 参数
  • KEIL、MDK中关于__LINE__宏 printf 的显示不正确的问题

    span class token operator gt span define span class token function DEBUG span span class token punctuation span log span
  • VINS-回环检测与重定位

    参考博客 pose graph分析1 pose graph分析2 pose graph分析3
  • 源码安装naviagtion,但是出现[move_base-2] process has died 运行错误的解决办法

    今天开始记录ros遇到的问题 安装navigation可以使用两种方法 第一种 xff1a sudo apt get install ros kinetic navigation 这种安装方法最简单 xff0c 新手或者不需要动naviag
  • linux---静态库和动态库的制作和使用

    静态链接和动态链接 静态链接 xff1a 生成可执行代码 xff0c 链接静态库 xff08 与代码位置有关的链接方式 xff09 xff0c 需要将代码拷贝到我们的源代码中才能运行 动态链接 xff1a 生成可执行代码 xff0c 链接动
  • 加一

    加一 描述 给定一个由整数组成的非空数组所表示的非负整数 xff0c 在该数的基础上加一 最高位数字存放在数组的首位 xff0c 数组中每个元素只存储单个数字 你可以假设除了整数 0 之外 xff0c 这个整数不会以零开头 示例 1 输入
  • STM32bootloader原理解释

    STM32bootloader原理解释 一 STM32的常规启动流程 STM32的内部flash地址起始于0x8000000 xff0c 一般情况下 xff0c 程序文件就从此地址开始写入 此外STM32是基于Cortex M3内核的微控制
  • 模糊PID基本原理及matlab仿真实现(新手!新手!新手!)

    有关模糊pid的相关知识就把自己从刚接触到仿真出结果看到的大部分资料总结一下 xff0c 以及一些自己的ps 以下未说明的都为转载内容 1 转自 https blog csdn net weixin 36340979 article det
  • VMware+ubuntu+win10笔记本实现笔记本连接WIFI且ubuntu既可以上网又能连接开发板

    背景 最近在学习imx6ull开发板的时候 xff0c 发现开发板通过网线连接笔记本电脑却无法ping通ubuntu xff0c 于是捣鼓了很久终于可以了 xff0c 却又发现ubuntu不能上网了 xff0c 经过一番查找资料和尝试 xf
  • 在windows上用vscode打造比vc++6.0好用的C/C++ IDE,适用编程小白

    准备 xff1a 1 安装MinGW xff0c 添加gcc gdb等编译调试工具bin目录 头文件Include目录 库lib的路径到系统环境变量 xff0c 安装LLVM 添 加Clang编译器所在bin目录到系统环境变量 具体操作百度
  • C语言数据结构——线性表的链式存储结构

    文章目录 线性表的链式存储结构1 基本概念2 设计与实现3 优点和缺点 线性表的链式存储结构 1 基本概念 链式存储定义 xff1a 为了表示每个数据元素与其直接后继元素之间的逻辑关系 xff0c 每个元素除了存储本身的信息之外 xff0c

随机推荐

  • 智能车浅谈——硬件篇

    目录 初识小车硬件系统1 电源系统线性电源开关电源 2 人机交互系统3 MCU最小系统4 传感器系统摄像头电感编码器 5 驱动系统 机械结构 17届完赛代码智能车系列文章汇总 前言 xff1a 作为一名老三本玩家 xff0c 笔者深知一些同
  • 智能车浅谈——图像篇

    文章目录 前言认识图像基本含义图像类型数字图像彩色图像灰度图像黑白图像 小结 图像处理图像压缩二值化固定阈值法大津法 图像降噪 xff08 腐蚀 xff09 寻边线 总结17届完赛代码17届完赛代码智能车系列文章汇总 前言 前面已经记录了智
  • 智能车浅谈——手把手让车跑起来(电磁篇)

    文章目录 前言材料准备备赛组车模硬件 练习组车模硬件方案 整车原理赛道信息获取及转向原理工字电感运放模块转向原理元素判断 电机及舵机控制原理 代码实现效果欣赏总结17届完赛代码智能车系列文章汇总 前言 电磁寻迹小车 之前智能车系列已经做了一
  • 手把手教你OneNET数据可视化

    文章目录 前言OneNET实现数据可视化效果一览发布项目 xff08 5 17更新 xff09 总结 前言 之前介绍了Hi3861使用MQTT协议接入OneNET实现数据的上传以及命令的下发 xff0c 本文主要是介绍一下如何使用OneNE
  • linux---进程间通信(ipc)之管道

    进程间通信方式 管道共享内存消息队列信号量本地套接字等等都能作为我们进程间通信的方法 操作系统提供进程间通信方式的原因 因为对于我们进程来说 xff0c 每一个进程都是相互独立的 xff0c 具有独立性 xff0c 如果我们需要两个不同的进
  • 嵌入式学习笔记——STM32的USART收发字符串及串口中断

    USART收发字符串及串口中断 前言字符串的收发发送一个字符串接收字符串需求 利用串口实现printf 中断中断是什么串口的接收中断以及空闲中断实现代码实现效果 总结M4系列目录 前言 上一篇中 xff0c 介绍了串口收发相关的寄存器 xf
  • 嵌入式学习笔记——PWM与输入捕获(下)

    输入捕获 前言输入捕获的概述框图输入通道部分比较捕获寄存器与事件生成 寄存器编程思路 实际需求配置流程打开对应的时钟配置GPIO为复用模式定时器的时基部分配置定时器输入通道部分配置定时器中断配置 代码 xff1a 运行效果 xff1a 需求
  • 嵌入式学习笔记——SPI通信的应用

    SPI通信的应用 前言屏幕分类1 3OLED概述驱动芯片框图原理图通信时序显示的方式页地址 列地址初始化指令 程序设计初始化代码初始化写数据与写命令清屏函数 初始化代码字符显示函数 总结M4系列目录 前言 上一篇中介绍了STM32的SPI通
  • 嵌入式学习笔记——IIC通信

    IIC通信 前言IIC概述通信特征物理拓扑结构IIC通信的流程IIC的特点 xff1a STM32的IIC通信GPIO模拟IICIIC的时序组成 xff08 主机对从机写入数据 xff09 1 起始信号2 器件地址与读写位3 从机应答信号5
  • 立创梁山派学习笔记——GPIO输出控制

    梁山派 前言开发板简介GD32F407ZGT6官方资源数据手册1 系统框图2 引脚复用表3 命名规则4 其他 用户手册固件库与PACK包 开发环境搭建立创官方的资料包资料齐活 xff0c 开发1 工程搭建2 使用寄存器点亮LEDGPIO数量
  • C51_day5:串口通信UART

    3 1 串口基本认知 串行接口简称串口 xff0c 也称串行通信接口或串行通讯接口 xff08 通常指COM接口 xff09 xff0c 是采用串行通信方式的扩展接口 串行接口 xff08 Serial Interface xff09 是指
  • 哈希表/哈希冲突及解决方法(较全)

    哈希表的概念请参阅他人文章 xff0c 关于哈希冲突的解决这篇文章基本都整理到了 xff0c 还有几个常见的面试题 解决hash冲突的几种方法 前导 xff08 题外话 xff09 xff1a 一 开放定址法 xff08 闭散列 xff09
  • 关于构造函数,拷贝构造函数,析构函数的调用顺序(1)

    导言 对象是由 底层向上 开始构造的 xff0c 当建立一个对象时 xff0c 首先调用基类的构造函数 xff0c 然后调用下一个派生类的构造函数 xff0c 依次类推 xff0c 直至到达派生类次数最多的派生次数最多的类的构造函数为止 因
  • vector的内存释放

    xff11 vector内存分配机制 C 43 43 中vector的一个特点是 xff1a 内存空间只会增长 xff0c 不会减小 即为了支持快速的随机访问 xff0c vector容器的元素以连续方式存放 xff0c 每一个元素都挨着前
  • MFC多人在线聊天室

    我已经在我的资源里上传了这个聊天室的代码了 基于MFC的C 43 43 的select模型的TCP聊天室 采用select网络模型 xff0c 支持多人同时登陆 xff0c 功能有上线 下线 群聊 私聊 使用CjsonObject进行数据传
  • linux---进程间通信(ipc)之共享内存

    前面我们讲解了进程间通信之管道 xff0c 这段我们讲解我们的共享内存 共享内存是所有进程间通信方式最快的一种 内存共享模型就像下面的图一样 xff0c 就是将物理内存映射到我们进程的虚拟地址上 xff0c 我们就可以直接操作我们虚拟地址空
  • Effective C++总结

    explicit关键字 C 43 43 中的explicit关键字只能用于修饰只有一个参数或者是其他参数有默认值的类构造函数 它的作用是表明该构造函数是显式的 而非隐式的 跟它相对应的另一个关键字是implicit 意思是隐藏的 类构造函数
  • 计算机网络(5)TCP之重传机制

    重传机制 超时重传数据包丢失确认应答丢失 快速重传SACKD SACK例一 ACK 丢包例2 xff1a 网络延时 TCP 是通过序列号 确认应答 重发控制 连接管理以及窗口控制等机制实现可靠性传输的 TCP 实现可靠传输的方式之一 xff
  • 中断与回调

    1 xff0c 回调函数 回调函数的原理是使用函数指针实现类似 软中断 的概念 比如在上层的两个函数A和B xff0c 把自己的函数指针传给了C xff0c C通过调用A和B的函数指针达到 当做了什么 xff0c 通知上层来调用A或者B 的
  • CUDA 程序的优化(3) 任务划分

    4 3 1任务划分原则 首先 xff0c 需要将要处理的任务划分为几个连续的步骤 xff0c 并将其划分为CPU端程序和GPU端程序 划分时需要考虑的原则有 列出每个步骤的所有可以选择的算法 xff0c 并比较不同算法在效率和计算复杂度上的