首次曝光 | 阿里AI芯片含光800硬核编程模型

首次曝光 | 阿里AI芯片含光800硬核编程模型

阿里妹导读:今年,平头哥发布全球最强AI推理芯片含光800。在杭州城市大脑的业务测试中,1颗含光800的算力相当于10颗GPU。在业界标准的ResNet-50测试中,含光800推理性能达到78563IPS,比目前业界最好的AI芯片性能高4倍。究竟是什么模型打造出如此性能,今天,我们请来阿里高级技术专家一皓来聊聊它的编程模型。

前言

当我们手拿含光这把神兵利器的时候,首先要了解这把剑的精华。比如杨过用的玄铁重剑,其剑法要诀是“重剑无锋,大巧不工”,其中境界,远胜世上诸般最巧妙的剑招,越是平平无奇的招数,对方越难抗御,如挺剑直刺,劲力强猛,轻重刚柔随心所欲,刚劲柔劲混而为一,威力远比变幻奇妙的剑招更大。又如金蛇剑,形状奇特,却因此比之寻常长剑增添了不少用法,配合“金蛇秘笈”,颇多招式看来绝无用处或者甚不可解的,尽成厉害招术。虽然这是武侠小说中的创意,但金大侠确是有深意:剑不同而诀不同,需为剑量身打造剑法心诀,才能发挥剑的威力。

如果由我来理解,含光的精髓在于“利”,如一把削铁如泥的宝剑!使用如此锋利的宝剑,其剑道需要由繁化简,由简而快,达到返璞归真。在芯片领域,特定领域架构(Domain-Specific Architecture, DSA)正是一种由繁化简的思路。虽然怎么去做到真正的由繁化简其实很难!

编程模型(Programming Model),就是从开发者和用户的角度出发,基于NPU的硬件系统,软件系统的架构和特性,提炼出怎么理解和使用NPU的要诀,主要包括:

  • 硬件模型:设备和核心的抽象及相互关系
  • 存储模型:即存储的分级,各级主要特性和相互的关系
  • 深度神经网络在含光NPU系统上的结构模式
  • 深度神经网络在含光NPU系统上的布局模式
  • 执行网络的异构和并行模式

通过对这些的了解,我们大致能在顶层抽象上,对含光800NPU建立一个整体的模型,理解编程使用含光NPU的基本要点,为后面具体使用打下坚实的基础。

深度神经网络推理计算模型

在开始介绍含光800NPU的编程模型之前,有必要看看深度神经网路推理的计算模型。分析一下CPU,GPU,以及其他AI芯片的计算模型。通过这个对比,应该可以帮助大家理解含光的特点和编程模型。

推理计算特性

首先借用网络上的训练和推理介绍图来看看推理计算的最简单的计算模型是什么。虽然这是一个非常非常基本的知识点,还是请允许我再强调一遍。

如果只关注计算部分,我们可以这么简单地理解:

  • 训练是双向计算,先将输入和网络模型参数做前向计算得到结果,然后根据标签,做逆向的计算,修改网络模型参数。
  • 推理的计算是单向的,网络模型参数是训练之后固定下来不会再改变的,可以认为是一个常数,输入和模型参数计算之后直接就得到推理的结果了。可是在当前几乎所有芯片的实际计算模型中,并没有特别的体现这一个特性。 

首次曝光 | 阿里AI芯片含光800硬核编程模型

其他芯片计算模型

深度神经网络确实是名副其实的比较有“深度”!这就导致了当前主流的网络模型参数量都很大。即使有很多轻型模型的网络模型设计,加上量化,剪枝等技术配合,大部分网络模型还是远远大于当前芯片的Level1缓存的大小,何况缓存还需要存放输入,输出,和中间临时计算结果。

看看来自于网络的两张对比图,分别是CPU,GPU,TPU在存储子系统架构和计算单位两个方面的对比。

首次曝光 | 阿里AI芯片含光800硬核编程模型

首次曝光 | 阿里AI芯片含光800硬核编程模型

