模型性能分析 - yubo105139/paper GitHub Wiki

TOC

使用torchstat 工具得到模型的各项指标

https://github.com/Swall0w/torchstat

安装:

pip install torchstat

使用:

from torchstat import stat
stat(model, (3, 32, 128))

torchstat 返回的结果: image

... image

计算过程举例:

第0行卷积层的计算(每个参数占4个字节)
# 参数量
params = 卷积核参数量
       = k*k*c_in*c_out+c_out = 3*3*3*8+8 = 224

# 访存量
memory = [(卷积核参数量+输出特征图参数量)*4/1024/1024]MB
       = [(k*k*c_in*c_out + H_out*W_out*c_out)*4/1024/1024]MB
       = [(3*3*3*8+8) + 32*128*8]*4/1024/1024 MB = 0.12585MB 

# 乘法和加法的总的运算次数
MAdd = 乘法运算次数 + 加法运算次数
	 = 卷积的乘法运算次数 + 卷积中的加法运算次数 + 偏置加法运算次数
	 = k*k*c_in*H_out*W_out*c_out + (k*k*c_in-1)*H_out*W_out*c_out + H_out*W_out*c_out
	 = 3*3*3*32*128*8 + (3*3*3-1)*32*128*8 + 32*128*8
	 = 884736+851968+32768 = 1769472

# 浮点运算次数
Flops = 卷积中的乘法浮点运算 + 偏置的浮点运算
	  = k*k*c_in*H_out*W_out*c_out + H_out*W_out*c_out
	  = 3*3*3*32*128*8 + 32*128*8
	  = 884736 + 32768 = 917504

# 读内存
MemRead(B)	= (输入特征图参数量 + 卷积核参数量)*4B
			= (3*128*32 + 3*3*3*8+8)*4B = 50048 B
			
# 写内存
MemWrite(B)	= (输出特征图参数量)*4B
			= 32*128*8*4 = 131072 

# 持续时间占比
duration(%) = (module_endtime - module_starttime)/total_duration

# 内存读写量
MemR+W(B) = MemRead + MemWrite = 50048+131072 = 181120 B
  

# TOTAL
Total params: 146,826  # 模型的总参数量
Total memory: 4.65MB   # 模型的访存量
# 此处的M为million,即303.08*10^6MAdd
Total MAdd: 303.08MMAdd  # 模型乘法操作和加法操作的次数 
# 152.68 *10^6 Flops
Total Flops: 152.68MFlops # 模型浮点运算次数 (看代码此处未考虑卷积内的加法运算次数)
Total MemR+W: 9.99MB  # 根据当前的输入特征图 计算的模型访存量

Attention:

memory 和 MemR+W 的区别

通常使用memory来描述模型的访存量, 因为 MemR+W 的值是依赖于输入模型的特征图的大小而计算的.

模型计算密度

对conv替换成ghost的模型进行轻量化理论指标的对比。

# 替换前
## torchstat 计算结果
Total params: 146,826
Total memory: 4.65MB
Total MAdd: 303.08MMAdd
Total Flops: 152.68MFlops
Total MemR+W: 9.99MB

# 替换ghost后
## torchstat 计算结果
Total params: 81,150
Total memory: 12.72MB
Total MAdd: 170.27MMAdd
Total Flops: 86.29MFlops
Total MemR+W: 27.88MB

测试集上平均每张图的推理时间对比:

模型\测试集 test250 test295
原模型 212.53ms 250.04ms
改进后的ghost模型 408.00ms 481.28ms

替换后的模型参数量减少近一半,乘加运算次数也减少一半,访存量增加, 由4.65MB增加到12.72MB

计算原模型和改进后ghost模型的计算密度

  1. 原始模型计算密度:
152.68MFLOPs\div4.65MB = (152.68*10^6) \div (4.65*1024*1024) = 31.31 FLOPs/Byte

每一个字节的内存交换,能够完成约31次浮点数计算.

  1. ghost模型计算密度:
