9月份时,机器之心曾发文章宣布「机器之心」和「矽说」将共同推出系列文章「脑芯编」,揭秘类脑芯片的过去、如今与将来。本文是此系列文章的第四篇,烛台簇华照单影。
烛台簇华照单影
写这篇的时候想到哥哥的《我》
因为这次的主角和那个特里独行的我
很像:
我就是我,是长度不一样的开拓
天空海阔,要讨最并行的生活
我喜欢我,让矢量算出一种结果
简单的指令集,一样加速的很妥妥
他的名字,叫做
SIMD
Single Instruction Multiple Data
话接上回(梦里不知形与令)我们说到,有三种方式,可以在传统体系结构的基础上面向神经网络人工智能硬件进行优化设计。这次,我们先来提第一种——在简单指令集(RISC)中增加指令的方式来达到性能的优化。
这次的故事,要从并行计算机体系结构讲起。说到并行计算机体系结构,就要掉一个书袋——
小编置入行销 不买不吃亏之一
我从上的第一门计算机体系结构课,到并行提算计体系机构,到高级计算机体系结构都在用这一本书。不得不感谢作者让我少买了好多教科书钱。当然,牛 x 书的作者也很牛 x,这里就不八卦了。有人愿意把这本书叫做计算机体系结构的 bible,我不评论,但是下面我们所讲的,好多都出自这本书。
言归正传,这个特立独行的故事从这里开先,我们要认识一个老爷爷(还活着好像),他叫 Michael Flynn。老人家生于大萧条时代的扭腰城,一不小心提出了一个分类法,叫做 Flynn Taxonomy(1966)。然后计算机体系机构就被 Flynn taxonomy 的五指山给压几十年。
Flynn Taxonomy 的五指山把计算机结构分为两个部分:指令与数据,在时间轴上指令与数据可以单步执行或多次运行进行分类,即单指令单数据(SISD),单指令多数据(SIMD),多指令单数据(MISD)和多指令多数据(MIMD)。
并行,从 Pipeline 到 SIMD
Flynn taxonomy 给并行计算机体系结构指了两条明路——指令级并行和数据级并行。
首先来看下指令与数据的关系。指令是处理器单步可以实现的操作的集合。指令集里的每一条指令,都包含两个部分(1)什么操作,(2)对什么数据进行操作。专业地,我们把前者叫做 opcode,后者叫做 operand(也就是数据)。当然,并不是所有的命令都有数据操作。传统定义的指令集里面,对应的 operand 不超过 2。比如,加减乘除都是典型的二元操作。数据读写就是一元的,还有些就是没有任何数据的操作,比如一个条件判断(if)发生了,根据判断结果程序何去何从,只是一个 jump 操作,他并不需要任何数据输入。
你还记得(一)昨夜神风送层云里都提到的一个 neuron 计算么?每一个 neuron 都要经历 n 个输入的乘累加~
指令级并行的第一种、也是最经典的办法叫做时间上并行,这是所有的体系结构教科书最喜欢教的流水线架构(pipeline)。简而言之,在发明流水线以前,处理器里面只有一个老司机,什么事情都得他来干,但是下一条指令得等老司机干完上一个~但是,流水线就是把一个老死机变成了三个臭皮匠,每个人干三分之一就给下一个,这样下一条指令只需在上一条被干完 1/3 后就可以进来了。虽然老司机体力好,但赶不上年轻的臭皮匠干的快啊。这样,流水线可以实现时间上的指令集并行, 成倍提高实际指令的处理效率。
经典 MIPS-5 级流水线
有一就有二,有时间当然就有空间。所以,第二种办法是空间上的并行。空间并行基于一个观察——对数据的操作有很多种——加减乘除移位、整数操作、浮点操作……每一个模块的处理(ALU/EXU)是独立的,所以,就空间上,一个浮点加法在处理的时候,完全可以同时进行一个整数移位操作,像老顽童教小龙女的左右互搏分心二用。所以,在计算机体系的历史上,我们把练成「左右互博」术的处理器叫做——超标量(Superscalar)/超长指令 (Very Long Instruction Word, VLIW) 处理器。关键在于,有没有几套对应的前后 fetch /Decode /读写模块。在实际设计中,超标量从硬件层面进行自动对数据进行再排序,而 VLIW 是用编译器层面将 c-code 编译成更大条的指令。所以超标量的硬件更难做,而 VLIW 的编译器更难设计。
相比于流水线/超标量复杂的修炼过程(黄蓉都练不会「左右互博」),数据级并行就是简单纯粹的叠加硬件,打造并行处理的「千手观音」:
千手观音」的学名叫做 SIMD,Single Instruction Multiple Data,单指令多数据(流)处理器。其实,说白了就是原来有处理单元(ALU/EXU)现在一个加法器,现在变成了 N 个了。对应神经网络的计算,原来要 M 次展开的乘累加,现在只要 M/N 次,对应的时钟和时间都显著地降低。
简单粗暴的并行,不仅提高了让每个指令的数据吞吐率,还让本身单一的标量处理进化成阵列式的「矢量型」处理,于是就有 SIMD 又有了「矢量处理」指令的称呼。其实,SIMD 并不是到了神经网络再兴起的新玩样儿,早在 MP3 的年代,SIMD 处理器就广泛地使用在各类信号处理芯片中。所以关于 SIMD 指令也早有了需要行业标准。以下,我们就来看一个 SIMD 指令集实例。
ARM NEON,厉害了 word 令
在上一编中,我们简单提到了史上第一个攻城掠地的 RISC-ARM。为手机、平板等便携式的最重要处理器,ARM 的 SIMD 指令也是王者风范,从它的名字开始——NEON。
NEON 的指令的操作的输入(operand)是一组 128 位位宽的寄存器,但这个寄存器存着的几个数,就由码农自己去预定义了,可以是 4 个 32 位的浮点,或者定点,或者 8 个 16 位定点,或者 16 个 8 位定点……整个指令集宽泛地定义了输入、输出的位宽,供变成者自由支配,考虑到在神经网络中,前馈网络往往只要 16 位、8 位整数位宽,所以最高效的 NEON 命令可以一次实现 16 个乘累加计算(16 个 Synapse)。
仅仅是 SIMD 怎能彰显 NEON 的侠者风范?NEON 还充分应用了指令级并行,采用 10 级流水线(4 级 decode+6 级运算单元), 可以简单地理解为把卷积计算的吞吐率由提高了 10 倍。加起来,相比与传统的单指令 5 级流水,提高至少 32 倍的效率。再辅以 ARM Cortex A7 以上的超标量核心处理单元, 筑起了第一条通用并行计算的快车道。
当神经网络遇上 SIMD,滑起吧!
流水线和 SIMD 都是在神经网络还没羽翼丰满的时候就已经称霸江湖的大侠。在神经网络不可一世的今天,这两者还是固步自封么?答案显然是否定的。
当通用 SIMD 处理器遇上神经网络,他们既碰撞出了火花,也开始相互埋怨。我们先说埋怨——存储空间管理。我们知道,在 NN 中通常每个卷积核都需要先 load 系数与输入数据,再算出部分的乘累加结果,再 store 回存储空间。而指令执行与存储空间的通信就是我们上一编讲到的——冯诺伊曼瓶颈。对于神经网络来说,如此多次的存储读写是制约性能的关键。减少数据的载入与中间结果是面向神经网络的 SIMD 指令的主要问题。
那火花是什么?在深度神经网络,特别是 CNN 中,每个卷积核——相邻状态的数据输入只更新了小部分,而大部分数据保持不变,但更换了对应系数。这就给 SIMD 带来了一个面向神经网络的新机遇——部分更新与数据滑行(sliding)。我们来看下面这张动图「原作为 MIT Eyeriss 项目研究组」。
对于一个采用 SIMD 的卷积核,有一组输入是固定——系数矢量,而另一组输入像一个 FIFO,在起始填满后,每次注入一个单元(也排出一个单元)进行乘累加,另外上一次累加的结果在保存在执行单元的寄存器内,只有最终的卷积核结果会写回到存储器中。
这样,在神经网络中,无论是数据导入、还是结果输出,起对存储空间的访问都会大大降低。当然,上述示意图仅仅是一维的。当卷积核的维度达到二三维时,情况会复杂很多。这里推荐大家可以去读读 MIT 的 Eyeriss,Kaist 的 MIMD,或者 IMEC 的 2D-SIMD(ENVISION)。这里就不太多展开了。
好了,这次就到这里。所谓「烛台簇华照单影」就是那一粒粒自由定义的小数据,在同一声 SIMD 的指令下,排成队,集成行,成为了一个孤独的矢量运算。
所谓一个讲科普的公众号,好像《脑芯编》要逐渐地进入掉书袋的节奏,客官不要睡着哦,因为下次我们要读 paper 了,可以慢慢睡~
点击「阅读原文」可加入人工智能芯片交流群。
©本文由机器之心发布,转载请联系本公众号获得授权。
✄------------------------------------------------
加入机器之心(全职记者/实习生):[email protected]
投稿或寻求报道:[email protected]
广告&商务合作:[email protected]