AI推理计算时,大多数的芯片系统中,虽然计算的单元有区别,比如CPU以scalar为主, GPU Tensor Core 以vector为主,华为DaVinci AI Cube Core以Tensor为单位,但输入输出(即激活/activation)和模型参数(即权重/weight)是无差别对待的,把他们都看作是计算中的等价的操作数。从存储系统以及计算上都没有体现出任何区别。因此在编程开发中,用户不需要特别地关注模型参数的管理。

在TPU的架构中,激活Activation和权重Weight有了区分。特别的,TPU可以显式地管理权重,使用独立的权重FIFO来调度。由于权重的简单和特殊性,这种方式能简化调度,提高FIFO利用率,保证更高的吞吐量。

含光800硬件模型

含光的设计中,最重要的一点,就是利用相对比较大的一级存储,将网络模型的权重布局和分配好,保证权重一次上传之后,就能一直保存在本地存储中,避免了这部分数据的频繁地上传。这样既降低了对带宽的需求,也让整个计算流水线的设计变得相对简单了。

下图是含光800NPU的一个核心的示意图,详细芯片的架构图可见上一篇介绍。交代一些此处的用词:

  • 设备(Device):此处指的是含光800NPU(Neural-network Processing Unit),一个设备就是一块NPU卡。
  • 核心(Core):一个完整的计算核心,可独立调度运行的最小单位,组成如下图:
  • 本地存储(Local Memory):核内存储,核间连接。含光800 NPU核心简图

首次曝光 | 阿里AI芯片含光800硬核编程模型

每个含光800 NPU核包含4个Tensor Array和对应的本地存储(LM);LM里存放权重,激活(输入,输出,临时),等各种计算所需数据。权重是一次性装载后常驻LM,其他数据是每个批次(batch)流水处理的。

含光800编程模型

存储

从系统的角度来看,存储系统可以分为4种不同级别:

  • L0 Cache/Buffer

特殊用途的cache/buffer,比如给累积缓存(Accumulation buffer)等。由于这部分属于硬件控制,从编程的角度来说完全不可见。编程模型通常不需要考虑这一级别的存储。

  • Local Memory

片上核内存储,HanGuangAI软件栈分配控制。HanGuangAI应用层不能直接访问和控制核内存储,没有相关操作的任何API,但可以通过核心分配来间接地影响使用情况。

  • Host Memory

映射到设备地址空间的主机内存(PageLocked Host Memory),NPU可以DMA访问。软件栈负责分配和使用这部分存储。应用层也可以通过HanGuangAI的API直接分配和使用。

  • System Memory

CPU访问的主机内存,NPU不能直接访问。

后面两种存储是应用层可访问的。特别的,应用层可以通过HanGuangRT API创建相关的张量,并读写数据。在上传和下载过程中,如果按照NPU的规则直接把数据放到Host Memory,相比放到System Memory,可以少一次拷贝,对整体性能功耗是有好处的。当然由于这部分(Host Memory)大小比较有限,所以需要快速周转,提高存储的利用率。

深度网络模型

深度神经网络是以图的方式表达,以算子(Operator)做为节点。从一个训练好的网络模型到含光NPU上的可执行指令。网络模型需要量化和编译两个步骤:

1)量化

含光800 NPU主要算子是8-bit和/或16-bit整形和运算。需要使用量化操作将32-bit浮点精度的网路模型转化成对应的整形运算。为了让量化更能充分利用含光NPU的设计,HanGuangAI整合了量化的功能,使用了一些特有的量化技巧来达到更好的效果和效率。

HanGuangAI软件栈提供python的量化接口。量化分为两个步骤:校准和量化。两个步骤分别在原来的模型中插入了适当的校准和量化算子,这些算子有HanGuangAI提供,做为原始框架的自定义算子。请参考HanGuangAI相关文档了解校准和量化的细节。

首次曝光 | 阿里AI芯片含光800硬核编程模型
上图是量化产生的网路图的部分节点示范图

2)编译

高效的推理运行模式,是将整个网络做提前编译(AOT),生成优化的后端代码。运行时把整个网络看作一个整体执行运算。我们把这个可执行的整体叫做一个运行引擎(Engine)。

如果所有算子都能在含光NPU上执行的话,这个网路就变是一个单独的引擎。实际在一些网络中,像TensorRT一样,有些算子现在的含光NPU软件或者硬件不支持。这时候,我们需要对整个网络做合适地分割,分成一段一段能支持的子网络,然后再对每一段子网络进行编译,这个过程是由HanGuangAI软件栈来完成。