86.29MFLOPs \div12.72MB = (86.29*10^6)FLOPs\div (12.72*1024*1024)Byte = 6.47 FLOPs/Byte

每一个字节的内存交换,能够完成约6次浮点数计算.

平台计算密度

显卡参数

上面实验的测试平台为GeForce RTX 2070 super, 使用nvida-smi -a查看.

下图参数来源:https://www.nvidia.com/en-us/geforce/graphics-cards/rtx-2070-super/#specs image

RTX-OPs

RTX Operation Per Second, 每秒完成计算操作的数量,越大越好.是NVIDIA针对Turing架构显卡启用的新的性能指标,Turing架构配合RTX计算一张画面,需要经过实时光线追踪、32位元浮点运算着色、32位元整数运算着色、深度神经网络后处理等步骤,要计算RTX OPs,需要整合这几个类型来计算。如Quadro RTX 6000的RTX OPs是84T 78T,Quadro RTX 5000的RTX OPs是62T。

Giga Rays/s

指该显卡每秒可以追踪的光线数量,越高越好。8Giga Rays具体是指每秒追踪8百万光线。

Boost Clock

OC为overlocking 超频频率

Base Clock

核心频率. 显卡处理图像的频率大小,即显卡的核心频率越高,它处理图像的速度和效率也就更高。

Memory Speed

显存速率:是指显存每秒传输数据位数,单位是bps,描述的是数据流量;

Standard Memory Config

标准显存配置, 8GB的GDDR6显存类型

Memory Interface Width

显存位宽, 256-bit

Memory Bandwidth

显存带宽:在一定时间内可交换的数据量, 它以byte/s为单位.

可以获得以下参数信息

cuda核心数: 2560
核心频率:1605MHz
显存频率: - 
显存位宽:256bit
显存速率:14Gbps  (那么显存带宽 = 14Gbps*256bit/8 = 448GB/s)
显存带宽:448GB/s

此处用显存速率来标识显存,显存带宽计算如下:

显存带宽(GB/s)=显存数据速率(Gbps)*显存等效位宽(bit)/8

我们要计算平台的最大计算密度, 现在已知了显存带宽, 需要得到该显卡的算力.那么如何计算获取显卡的算力?

https://developer.nvidia.com/zh-cn/cuda-gpus#compute上可以获取每种显卡类型的算力. 此处的计算能力由版本号表示,主要是对其核心架构的一种标识.

而我们需要的算力是显卡每秒钟尽全力所能完成的浮点运算数.

如何计算显卡的理论算力峰值?

此处涉及到一些GPU架构的知识.

理论算力峰值计算公式如下:

PeakFLOPS= F_{clk} * N_{SM} * T_{ins} *2

​ 其中$F_{clk}$ 为 GPU 核心的运行频率,$N_{SM}$ 为 GPU SM 数量,$T_{ins}$为特定数据类型的指令吞吐,后面乘 2 是因为乘加视作两次浮点运算。

单精度和双精度理论峰值如下:

FP32\_PeakFLOPS = F_{clk} * FP32 Cores* 2
FP64\_PeakFLOPS = F_{clk}*FP64 Cores * 2

上述 SM数量以及 特定数据类型的指令吞吐,根据显卡的架构类型,以及SM架构中的SP组成得到.

那么根据上面的参数,2070s的算力计算为:

2560 * 1605MHz *2 = 8217600 = 8217.6GFLOPS

CUDA Runtime API 获取并计算显卡的信息.

得到本机的显卡信息如下:

GPU count = 1
=================GPU #0=================
GPU Name = GeForce RTX 2070 SUPER     # GPU名称
Compute Capability = 7.5				# GPU算力, 此处的算力指的是架构版本号
GPU SMs = 40							# SM多流处理器个数
GPU CUDA cores = 2560					# cuda 核心个数
GPU SM clock rate = 1.815 GHz			# 核心频率
GPU Mem clock rate = 7.001 GHz			# 显存频率
FP32 Peak Performance = 9292.800 GFLOPS		# 单精度浮点峰值算力
FP16 Peak Performance = 18585.600 GFLOPS	# 半精度浮点峰值算力
INT8 Peak Performance = 37171.200 GFLOPS	# 整型峰值算力
Tensor Core FP16 Peak Performance = 74342.400 GFLOPS  #
Tensor Core INT8 Peak Performance = 148684.800 GFLOPS

