仅个人学习记录,有错误还希望被指出
本来只是想了解芯片中矩阵相乘的具体操作过程,看了一堆资料和讯息,就将了解到的记录下来了
一些混淆概念的介绍
推荐视频和链接:
1、芯片的介绍
2、硬件知识cpu/gpu/tpu/npu相关知识
3、简单的聊聊 NPU ,Tensor core
- CPU的主要计算单元是标量运算,当然后面出来的SSD,AVX指令不包含在里面
- GPU的主要计算单元是矢量运算,现在一般都是SIMD,然后多个SIMD组成MIMD
- NPU的主要计算单元是矩阵运算. 矩阵运算器就是tensor core.
- TPU英文全称Tensor Processing Unit,中文全称为张量处理单元,是深度学习算法的专用芯片。
- 一般情况下,npu和tpu就是ai芯片的核心,cpu和gpu就是简单处理一些计算。但这只是一般情况。
- 普通芯片
1、普通的芯片是遵循冯·诺依曼计算架构的。
2、cpu和并行加速运算的GPU都是属于普通芯片。
3、GPU在早期是为了处理图形数据。gpu相当于是10000个加法题找很多的幼儿园学生去做。效率也就高了很多。
4、cpu/gpu支持矩阵相乘的库:cpu(OpenBLAS、Intel MKL)、gpu(cuBLAS、cuDNN)
cpu中的矩阵计算
推荐链接:cpu中的矩阵计算
主要的计算过程就像下面写的代码一样:
for (unsigned int i = 0; i < hA; ++i){
for (unsigned int j = 0; j < wB; ++j) {
float C[i][j] = 0;
for (unsigned int k = 0; k < wA; ++k) {
C[i][j] += A[i][k] * B[k][j];
}
C[i][j] = Cij ;
}
}
就是按照最原始的线性代数的方式,不断的从内存中获取值并计算。下图采用的是行优先存储,也就是按照先一行行的存储再进行下一列的,正好符合上述代码。
具体计算过程见上述链接,本文只叙述大概的过程。
gpu中的矩阵计算
推荐链接:gpu中的矩阵运算、更详细的不同gpu计算方法
gpu中为了实现并行运算,主要的思想还是将矩阵分为多个子矩阵,然后同时计算他们的乘积。
具体计算过程见上述链接,本文只叙述大概的过程。
ai芯片
1、ai芯片也是遵循冯·诺依曼架构的
2、ai芯片主要用于处理深度学习,专门用于机器学习的芯片。(例如:Google TPU系列、NPU)
3、ai芯片的指标:
1)OPS(Operations Per Second);
2)MACs(Multiply-Accumulate Operations);
3)FLOPs(Floationg Point Operations);
4)MAC(Memory Access Cost)
卷积变矩阵相乘
在研究ai芯片矩阵相乘的过程中,要知道卷积变为矩阵相乘也是一个小内容。
例如:下图中为卷积变矩阵相乘的过程,但是途中的1234为位置编号,非数值。
ai芯片中的矩阵相乘
推荐链接:AI芯片:高性能卷积计算中的数据复用、芯片原理:NPU矩阵乘法加速详解
Nvidia TensorCore矩阵相乘过程
简单来说,就是在矩阵相乘的过程中矩阵a和矩阵b中需要相乘的每一个过程都分别计算出来。
第一步就是先分别计算需要的相乘部分;第二步就是按照矩阵相乘的要求,再进行相乘部分的相加。
下图是博主HaoBBNuanMM
给出的一个解释,很形象。
图中解释的是A*B+C的过程,1-3步解释的是A*B,第四步是相加。图中不好理解的时候可以倒着推一下过程,A*B的结果矩阵中(i,j)位置上的数是怎么得来的,再去看第2步和第1步。
上图只是针对4*4的矩阵过程,如果是8*8。此时并行就需要把大的矩阵转化为小矩阵进行操作计算。如图
总结:简单来说此时的计算就是需要拆分计算过程,优先乘法,再加法。需要添加一个3维的硬件计算单元。(这也是为什么nv要求用户模型中的矩阵大小最好是4的倍数)
Google TPU矩阵相乘过程
官网介绍:张量处理单元 (TPU) 是 Google 定制开发的应用专用集成电路 (ASIC),用于加速机器学习工作负载。如需详细了解 TPU 硬件,请参阅系统架构。Cloud TPU 是一项网络服务,可让 TPU 作为 Google Cloud 上的可伸缩计算资源使用。
TPU 使用专为执行机器学习算法中常见的大型矩阵运算而设计的硬件,更高效地训练模型。TPU 具有芯片上高带宽内存 (HBM),让您可以使用较大的模型和批量大小。TPU 可以组成 Pod 组,这样无需更改代码即可纵向扩容工作负载。
博主HaoBBNuanMM给出的一个计算过程解释,感觉比官网形象。
就是两个矩阵按照特定的方式进入MAC计算阵列,边相乘边相加。
类脑芯片
1、类脑芯片是不遵循冯·诺依曼架构,采用的是存算一体的方式进行计算和处理数据。
2、现在的类脑芯片研究主要是进行人工智能边缘计算,突破冯·诺依曼架构,提供一种新的创新发展路径。
3、现如今发展的类脑芯片有:
TrueNorth芯片(IBM-2014年)
英特尔Loihi芯片(英特尔-2018)
天机Tianjic芯片(清华大学-2019nature)
达尔文Darwin芯片(浙江大学和之江实验室-2020年)
英特尔Loihi2芯片(英特尔-2021年)
苏轼SHSHI芯片(中科院-2023年)
4、忆阻器也会处理double类型的数据。
5、即使现在有很多框架可以让snn跑在ai芯片上(例如:spikingjelly),但是实际结果并不具备压倒性的优势。
忆阻器
忆阻器是一种存算一体电子突触。忆阻器是一种具有记忆功能的非线性电阻。具体构造解说见:忆阻器芯片介绍
简单来说就是一种电阻,受流过的电荷影响而变化的元件。
说明:
忆阻器的电阻值通电结束后是不变的,需要重新输入权重的话,再来个电压给电阻值重新刷新即可
忆阻器阵列,其实是一种利用物理规律完成的模拟计算。
举例说明忆阻器的应用过程:
现在有一个矩阵和一个向量相乘:
然后如下图所示,小圆圈部分是忆阻器元件,我们可以通过在1,2,3号竖线上加电压,分别表示向量中的三个值。让9个小圆圈(忆阻器)的电导和矩阵中的数值对应起来。电流=电导*电压。那么此时横向方向上在电线位置测量一下总电流就是矩阵的结果。
应用:
1、2015年,IBM用一个12*12的阵列实现了三个字母的识别
2、2017年,清华大学吴华强团队用128*8的阵列实现了人脸识别等等
模拟计算中,计算精度是一个很大的考验。毕竟以二进制为代表的数字计算只需要管有电和没电,所有的状态都转化为0101数字再进行数学运算,对物理精度完全不关心。但是模拟计算中,电压电流就代表数值,每一点微小的变化就会带来计算数字的变化。
那么由此可见,提高模拟计算的精度就变成了一个新的问题。清华大学吴华强团队(研究出全球首个多阵列忆阻器存算一体芯片)的一些改进:
1、阻值变化不稳定问题
使用千层饼结构,导电层和不导电层交替叠加,使其成为了一个千层饼结构,并且在千层饼之上又增加了一个热导率低的热交换层。
2、电流的不稳定
增加限流电路
把忆阻器分为正向权重和负向权重。相同电压下,一个电阻值增加,一个电阻值降低。增加权重,更新正向。减少权重,更新负向。
3、算法
误差模型。通过压力训练,让AI自己寻找误差规律。
TureNorth
推荐视频:truenorth
1M的神经元,每个神经元256个轴突输入。
计算过程如下图:
横向的线是轴突,用于接受上一层神经元信息。
纵向的是树突,用于每个下层神经元接收信息。
横向和纵向的交叉点就是突触,每个突触会有不同的权重
过程:来自上层神经元的信息通过轴突再通过突触最后汇聚到下方的神经元。通过激活函数的处理,产生新的脉冲再传递到更下一层的神经元。
整个芯片没有全局的时钟,靠脉冲驱动。也就是说脉冲来了就计算,没有脉冲就休息。但是芯片还是基于CMOS架构。实际采用的还是传统的计算。物理结构如下图。
数据存储到SRAM中,再采用一个本地时钟进行同步。物理的神经元只有一个,它把时间分成小块,在一个计算电路中依次计算256个神经元的方程,此处没有并行运算。就和传统的cpu一样挨个计算。
Loihi芯片
218个神经内核和三个嵌入式x86处理器,每个内核有1024个尖峰神经单元。神经元突触的密度比TrueNorth高了三倍。
天机Tianjic芯片
速度比TrueNorth提高了10倍,带宽提高了至少100倍。最关键的是把ANN和SNN的计算逻辑合并到了一个计算单元中。通过巧妙的打开和关闭乘法运算单元,同时兼容ANN和SNN。
苏轼SUSHI芯片(中科院-2023年)
推荐视频:sushi超导神经形态芯片
1、采用超导神经形态芯片。需要极低的超导温度。
2、RSFQ:快速单通量量子
3、Josephson junctions约瑟夫森结
4、半导体中实现snn:硬件模拟出神经元,然后构建加法器,把脉冲权重累加一下做一个判断,超过阈值就输出一个1,然后发送给下一级的神经元。(TrueNorth就是这样实现的)
01脉冲通过晶体管的通断来维持,线路打开,电流通了就是1,关闭就是0。会有一个全局时钟来控制。
5、分离器可以复制脉冲
超导中会遇见的问题——————————————————————
同步时钟:超导中RSFQ就不可能像半导体一样,脉冲是随机产生的,所以需要一个同步时钟。
存储器拖后腿:无法满足快速计算的要求
芯片集成度不够:很难形成大规模
中科院的创新SUSHI——————————————————————
状态控制器:完成数据的存储;状态的翻转,会在脉冲来了的情况下触发四种状态:1)当前是0,不翻转;2)当前是0,翻转;3)当前是1,不翻转;4)当前是1,翻转;
状态控制器完成了激活函数的计算。将状态进行单位统一,便于定量分析。例如:500个状态就可以记录整个神经元的放电过程的所有状态,只需要用状态控制器表示这500个状态就可以,就相当于用计数器在记录倒水的次数。
那不同的状态怎么变为这统一定量下的标准呢?这就采用了超导脉冲神经网络(Superconductuing SNN,SSNN),绕开了传统的权重计算过程。
传统计算如下,第一个权重是0.3,第二个权重是0.5。
改进就是统一单位,数数量。本来是按照权重计算大小,现在只需要数脉冲个数了。使用分离器将脉冲进行复制。
网络框架
横向是轴突,纵向是树突。树突的末尾是神经元。横向和纵向交叉的地方就是突触。
权重的逻辑,就是统一单位定量的地方就在这里。每个突触上设计了一个开关,可以决定更新脉冲到达的时候要不要更新权重。这样就可以并行更新所有需要更新的权重了。
计算过程就是脉冲沿上一级神经元的轴突横向传过来,在每一个突触上根据权重被复制出不同的份数,然后这些脉冲再沿着树突传播,汇聚到神经元上。根据累计的状态来决定是否需要发射一个新脉冲到下一个神经元。
位片处理切片
为了在较小的规模下运行很大规模的网络,还采用了一种位片处理的方法把网络进行切片。按照神经网络的层分为很多片。这样就可以重复利用计算核心,以时间换空间。
相关问题
1、我还是不太清楚,类脑计算中的数值在权重计算时,还是以double类型存在吗?
2、类脑芯片中处理的数据是ann还是snn(spike neural network)还是都有呢?
3、类脑芯片的核心是存算一体还是说脉冲计算呢?
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_46806752/article/details/137228802
这里是 AI 研习社,我们的问答版块已经正式推出了!欢迎大家来多多交流~
https://club.leiphone.com/page/question
社长为你推荐来自 AI 研习社问答社区的精华问答。如有你也有问题,欢迎进社区提问。 话不多说,直接上题
问:如何理解 NVIDIA 新 GPU 架构 Turing 的 Tensor Core?
不久前,NVIDIA在SIGGRAPH 2018上正式发布了新一代GPU架构——Turing(图灵),黄仁勋称Turing架构是自2006年CUDA GPU发明以来最大的飞跃。Turing架构的两大重要特性便是集成了用于光线追踪的RT Core以及用于AI计算的Tensor Core,使其成为了全球首款支持实时光线追踪的GPU。
来自社友的回答
▼▼▼
@约翰尼德普
在关于Volta混合精度Tensor Core的几个谜团中,一个比较烦人的问题是4×4矩阵乘法的能力。Tensor Core是一种新型处理核心,它执行一种专门的矩阵数学运算,适用于深度学习和某些类型的HPC。Tensor Core执行融合乘法加法,其中两个4*4 FP16矩阵相乘,然后将结果添加到4*4 FP16或FP32矩阵中,最终输出新的4*4 FP16或FP32矩阵。
NVIDIA将Tensor Core进行的这种运算称为混合精度数学,因为输入矩阵的精度为半精度,但乘积可以达到完全精度。碰巧的是,Tensor Core所做的这种运算在深度学习训练和推理中很常见。
Tensor Core虽然在GPU里是全新的运算单元,但其实它与标准的ALU流水线并没有太大差别,只不过Tensor Core处理的是大型矩阵运算,而不是简单地单指令流多数据流标量运算。Tensor Core是灵活性和吞吐量权衡的选择,它在执行标量运算时的表现很糟糕,但它可以将更多的操作打包到同一个芯片区域。
Tensor Core虽然有一定的可编程性,但仍然停留在4*4矩阵乘法累加层面上,并且不清楚累积步骤是如何以及何时发生的。尽管被描述为进行4*4矩阵数学运算,但实际上Tensor Core运算似乎总是使用16*16矩阵,并且操作一次跨两个Tensor Core进行处理。这似乎与Volta架构中的其他变化有关,更具体地说,与这些Tensor Core是如何集成进SM中有关。
对于Volta架构,SM被划分为四个处理块或子核。对于每个子核,调度器每个时钟向本地分支单元(BRU)、Tensor Core阵列、数学分派单元或共享MIO单元发出一个warp指令,这就首先阻止了Tensor运算和其他数学运算同时进行。在利用两个Tensor Core时,warp调度器直接发出矩阵乘法运算,并且在从寄存器接收输入矩阵之后,执行4*4*4矩阵乘法。待完成矩阵乘法后,Tensor Core再将得到的矩阵写回寄存器。
在Tensor Core执行实际指令时,即使在使用NVVM IR(LLVM)的编译器级别上,也仅存在用于warp级矩阵操作的本征,对于CUDA++和PTX ISA,warp级别仍然是唯一级别。加载输入矩阵的形式是每个扭曲线程持有一个片段,其分布和身份均未指定。从广义上讲,它遵循标准CUDA核心的基于线程级别拼接的GEMM计算的相同模式。
一般而言,给定A*B+C Tensor Core操作,片段由A的8个FP16*2元素(即16个FP16元素)和B的另外8个FP16*2元素,以及FP16累加器的4个FP16*2元素或 FP32累加器的8个FP32元素组成。
在矩阵乘法累加运算之后,计算结果会分散在每个线程的目标寄存器片段中,需要在整个范围内统一,如果其中一个warp线程退出,这些低级操作基本上就会失败。
Citadel LLC团队的低级微基准测试揭示了许多Volta微体系结构细节,包括Tensor Core操作和相关的片段,与输入矩阵相比,它们都位于寄存器和标识中。他们观察到,子核核心以特定的拼接模式计算矩阵乘法,其中所有32个warp线程都在运行。
从概念上讲,Tensor Core在4*4子矩阵上运行,以计算更大的16*16矩阵。warp线程被分成8组,每组4个线程,每个线程组连续计算一个8*4块,总共要经过4组的过程,每一个线程组都处理了目标矩阵的1/8。
在一个集合中,可以并行完成四个HMMA步骤,每个步骤适用于4*2子块。这四个线程直接链接到寄存器中的那些矩阵值,因此线程组可以处理单个Step 0 HMMA指令,从而一次性计算子块。
由于矩阵乘法在数学上需要对某些行列进行复用,以允许跨所有8*4块并行执行,每个4*4矩阵被映射到两个线程的寄存器。在计算16*16父矩阵的4*4次子矩阵运算中,这将包括将连续计算的集合相加,形成16*16矩阵中4*8个元素的相应块。尽管Citadel没有对FP16进行测试,但它们发现FP16 HMMA指令只产生2个步骤,而不是4个步骤,这或许与FP16只占用的较小的寄存器空间有关。
通过独立的线程调度和执行,以及warp同步和warp-wide结果分配,基本的4*4*4 Tensor Core操作转换为半可编程16*16*16混合精度矩阵乘法累加。虽然CUDA 9.1支持32*8*16 and 8*32*16矩阵,但相乘的矩阵都需要相应的列和行为16,最终矩阵为32*8或8*32。
Tensor Core的运行方式似乎是NVIDIA GEMM计算层次结构的一个硬件实现的步骤,如CUTLASS(用于GEMM操作的CUDA C ++模板库)中所示。对于传统的CUDA核心,最后一步需要将warp tile结构分解为由各个线程拥有的标量和向量元素。使用WMMA API(现在表示张量核),所有这些都被抽象掉了,只剩下了需要处理的合作矩阵片段加载/存储和多重积累。积累发生在一个FMA类型的操作中。
NVIDIA深度学习Tensor Core全面解析(上篇)在寄存器级别上,NVIDIA在他们的Hot Chips 2017论文中提到“使用三个相对较小的乘法和累加器数据的4*4矩阵,可以执行64次乘加运算。”而增强的Volta SIMT模型的每线程程序计数器(能够支持张量核)通常需要每个线程2个寄存器槽。HMMA指令本身会尽可能多复用寄存器,所以我无法想象寄存器在大多数情况下不会出现瓶颈。
对于独立的4*4矩阵乘法累加,Tensor Core阵列在寄存器、数据路径和调度方面很有核能并没有物理设计,它只能用于特定的子矩阵乘法。
无论如何,从NVIDIA的角度来看,Volta不是一颗深度学习的专用ASIC,它仍然覆盖GPGPU的领域,因此保持CUDA可编程Tensor Core适用于GEMM / cuBLAS和HPC是合乎逻辑的。对于CUDA c++的CUTLASS来说,情况更是如此,因为它的WMMA API支持旨在为广泛的应用程序启用Tensor CoreGEMM操作。从根本上说,NVIDIA深度学习硬件加速的发展与cuDNN(以及cuBLAS)的发展有很大关系。
详细信息请参考:
接着讲架构层面的知识。
cpu发射指令 or NPU自己取指
两种方案:
第一种,类似传统的协处理器(如早期的FPU),是CPU发射指令,FPU负责执行单条(或线性顺序)指令和写回,而其他流水线的调度和控制(取指 Load/Store bypassing等)是CPU在做。
第二种,类似GPU,因为GPU自己的流水线很复杂,必须自己来独立取指,控制自己的运行。更像是是个独立的CPU,只是共享主存,其他的和CPU的交互如同我们接着调试器的MCU。
这类NPU内部都会有一个小的cpu,用于指令预取、控制(分支 跳转)、流水线。
华为昇腾
以下是一些实例:
CPU发射指令:TPU(Google) MMA(TI) NVDLA(nVidia) Ethos-U55(ARM)等
NPU取指: GPU(nVidia) Ethos-N(ARM) Hexagon(高通) 昇腾 寒武纪 等
其中NVDLA比较有意思,我们看一下它有两种用法:
第二种,large的用法,即增加了一个MCU,可以用来跑KMD(Kernel Mode Driver, 离硬件最近的Runtime),貌似对应了第二种选择,理论上可以用来做很多事情,比如魔改成一个取指和流水线调度器。
但是,我看完NVDLA开源的部分,差的太远,因为:第一,编译只生成顺序图(layers);第二,指令并非risc,而是高度针对cnn的计算指令(见下图),缺乏控制指令。缺乏指令优化的空间和必要性。
所以NVDLA要么退出,要么完全重新设计。
我们看到,这几年的现实是,第一种选择的NPU都命运多舛,大部分即将退出应用舞台。
而当时选择第一种方案的理由,只有两点,省事儿(不改变现有架构)+省钱(不占用面积)。
先不提这些大公司到底是人员过剩还是人员短缺,我们看一下增加一个mcu多出多少die size:
寒武纪早期NPU,大约对应今天的10Tops
MCU面积占比不超过1/10。
但牺牲的灵活性,则比较大:要求cpu和npu间更高的同步性(FIFO没有RAM大,且不能jump);任何可能的控制(分支 条件跳转)调度都需要从主存绕回CPU处理。
作为第二种的典型方案,我们看高通的Hexagon:
非常齐全的cpu配置,多组reg,分支预测,TLB虚拟地址转换。
考虑以下三种情况:
- 1个core跑2个网络
- 2个core跑1个网络
- 1个core跑1个网络
第一种情况,是kernel调度,cuda里也叫stream调度,将两个独立的网络压到一个FIFO里。所有方案的这部分操作都是由CPU完成的。
第二种情况,是多core的调度,这部分上次讲过了,有大小核方案的区别。nVidia是npu侧有个block scheduler,但是昇腾等还是由CPU完成的。
而本节所讲的指令调度,是指的第三种场景。即使是单core单stream,如果有片上动态调度的指令的能力,理论上可以更大范围的实现指令的动态切换。
有取指能力的NPU,理论上是可以在网络中增加if-else的(如同nVidia的GPU),动态改变网络。当然,能不能实现是另一回事儿,需要做到隐藏存储延时,这很难,也是目前国内欠缺的,造成了虽然架构比TPU灵活,但实际使用时区别不大。
矩阵计算单元 标量 or 向量
还是两种方案:
第一种,spatial型,如脉动阵列(systolic array)。也叫标量MAC。
第二种,向量MAC,时间型(一组向量点积 一组向量点积..),如H树阵列,。
前者:TPU(Google) 众多初创公司(CGRA Wave Inferentia Samba-Nova) 等
后者: GPU(nVidia) MatrixCore(AMD) 昇腾 寒武纪 等
第一种,我个人认为属于另类。其基本的逻辑是,一个运算之后,结果不用写回寄存器或cache,而是直接送给邻近单元做下一个运算:
这一波热潮的开头者,还是google的TPU(上图),衍生了众多变种,架构都同下图
一个标量运算单元称作一个PE,构造都长这样:
采用这种方式唯一的动机,在于抚平处理速度和内存速度的不平衡。
第二种方法用流水线,一样可以实现同样的效果。只是除了运算单元要n倍之外,解码和寄存器读写单元也要n倍,占用资源更多。
而第一种方法的代价,则是不够灵活,只适合做特定运算。
比如做卷积:
做矩阵乘法:
在卷积的例子中,一个权值固定在一个PE中,输入值流动,叫做Weight Stationary数据流。这是主流方式。
除此之外,还有一个输出的部分和固定在一个PE中,权重和输入值流动,叫做Output Stationary数据流。
显然,二维卷积和全连接层的数据复用特点不一样,前者权重被重复使用,后者一个权重只用一次,而输入的使用次数更多。
这也是这种空间方法要兼容不同网络计算时遇到的困难。
我们看第二种:
这时候最小单元PE不再是标量计算,而是向量计算。
C=A*B+D
输出是C矩阵里的一个数,等于A的行向量乘以B的列向量加上D的一位。
不要被下面这个图骗了,nVidia并没有公开tensor core的任何设计。
也并非一个clock这个矩阵mac就算完了。于是很多人在外围去测试破解。
这篇文章想看,tensorcore究竟多长时间算完一次。(流水线有多长)
结果是24个cycles,完成一次HMMA16816指令,也就是算一次:A=16*8 B=8*16 D=16*16
一次HMMA16816指令,tensorcore要算几次呢?如下图所示(下图是算16*16的,自己换成16*8的)16次。
似乎可以猜测,之所以HMMA一次要算更大的矩阵,就是因为要掩盖流水线,减少数据依赖竞争。而一次tensor core计算的latency可能是8(流水线阶段数)。
最终对比,来自陈云霁老师的书,
总结一下,第一种标量单元方式,最大的优点是减少访存(32vs512),相应的缺点是灵活性差,只能支持提前定义好的特定算子。
1-D 向量计算单元
NPU有三种情况:
- 全是Vector Unit,矩阵计算也靠它;
- 没有Vector Unit,向量计算也靠Tensor Core;
- 有Vector Unit,作为Tensor Core补充。
目前主流的是第三种。
因为现在的CPU DSP都已经有SIMD了,512bit数据长度,所以协处理器模式的都支持向量计算。
Vector计算单元占用的面积只是张量计算的1/n(n是矩阵维度),所以NPU也不差这点。
向量计算单元用来做pooling, activation, normalization这些,理论上来说,性价比更高,让张量计算单元专心去做矩阵和卷积操作。
但其实,tensor core用来做这些reduction和element-wise操作也不见得慢:
着篇文章直接把一个1D向量,填满了两个矩阵,用来做reduction操作。最终取得了比CUB库(使用CUDA)更好的结果。
但是,我们依然容易理解增加一个向量计算单元的好处
- 适合更小的向量计算,尤其是国内普遍大核的现状下;
- 相当于multi-issue,增加了指令并行性。目前国内车载NPU,用于一体机的普遍是只用1个tensor-core,这时增加一个向量core就显得能大幅提速了。
最后提一下Reduction Tree:
Reduction Tree原本就是朴素的在做MLP,追求最少的乘法器和加法器。后来因为MLP不流行了,Transfomer开始流行,所以比较尴尬。
但是,用Reduction Tree可以实现动态配置,有研究用于解决稀疏计算的问题。见上图,挺复杂,需要专门得稀疏检查,建立索引,建立映射,固件算控制寄存器得配置。
目前稀疏问题的处理,小核处理稀疏问题有天然优势(特别稀疏的,可以全0);不太稀疏的(比如一半是0),用Compressed Sparse Row (CSR) 非Reduction Tree得效率可以追平Reduction Tree。
所以,上面这类复杂的Reduction Tree方案,尚无商用产品。
存储 gather-scatter
计算单元和存储单元速度的巨大不一致,是所有计算机体系架构的核心问题,NPU也不例外。
CPU和NPC共享一块主存地址,和CPU自己用的地址隔离;在GPU里是独立的显存芯片。一般是CPU维护堆,NPU维护栈。(对于3.1里的第一种类型,个人理解没有主动操作主存的能力,只能接收CPU发来的配置信息,完成既定动作,写入既定地址。)
指令、权重、输入 是相互独立的数据通道。考虑到权重和输入数据量较大,显然指令要提前预取很久,这样才能在exe阶段,准备好数据。并且写回cache和主存,也需要很多pipeline stage,不能让计算stall。
在编译阶段,就要静态指定哪些数据要写回cache和ram,哪些数据直接进入下一层计算。后者动态再打个tag,当一个数据再也不需要使用时,就可以丢弃了,否则一层cache满时要逐层write back。
继续详细分析之前,我们先看一个目前很有代表性的算子:
gather就是张量向量数据按index“查表”,它看上去很简单,但问题在于需要随机的去读Rin,并不是地址连续的去读Rin。
Scatter是随机的去写Rout,这更难一些,因为读只有hit和miss两种情况,但写就有cache一致性维护的问题。
稀疏运算,可以通过gather来把非0元素提取出来。
gather在CV最近很常见,因为BEV流行的原因(把2D的featuremap映射到BEVmap,就是对featuremap的index查表),下面只讲gather。
如果Rin维度很小,全在reg或L1 cache里,那理论上就没有额外的开销。可实际上,Rin是网络输入data,一般最多在片上sram。
我们知道顺序取数时,可以又dcache做预取,一次取很多等着慢慢用。
可是不停的随机去很大的sram里面取一个数,是很耗能量的,如果miss了,要去ddr里面去取,那计算就卡住了,慢200-1000倍,满足不了部署要求。看下图右侧时间柱的高度。
我们看一下nvidia的解决办法,业界楷模,当很多家都不知道存在这个问题时,nvidia已经实现了完美方案:
随机读才是常态,顺序读硬件加了一个合并器(Coalescer),统一处理可能的打包优化。反其道而行之。可见nvidia最初做GPGPU就是瞄着最通用的场景去的。
我们再来看高通的解决办法:
增加了一个TCM,可以软件取data,更大的带宽。注意到这是独立于原有dcache通道外新加的一路通道。
“ A worst-case gather from a hypothetical 512 KB, 8-way L2 could require 128 * 8 = 1024 tag comparisons. Since the TCM isn’t a cache, it avoids the overhead of tag checks. Hexagon doesn’t even try to do scatter and gather operations on cacheable memory, and only does them on the TCM.”
TCM不是cache,不用比较tag(判断是否hit)。从中我们也可以看出,没有经过特殊硬件设计的话,scatter-gather是很难实现的。
比如,三星第四代NPU,去年年底才预告支持scatter-gather。
同样是TCM方案
这次先讲到这里。
后面会更细致的做一些NPU内存响应和算力负荷的定量分析,对一些具体的模型。
我的计划,是讲完 NPU <> AI编译 <> 模型 <> 应用 完整图景。
每个部分都还有不少值得写的内容,比如 存算一体,多面体理论,PAC理论,一些CV里的理论等等。
当这些东西串起来,有可能会有新的发现。和排序被研究的如此透彻相比,目前CV NLP的领域还大部分未被发掘。
最后聊个轻松的话题。
经常看见的场景:
算法工程师说某NPU算子支持的不好。
当然如果是pytorch都没匹配好,那是NPU的问题。或者性能严重偏离,是编译参数优化的问题。这些之前的文章都讲过。
但有一种情况,就是
要用某个NPU就不支持的模型。
我们从本文看到,NPU很可能设计之初,就不支持一些算子,一些数据操作,一些输入大小,稀疏,控制指令;或只能以固定的权重或输入复用方式进行计算。
这是去麦当劳非要点卤煮。
先天局限。