编译后的图结构是一个经过算子融合简化的图结构。包含一个或多个NPU引擎算子(AliNPUEngineOp)和0到多个原来的算子。如图:   

首次曝光 | 阿里AI芯片含光800硬核编程模型

另外,编译后的网络模型的格式和输入一样,比如原来是Tenorflow的pb格式,编译后输出还是pb文件格式。这样在插入我们的自定义算子之后,可以保证网络模型能在原来的框架中运行。

3)网络的结构模式

综上所述,从网络结构和算子属性的角度来说,用于推理的模型有以下4种模式:

  • 原始模型(Original Float Model)
  • 校准模型(Calibration Model)
  • 量化模型(Quantized Model)
  • 编译模型(Compiled Model)

另外,通常来说,剪枝压缩不会改变网络结构,这里没有特别的讨论相关影响。

异构执行

编译网络模型在NPU上的计算执行,是以一个NPU引擎算子为运行单位的。NPU引擎算子将通过HanGuangAI的软件栈,执行在含光NPU上。

如果一个网络模型的所有算子都被编译在一个引擎算子里,推理的计算运行就比较简单了。但如果网络模型包含其他算子,或者被分割成了多个引擎算子,这时候,其他的算子需要在其他的设备上运行,比如CPU,GPU。

比如上图的编译模型,AliNPUEngineOp是有NPU执行的,而NonNPUOp(SparseToDense)可以在CPU上执行。

这种异构计算可以由几种工作模式:

  • 基于框架,这是一种简单的方式,利用框架的多设备异构执行的支持。因为编译后的模型文件格式一样的,所以如果原来有框架可以执行,可利用框架来执行。框架可以根据设置将其他的算子转到对应的设备上执行。
  • 实际应用中,很多用户有自己的推理引擎,以前是直接使用比如TensorRT在GPU上做推理,TensorRT不支持的算子,会有CUDA或者其他库在GPU上执行,或者使用CPU库在CPU上执行。现在使用含光NPU,只需要使用HanGuangRT,将NPU引擎算子的支持加到这个推理引擎中,然后把其他NPU不支持在其他设备上实现。

后续,我们会提供更多的方式友好地支持异构执行,保证执行的方便和流畅。

权重装载

如前所述,含光NPU的高效工作模式,是需要将权重数据等全部事先分配好空间,这样就避免运行过程中重复地从Host上传权重数据到LM。由于我们有很多不同的业务情况,有些模型很小,只需要少数的几兆空间,有的模型很大,有的模型被分割成多个引擎,还有很多业务需要几个到十来个模型一起协同工作。因此我们需要提供灵活多变的装载模式。

我们在编译的时候,根据引擎需要的存储大小和可以使用的核心数等信息,我们提供一些不同的解决方案:

  • 单核单引擎

模型比较小,编译成一个引擎,比如ResNet50,一个核心能够容纳下,编译出的引擎以单核心为最小运行单位。在Runtime运行时:

  1. 既可以每个核心装载一个不同的模型,可以独立地运行
  2. 也可以将这个模型同时装载在多个核心,多个核心并行工作,提高吞吐

首次曝光 | 阿里AI芯片含光800硬核编程模型

首次曝光 | 阿里AI芯片含光800硬核编程模型

  • 单核多引擎

引擎比较小,而且不需要并行时,可以将多个引擎放在同一个核心上,这时候根据是否共享输入/输出/临时空间,又可以分为两种:

  1. 共享输入/输出/临时空间,输入/输出/临时都是按照最大的来分配,这样可以放更多的引擎
  2. 不共享,完全独立分配空间,逻辑简单,但需要更多空间

首次曝光 | 阿里AI芯片含光800硬核编程模型

  • 多核单引擎

这是一种特殊的工作模式,由于单个引擎的大小超过了一个核心的存储大小,可以将单个引擎分布在多个核上,主要是将权重分割,多个核心协同工作。

首次曝光 | 阿里AI芯片含光800硬核编程模型

  • 多核多引擎