可以看到官网给出的base clock为1605MHz, 但是API 得到的显卡频率为 1815MHz, 甚至超过了官网的boost clock. 这应该是公版显卡和我们实际使用的非公版显卡导致的差异. 在实际计算中以 实际使用的显卡频率来计算.

平台最大计算密度

GeForce RTX 2070 SUPER 的最大计算密度为:

9292.8GFLOPS / s \div 448GB/s = (9.062*10^{9})FLOPs/s\div(448*1024*1024*1024)Byte/s = 19.318FLOPs/Byte

以tesla V100为例

teslaV100

image

PCI-Express(peripheral component interconnect express):是一种高速串行计算机扩展总线标准.

NVLink: 能够在GPU-GPU以及GPU-CPU之间实现高速大带宽直连通讯的快速互联机制。 相较于PCIE总线能够达到更高的带宽值.

CoWos HBM2堆叠式显存容量和CoWos HBM2堆叠式显存带宽

CoWoS(Chip-on-Wafer-on-Substrate) 一种2.5D封装技术,把多个小芯片封装到一个基板上.

v100参数
核心频率:1455MHz
内存总线宽度:4096Bit
显存频率:1758MHz ==1.758GHz  (显存带宽=1.758*4096/8=900.0GB/s)
显存带宽:900GB/s
v100的单精度浮点运算性能:14TFLOPS/s

使用CUDA Runtime API获得40服务器显卡信息如下:

GPU count = 1
=================GPU #0=================
GPU Name = Tesla V100-PCIE-32GB
Compute Capability = 7.0
GPU SMs = 80
GPU CUDA cores = 5120
GPU SM clock rate = 1.380 GHz
GPU Mem clock rate = 0.877 GHz
FP32 Peak Performance = 14131.200 GFLOPS
FP16 Peak Performance = 28262.400 GFLOPS
INT8 Peak Performance = 56524.800 GFLOPS
Tensor Core FP16 Peak Performance = 113049.600 GFLOPS

v100的最大计算密度为:

14TFLOPs / s \div 900GB/s = (14*10^{12})FLOPs/s\div(900*1024*1024*1024)Byte/s = 14.487FLOPs/Byte

Roofline 模型性能分析

计算密度是指一个程序在单位访存量下所需的计算量,单位是 FLOPs/Byte。

模型的计算密度(FLOPs/Bytes) = 模型的计算量(FLOPs) \div 模型的访存量(Bytes)

RoofLine 模型是一个用于评估程序在硬件上能达到的性能上界的模型,可用下图表示:

image

模型的计算速度(FlOPs/s) = min(模型的计算密度 * 平台内存带宽, 平台的峰值计算速度)

当程序的计算密度I较小时,程序访存多而计算少,性能受内存带宽限制,称为访存密集型程序.即图中橙色区域。

程序性能上界=模型计算密度×内存带宽,表现为图中的斜线.

访存密集型程序 , 模型的计算密度越大,程序所能达到的速度上界越高,但使用的内存带宽始终为最大值。

如果计算密度I较大,程序性能受平台最大计算峰值限制,称为计算密集型程序,即图中蓝色区域.

程序性能上界=平台的峰值算力

此时计算速度不受模型的计算密度影响,但模型计算密度越大,所需内存带宽就越少。

基于Roofline模型的 enocr性能分析

对于2070 super

image

影响原模型的是平台的峰值算力

限制了ghost模型的是平台的显存带宽.

Tesla V100 image

原模型在v100上也属于计算密集型程序, 能够充分利用v100的算力

ghost模型在v100上仍受到显存带宽限制.

模型推理时间

通常加速模型, 考虑的是模型的推理时间.

当模型处于访存密集区时:

程序推理时间 = 模型访存量 \div 平台带宽

当模型处于计算密集区时:

程序推理时间 = 模型计算量 \div 平台理论算力

enocr改进分析

​ 当前的enocr模型处于计算密集区, 推理时间与模型计算量呈正比, 所以我们需要改进模型的方向是, 减小模型的计算量 同时 尽可能不增大模型的访存量, 即在减小模型计算量的同时,少使用访存密集型算子.

Others relate

GPU架构

以安培架构的GA100显卡为例,总体架构如下: image

A100 SM 的架构细节如下图所示: image

  • GPC —— 图形处理簇,Graphics Processing Clusters
  • TPC —— 纹理处理簇,Texture Processing Clusters
  • LD/ST 是load store unit,用来内存操作的
  • SFU是Special function unit,用来做cuda的intrinsic function的,类似于__cos()这种。
  • SM —— 流多处理器,Stream Multiprocessors.多个SP加上其他的一些资源组成一个streaming multiprocessor。也叫GPU大核
  • HBM2 —— 高带宽存储器二代,High Bandwidth Memory Gen 2
  • SP:最基本的处理单元,streaming processor,也称为CUDA core, 为图中的FP32。(CUDA Core是Single Precision的,也就是计算float单精度的,其他部分不算做在内.)

可以看到上面SM架构中, 一个SM下有 [16(fp32)]*4= 64个 CUDA Core

根据上面的总体架构, 可以知道, 一张GA100卡内有8个GPC (图形处理簇) , 每个GPC中有16个SM(多流处理器), 每个SM中包含四个区块能并行执行4组不同指令序列, 每个区块有16个FP32计算单元, 16个int32计算单元,8个FP64计算单元, 4个tensor core.那么有  8x16=128 个SM. 有  8x16x4x16 = 8192 个FP32计算单元, 即8192个CUDA Core有 8x16x4x1 = 512 个tensor core 单元

GA100 以及基于 GA100 GPU 实现的 A100 Tensor Core GPU 内部资源如下表所示: image

考虑芯片工艺水平以及整板功耗以及商业竞争导致的对刀法的要求, 实际的到手的A100为右边的阉割版本.

ref:

聊聊 GPU 峰值计算能力(包含一点GPU架构基础)

Nvidia GPU架构 - Cuda Core,SM,SP等等傻傻分不清?

GPU架构,由sp,sm,thread,block,grid,warp说起

不同显卡的算力计算

显存带宽的计算

显存频率 : 通常标识默认情况下,某显卡内部的显存在显卡上进行工作的频率,通常以MHz(兆赫兹)为显存频率计数单位. 显存带宽的计算 如果用显存频率来标识显存, 那么带宽计算如下:

显存带宽(GB/s)= 显存实际频率(MHz)*显存数据倍率*显存等效位宽(bit)/8			  = 等效显存频率*显存等效位宽(bit)/8

其中显存实际频率乘以显存数据倍率得出的数就是我们常说的等效显存频率.

等效位宽就是显存颗粒位宽乘以显存颗粒数得出的值。比如A100中有HBM2显存颗粒6个, 每个HBM2显存颗粒

显卡的核心频率和显存频率区别

两者相互制约,相互联系.两者的关系就像CPU和内存的关系. 核心频率高但是显存频率底就会制约显卡性能的发挥,所以两者都要高是最好的 不过通常情况下主要还是核心频率高的好.

​ 显卡的核心频率是指显示核心的工作频率,其工作频率在一定程度上可以反映出显示核心的性能,但显卡的性能是由核心频率、显存、像素管线、像素填充率等等多方面的情况所决定的,因此在显示核心不同的情况下,核心频率高并不代表此显卡性能强劲。 ​ 显存频率是指默认情况下,该显存在显卡上工作时的频率,以MHz(兆赫兹)为单位。显存频率一定程度上反应着该显存的速度。 ​ 决定显卡性能的三要素首先是其所采用的显示芯片,其次是显存带宽(这取决于显存位宽和显存频率),最后才是显存容量。一款显卡究竟应该配备多大的显存容量才合适是由其所采用的显示芯片所决定的,也就是说显存容量应该与显示核心的性能相匹配才合理,显示芯片性能越高由于其处理能力越高所配备的显存容量相应也应该越大,而低性能的显示芯片配备大容量显存对其性能是没有任何帮助的。

