本篇来讲点NPU的high level顶层设计思想,或许在指导你理解NPU和平时的编程工作中有所帮助。
1. NPU的计算机体系结构设计思想
送一本上图中的免费电子书:foxsen.github.io/archbase/in...
1.1 并行分布式计算思想
如果不是集中式的存储和计算,会分散到不同的核和存储。计算单元跟存储单元在芯片布局上是在一块的,这样距离近技术速度快。
数据总线和控制总线都是环线,就像一条环形的大路,路两边均匀分布不同的存储和运算单元,可以进行广播给各个存算一体的处理单元。
广播对并行处理很重要
对于多个处理单元,不能一个一个的给数据和控制信号,太慢了,不利于并行处理,需要这些处理单元几乎同时拿到数据,不是自己的可以丢弃,但是自己的就必须第一时间拿到并进行处理,虽然设计上有硬件的浪费,但是为了快速软件都可以硬化,其实就是用成本来提高了运行速度。
并行要用到两个概念SIMD和MAC,下面介绍下:
什么是SIMD?
SIMD(Single Instruction Multiple Data,单指令多数据)是一种并行处理技术,它允许处理器在一个周期内对多个数据执行相同的指令。这种技术特别适合于神经网络等需要大量并行计算的应用场景。在NPU中,通过一维MAC来实现SIMD的并行处理,可以显著提高计算效率。
什么是MAC?MAC(Multiply-Accumulate,乘累加)运算是指执行两个数的乘法,并将结果累加到一个累加器上的操作。在神经网络中,尤其是深度神经网络中,大量的计算涉及到矩阵乘法和向量加法,这些操作都可以通过MAC运算来实现。
1.2 存算一体思想
存算一体对并行处理很重要
并行计算需要立即拿到数据和指令,传统的CPU架构指令到达时去找数据,这时候数据可能在cache立即拿到也可能不在cache里面,在DDR里面,或者在Flash、硬盘里面等,这时候就比较耗时了,本来并行就需要协同处理,一个并行子任务就可能卡住整个计算的队列,就像流水线,一个工人歇了其他人就干瞪眼,充满了不确定性。
有了存算一体的确定性,就可以衍生出一系列基于编译器的调度技术,可以更加确定的对任务进行分割,因为信赖硬件会按照顺序不卡壳的运行。虽然硬件有设计冗余但是调度器会榨干这些分布式计算单元的性能,有活就直接把性能榨干到100%。传统CPU架构虽然硬件上更精简但是处理的是复杂任务,经常平均性能可能不到10%。所以应用场景的不同,需要对应不同的硬件设计,在SoC上各种系统都存在就需要把不同的计算任务分配到不同的硬件上,再进行系统级的协同工作。
为了保障存算一体数据的确定性,保障计算单元能准确的时间内拿到存算一体数据,需要有一个同步机制。这样就保证了系统中知道某个时间数据在哪里,数据的运动情况,数据到新位置后需要同步机制知道数据的新位置。具体就是数据到了计算单元就同步给计算单元开始运算,计算单元运算完结果放入存储就同步告诉调度器数据返回给AI APP。
1.3 流水线思想
NPU中有各种各样的算子,对于一个计算任务可以进行分解并行计算,但是有些还是有依赖的串行才能操作,就存在某个时刻会空出来一些算子闲置没有进行运算工作,但是这些算子都是硬件,闲置就闲置了,所以整个NPU算子利用率从理论上也达不到100%,但是我们要通过调度尽量让算子去并行执行,来提高利用率。
可以把上图中的一个圈看成一个算子,那第二列是依赖第一列的,但是第一列不是一次就可以计算完,例如第一列需要100个计算,但是一次只计算了10个,这时第一列中的100个计算其中2个有了第一列的10个计算输入就可以进行计算了,就可以并行开始工作了。这其中就是串行中有并行,并行中有串行,都是相对的。跟数据结构里面的关键路径算法非常类似:
关键路径是指工程项目从开始到结束经过的耗时最长的逻辑路径,因此优化关键路径是一种提高工程项目有效方法。
关键路径基于拓扑排序,且引出了以下四个概念。
Ve(j):表示事件(顶点)的最早开始时间,在不推迟整个工期的前提下,表示从源点开始到该节点的需要的最长时间
Vl(j):表示事件(顶点)的最晚开始时间,在不推迟整个工期的前提下,表示从结束顶点到该点最短需要多少时间
e(i):表示活动(边)的最早开始时间,就是活动边的起点的最早发生时间, 表示该边起点的最早开始时间
l(i):表示活动(边)的最晚开始时间,就是该活动边起点的最晚发生时间,表示该边起点的最晚开始事件
这四个概念后面分别用顶点最早开始时间、顶点最晚开始时间、活动最早开始时间、活动最晚开始时间表示。
详细算法可以自己去搜这个关键路径算法。这里关键路径就是工程项目所用的最短时间,工程里面会去优化这个关键路径,缩短关键路径就可以缩减工期。跟我们这里串行并行的这些是一样的,算出来整个任务最短运行的时间,并进行优化,才能进行并行任务分配。
串行的算子之间由于是存算一体的实现,数据不必传出去到cache或者DDR,直接在NPU内部的算子之间流转,距离近,效率高。所以并行要从存储(存算一体数据)和计算(算子)两个维度去进行。
之前讲过算子的概念,其可大可小,大的来说可以对于一个NPU计算核心,小的来说可以针对一个计算核心里面的例如一个TCC张量运算单元,这里面都有并行的概念。就是需要在各个层级上去进行并行,整体并行,火力全开。
1.4 图编译器思想
上面的思想机制,其实都需要软件去实现,硬件的NPU只是计算和存储的功能,怎么用这些功能需要软件去调度去发指令。特别是并行处理,就需要编译器去分割任务并分配硬件资源,这样处理就像流水线一样,让数据流动起来,最大化利用硬件,这就是数据流芯片框架的软件实现。
调度的核心就是图依赖
并行计算中的依赖就像关键路径里面的图表达一样,这里也需要把这个图给抽象描绘出来,再使用软件算法进行规划,最后参与编译生成固定序列的运行指令,这就是图编译器的作用。
能做到编译的时候,而不是运行时进行任务调度是因为业务场景的固定,例如智能驾驶,固定的处理图片视频数据,固定的业务。
其实上面用到的流水线、数据结构等都是一些计算机体系结构最基础的知识,组合到一块,又有具体的应用场景就是创新了。
另外在具体的实现上,硬件还可以,软件上可能对着这些思想要复杂千万倍,需要进行分模块,大量的人员投入去梳理清楚业务变为代码。
从计算机技术的发展来看
计算机技术的一些核心思想感觉已经老早被一些聪明人发明出来了(本科阶段就可以学习到,例如计算机组成原理、数据结构、计算机网络、操作系统等),其实就是模拟人的思维,人的思维就那些都被用完了,现在的创新就是这些模式的组合也就是体系结构的变化,还有就是芯片制造工艺的提升,来增加技术能力。
2. NPU编程模型
什么是编程模型?
编程模型是指在软件开发过程中使用的一种抽象概念,用于描述计算机程序的组织和执行方式。它定义了程序的结构、数据处理方式、并发性、通信方式等,为程序员提供了一种高层次的方式来思考和实现软件。
2.1 CPU和GPU编程模型
CPU(CentralProcessing Unit)中央处理器,是一块超大规模的集成电路,主要逻辑架构包括控制单元Control,运算单元ALU和高速缓冲存储器(Cache)及实现它们之间联系的数据(Data)、控制及状态的总线(Bus)。简单说,就是计算单元、控制单元和存储单元。
CPU遵循的是冯·诺依曼架构,其核心是存储程序/数据、串行顺序执行。因此CPU的架构中需要大量的空间去放置存储单元(Cache)和控制单元(Control),相比之下计算单元(ALU)只占据了很小的一部分,所以CPU在进行大规模并行计算方面受到限制,相对而言更擅长于处理逻辑控制。
为什么说CPU善于逻辑控制?
CPU编程更加傻瓜化一些,是一个通用的计算机机构,编程者不需要知道硬件的组成,例如有多少个核,内存cache怎么组织,网络怎么样等,直接通过寄存器去控制就可以,硬件的处理细节不对用户暴露。
甚至CPU的一些流水线、乱序执行等架构,用户也不需要去关心,更像一个包办,什么东西都是黑盒去完成,但是控制权一直在CPU手里,完全的中央集权。
但是这样中心化的一个设计,一是要把CPU忙的不可开交,如果一圈硬件围着CPU,干什么都是亲力亲为,例如计算、数据搬运等。那个卡住了就把任务卡住了,很多时候需要等待。一般经常卡主的瓶颈就是存储,那就增加各种cache,一级不够两级,两级不够三级,但是其也不是能把所有的数据都兜住到cache里面,只能说满足大多数需求。
中央集权
- 优点:集中力量办大事,慢但是稳
- 缺点:不利于并行,权利不下放,下面就没有积极性,盲等频繁
DMA是其的一个对于数据搬运的改进,CPU把数据搬运的任务权利下放给DMA,DMA去一次性完成多个任务后给CPU汇报。对于计算来说,要进行并行那GPU就上场了。
GPU也只是把图形处理等AI任务从CPU那里抢过来,利用了SIMT的思想,一个指令周期进行并行计算。GPU具有如下特点:
- 多线程,提供了多核并行计算的基础结构,且核心数非常多,可以支撑大量数据的并行计算,处理神经网络数据远远高效于CPU。
- 拥有更高的访存速度。
- 更高的浮点运算能力。
GPU虽然在并行计算能力上尽显优势,但并不能单独工作,需要CPU的协同处理,对于神经网络模型的构建和数据流的传递还是在CPU上进行。GPU没有自己的存储,还是用的CPU的存储,只是计算部分采用了并行。那存储部分没并行就可能存在等数据的情况。
所以最终形态还是需要NPU:存储和计算都要并行,NPU有自己的无数小的存储和计算单元,更加确定的进行并行计算。
另一方面,GPU的功耗很大,体积大,价格贵,特别是对于终端设备基本无法使用,因此需要NPU这种:体积小、功耗低、计算性能高、计算效率高的ASIC专用芯片。
2.3 NPU编程模型
NPU (NeuralNetworks Process Units)神经网络处理单元。其针对于矩阵运算进行了专门的优化设计,解决了传统芯片在神经网络运算时效率低下的问题。NPU工作原理是在电路层模拟人类神经元和突触,并且用深度学习指令集直接处理大规模的神经元和突触,一条指令完成一组神经元的处理。相比于CPU和GPU,NPU通过突出权重实现存储和计算一体化,从而提高运行效率。
神经网络处理器(NPU)采用"数据驱动并行计算"的架构,特别擅长处理视频、图像类的海量多媒体数据。NPU处理器专门为物联网人工智能而设计,用于加速神经网络的运算,解决传统芯片在神经网络运算时效率低下的问题。
NPU是模仿生物神经网络而构建的,CPU、GPU处理器需要用数千条指令完成的神经元处理,NPU只要一条或几条就能完成,因此在深度学习的处理效率方面优势明显。
神经网络中存储和处理是一体化的,都是通过突触权重来体现。 冯·诺伊曼结构中,存储和处理是分离的,分别由存储器和运算器来实现,二者之间存在巨大的差异。当用现有的基于冯·诺伊曼结构的经典计算机(如X86处理器和英伟达GPU)来跑神经网络应用时,就不可避免地受到存储和处理分离式结构的制约,因而影响效率。这也就是专门针对人工智能的专业芯片能够对传统芯片有一定先天优势的原因之一。
对于编程来说,NPU的挑战更大,需要极强的程序控制能力。NPU把自己的硬件:计算单元和存储单元都暴露给软件去控制,整个并行的组织和执行都需要软件去协调,增加了编程的复杂度,降低了软件的灵活性和通用性。所以其必须用于特定的场景才变的可控。数据和算法都必须是确定的,这样配合AI APP、runtime及调度器和编译器等一起完成这个巨大的软件工程。
这里就是随着硬件的体系结构变的复杂,软件的复杂度也随着上升。NPU硬件中随处可见的DMA、SRAM、RISC-V、算子等,这些需要一个庞大的软件去控制,work起来。
参考:blog.csdn.net/qq_36944952...
后记:
本篇没有写细节的实现,但是从顶层思想更有助于理解这些设计。希望有帮助。