复杂的应用场景了,有时候一个应用流程里,需要几个步骤,几个或者十来个不同的模型,为了保证这些模型能流线性的高速地执行,我们需要将多个引擎分配在多个核心上。当然这些分配是有HanGuang编译来分析和适配的。结果会返回给用户做评估。

首次曝光 | 阿里AI芯片含光800硬核编程模型

  • 跨设备多引擎

这是更充分地一种扩展方式,保证更复杂场景的应用。多个设备一起合作,完成一个复杂的应用场景,保证足够好的性能和吞吐。以下例图,整个业务由engine0和engine1提取特征分割,engine2/4做一类特征提取和识别,engine3/5做另一类的特征提取和识别。其中engine0特别大,需要4核协同处理。

首次曝光 | 阿里AI芯片含光800硬核编程模型

以上各种解决方案,我们现在主要是由用户指定网络模型和可用资源(设备和核心),软件栈编译器做相关具体的优化配置,并在在编译后的结果中提供一个执行方案,然后推理运行时根据执行方案和实际的设备/核心情况做动态的运行分配。整个过程基本是不需要用户做太多的干预,即能达到比较好的效果。当然理解这些工作模式可以帮助用户做更深层次的控制和优化。

并行执行

深度神经网络计算在并行上要求很高,不同的芯片通过不同的方法,在不同的维度上去实现并行执行。在之前的一片文章里提到过一些对比。

含光NPU的并行模式和权重装载的方式相关,权重装载是基础,为特定的并行方式提高数据支持。而并行执行更多的是从数据流动的角度来看。可以从三个层次来看待并行执行的模式:DMA/compute引擎,核心之间,核心内部各管道(pipe)。

1)核心并行

这个层次的并行,是NPU核心在一个输入批次(batch)级别的并行。具体地说,如何把多个输入分布在多个核心上,让所有的核心都能运行起来,提高利用率。下面几种模式:

  • 核心并行模式

每个核心执行一个batch,N个核心共同工作完成N batches。这是简单高效的方式,适合网络能够运行在一个独立核心上,而执行批量比较大的情况。由于每个核心执行的计算是一样的,所需要cycle数相同,因此核心利用率很高。

  • 核心协同并行模式

也是每个核心执行一个batch,也就是统一时间,N个核心共同工作完成N batches,但与上面不同的是需要核心之间协同。这种对应上面的“多核单引擎“装载模式。由于权重是分配到多个核心上的,因此需要核心之间的相互合作和核间数据传输。

  • 核心流水线模式

多核心合作完成一个batch,所以输入的batch是一个一个进入到NPU的。比如,第一个核心先完成整个业务的一部分,完成之后传到后面的核心,然后第一个核心接着做下一个batch。因此,同一时刻,多个核心可以都在并行计算,但计算的不是同一个部分,而是已流水线的方式传递。

  • 混合模式

复杂业务情况下,上面多种并行方式的组合。一些核心之间,或者一个时段是核心协同并行,而另外一些核心或者其他时段是直接的核心并行。

核心的并行,像上面权重装载一样,大部分是软件栈来配置和实现的。但也通过软件接口提供配置方式让用户可以做一些调整和选择。

2)计算和数据传输并行

需要软件保证整个数据流的通畅:

  • 上传数据:System Memory-->Host Meomry-->Local Memory
  • 计算:compute
  • 下传数据:Local Memory-->Host Momory-->System Memory

首次曝光 | 阿里AI芯片含光800硬核编程模型

3)核内计算并行

核内有多个硬件引擎,如果没有数据依赖,引擎之间也是可以并行的。基本上可以认为模型/算法开发者不需要考虑这方面的并行,编译器会根据图和数据依赖来做相关的优化,所以不在这里做详细介绍。

答疑

上面从NPU架构,存储系统,网络模型,装载和执行几个方面建立了整个系统的编程模型。读者对整个编程模型了解之后,可能会产生一些问题。在回答一些问题之前,我先强调一个重点:含光800NPU是为数据中心和大型端上设计的。这两个场景的特点是:

  • 数据中心上会有数量众多的NPU可以做分配和调度
  • 大型端上,业务场景固定,推理的算法一旦确定实施,网络模型也就固定下来了,不需要动态的调整模型,