显存问题

对于显存 ,主要考虑 显存频率,位宽,显存类型以及显存容量.

显存频率和位宽决定了显存的带宽. 而显存带宽是计算平台计算密度的因素.

显存类型

​ 显存类型包括:SDRAM(Synchronous DRAM,同步动态随机存储器), 和 SGRAM(Synchronous Graphics DRAM,同步动态图像随机存储器)等

​ 但通常可以查到的显卡参数中标识的显存类型为:GDDR5,GDDR6,HDM等. 目前的主流显卡类型一般为GDDR系列和HDM系列.

​ GDDR和HDM其实指的是显存颗粒类型.显存颗粒是显存的物理存储组成单元。显存颗粒的类型(如GDDR5),单颗容量(MB),单颗位宽(bit),理论显存频率(MHz)影响着显卡的性能。显存上面看到每一个显存颗粒上都会有一串编号,可以从相应的官网中找到该显存颗粒的详细规格,包括显存容量、速度、位宽、带宽等参数信息.

显存容量

决定着显存临时存储数据的多少.显存可以看成是空间,类似于内存.显存用于存放模型,数据.显存越大,所能运行的网络也就越大.

GDDR系列显存和HBM显存

频率越高, 位宽越宽, 那么显存带宽越大.

**GDDR(Graphics Double Date Rate SDRAM)**是一种专为图形处理单元 (GPU) 设计的同步动态随机存取存储器 (SDRAM)。

**HBM (High bandwidth memory)**高带宽内存,是一种用于 3D 堆叠同步动态随机存取存储器 (SDRAM) 的高速计算机内存接口,最初来自三星、AMD 和 SK 海力士。它与高性能图形加速器、网络设备、高性能数据中心 AI ASIC 和 FPGA 以及一些超级计算机(如 NEC SX-Aurora TSUBASA 和富士通 A64FX)结合使用.它通过将存储芯片垂直堆叠在一起,缩短数据传输的距离,同时允许更小的外形尺寸,获得更大的位宽,从而使得显存带宽大幅度提升。

GDDR显存系列目前已经出现GDDR6X, HBM 目前已经出现HBM2,HBM2E, 即将出现HBM3.

ref:

WIKIPEDIA-GDDR SDRAM

WIKIPEDIA-High Bandwidth Memory

What Are HBM, HBM2 and HBM2E? A Basic Definition

NVIDIA为什么在游戏卡上死活不用HBM2显存?

关于Compute Capability?

参考官方解释:https://docs.nvidia.com/cuda/cuda-c-programming-guide/#compute-capability

image 这里的计算能力由版本号表示,有时也称为“SM 版本”。此版本号标识 GPU 硬件支持的功能,并在运行时由应用程序使用以确定当前 GPU 上可用的硬件功能和指令。

计算能力包括主要修订号 X 和次要修订号 Y,并用 X.Y 表示。

具有相同主要修订号的设备具有相同的核心架构。主要修订号为基于 Volta 架构的器件为 7,基于 Pascal 架构的器件为 6,基于 Maxwell 架构的器件为 5,基于 Kepler 架构的器件为 3,基于 Fermi 架构的器件为 2,和 1 适用于基于 Tesla 架构的设备。

次要修订号对应于核心架构的增量改进,可能包括新功能。

TF32,BF16数据类型?

Tensor Core FP16 和FP16的区别?

refs:

turing 架构白皮书: https://images.nvidia.com/aem-dam/en-zz/Solutions/design-visualization/technologies/turing-architecture/NVIDIA-Turing-Architecture-Whitepaper.pdf

显卡参数及性价比查询:https://technical.city/zh/video/GeForce-RTX-2070-Super