这两个场景的特点是硬件设计和编程模型设计的重要根据。因为这意味着我们要不可以动态地申请额外的核心,要不可以提前保证有足够的核心。

下面我根据自己的判断回答一些可能的疑惑:

Q:一个含光800NPU能放下所有模型吗?

首先,一般的大网络模型,通过多核单引擎的模式,基本都能在单张卡上运行。另外含光800NPU也支持压缩的网络模型,有些网络模型可以做到在有限的精度损失的情况下,获得很高的压缩比。

其次,碰到特别大的模型,一张卡放不下的时候,可以使用分割然后在两张卡上运行。所以通常我们不建议破坏权重常驻本地存储的模式,因为这样会影响性能。

如果说极端情况,就是只有一张卡,就是要跑超大网络网络,一个方法是分段大批量执行,把中间结果暂存。我想即使使用如此大的模型,应该是不会考虑单批量(即一个batch)的延迟了。

Q:看起来多模型装载很复杂,用户能掌握吗?

在上面文章中提到,绝大部分的模型装载逻辑由编译器来处理。用户只需要指定那些模型要一起编译,以及设备和核心的资源情况。编译器内会根据相关信息建立成本模型(Cost Model),选择一个相对最优的分配生成一个执行方案供用户使用。这个执行方案由HanGuangRT读取并根据实际的物理资源分配,所以通常不需要用户介入。当然用户也可以读取查看。

所以总的来说,开发用户不需要掌握太多细节就可以很方便地使用多模型装载模式了。后面在API介绍中会具体地讲解相关的使用。

Q:算子支持情况怎么样?异构执行设备间切换太频繁会不会导致性能下降太多?

如果一个网络模型被分割成很多的子网络,这种情况确实会严重影响推理的系统性能的。所以我们正在努力地完善软件栈,支持更多的算子和网络模型。

现在阶段图像,视频,搜索,推荐等应用的主要模型都没太大的问题。我们正在向集团内各业务方收集更多的算法模型,进一步完善。

另外软件栈也在做异构执行的优化,包括一些算子的CPU优化,以及减少异构切换的性能损失等。后续会发布更多的模型的性能数据。

后记

作者在写这篇文章的时候,英特尔首席架构师Raja Koduri正好在上海,在接受新闻媒体采访的时候,他强调了Intel最近提出的“软硬结合,软件为先”的策略。提到了一些观点:

  • 软硬结合,可使摩尔定律的提升幅度变成10倍级别,因为软件可以将越来越多的晶体管性能潜力释放出来。
  • 软件的重要性,是其他领域的10倍,没有软件的支持,无法实现计算性能的指数级的增长。
  • Intel现在推崇SVMS异构:标量(Scalar),矢量(Vector),矩阵(Matrix),空间(Spatial)四种架构,通过多种多样的组合方式异构。

对比Raja的这些观点和含光软硬结合的DSA芯片架构,有不少的相似之处!

不过,我们可以看到,不论是软硬结合,特定领域架构,还是多维异构,都对软件有更高的要求,远远超过去十几二十年的软件变迁。这些影响是多方面的:

  • 编程语言(接口):如何用统一的编程语言来对异构架构,各种DSA硬件架构编程。(Intel 在设计oneAPI)
  • 编译器:多种DSL(Domain Specific Language),对接各种硬件架构,对新的编译器技术的渴求
  • 异构与并行:不同级别的异构,包括:
  • 片上不同类型的核间
  • 同一系统的不同架构芯片
  • 互联的不同系统

总的说来,软件栈变得复杂了很多,需要软件架构的进化!后续我们也可以在软件栈架构方面做更多的探讨。

回到含光800NPU的开发和编程,乍一看,有些思路和在CPU或者GPU上编程很不一样。但是在经过软件栈对硬件特性的提炼和包装之后,实际使用起来是很方便的。开发者能通过主流的深度学习框架无缝使用含光NPU,也能比较方便地使用HanGuangAI开发部署推理引擎,充分发挥含光NPU的性能。

正所谓利剑在手,剑道心诀已熟记在心,只等开始学习剑法了!

原文发布时间:2019-12-19
作者:一皓
本文来自阿里云合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。