比如说,如下指令就是使用一条指令完成了4对32-bit数据的加法运算,其中的V8/V9/V10是3个128bit寄存器。
ADD V10.4S, V8.4S, V9.4S
但不管是NEON,还是Intel的相关技术,随着被操作数寄存器位宽的增加,会遇到指令数量激增的问题。比如在NEON中,如下的指令虽然用的是相同的寄存器,也都是加法,但翻译后却是不同的指令。
ADD V10.4S, V8.4S, V9.4S
ADD V10.2D, V8.2D, V9.2D
可以设想,当扩展到SVE要求的2048bit寄存器位宽时,所需要增加的指令数对于ARM这种等长精简指令集(RISC)的指令编码空间来说,是不可接受的。
另外,ARM处理器的设计目标涵盖从嵌入式轻量设备到高性能处理机群,类似NEON和SVE这样的SIMD指令,典型是针对高性能计算的,所以把针对高性能处理的设计使用在嵌入式领域,不管是成本还是功耗都是不能接受的。
为了解决这些问题,ARM就推出了SVE技术。首先是32个Z寄存器(数据寄存器)的位宽从128bit到2048bit是实现相关的。因此针对不同的设备就可以使用不同的位宽,嵌入式处理器可以使用128bit来节省功耗和成本,高性能处理器可以使用最长的2048bit提供最好的并行性。
另外SVE技术中的VLA(Vector Length Agnostic)算法可以在运行时断定硬件的真正位宽,从而实现相同的image(不需修改代码,不需重新编译)直接跑在支持SVE的不同位宽的处理器上,比如嵌入式设备和高性能机群上。
比如上面的NEON指令的例子,对应的SVE指令为:
ADD V10.S, V8.S, V9.S
ADD V10.D, V8.D, V9.D
可以看出,SVE指令中去掉了描述并行度的向量长度,从而减少了指令数,节省了指令编码空间,也提高了执行的效率(更好的指令cache命中率,更快的指令解码等)。但SVE是如何断定向量长度呢?
使用如下的C代码举例说明:
void example01(int *restrict a, const int *b, const int *c, long N)
{
long i;
for (i = 0; i < N; ++i)
a[i] = b[i] + c[i];
}
这段代码对应的汇编为:
# x0 is 'a', x1 is 'b', x2 is 'c', x3 is 'i', x4 is 'N'
mov x3, 0 # set 'i=0'
b cond # branch to 'cond'
loop_body:
ld1w z0.s, p0/z, [x1, x3, lsl 2] # load vector z0 from address 'b + i'
ld1w z1.s, p0/z, [x2, x3, lsl 2] # same, but from 'c + i' into vector z1
add z0.s, p0/m, z0.s, z1.s # add the vectors
st1w z0.s, p0, [x0, x3, lsl 2] # store vector z0 at 'a + i'
incw x3 # increment 'i' by number of words in a vector
cond:
whilelt p0.s, x3, x4 # build the loop predicate p0, as p0.s[idx] = (x3+idx) < x4
# it also sets the condition flags
b.first loop_body # branch to 'loop_body' if the first bit in the predicate
# register 'p0' is set
ret
可以看到,真正的SVE的加法指令是这样的:
ADD Z0.S, P0/M, Z0.S, Z1.S
SVE技术引入了16个P寄存器,作为一次操作的Z寄存器的bitmask,每个bit代表Z寄存器中的1个byte,因此其位宽是Z寄存器的1/8。其中只有Z寄存器中对应P寄存器bit为1的byte才会被执行加法运算。同样对于load和store指令也存在类似的指令。
关键的是WHILELT指令,这条指令根据待处理数据的向量长度N,寄存器的位宽,自动产生循环代码完成向量加法运算。比如N是75,也就是300个byte,而我们Z寄存器位宽比如是256个byte(2048bit),因此要执行两次加法指令。WHILELT指令在第一次循环时把P寄存器置为全1,第2次把低44bit置为1,其余bit置为0。
另外,SVE还增加了不连续内存的读写指令(Gather-load/scatter-store),可以一次性从不连续内存中为向量赋值,或写回。
SVE还有一些其他的技术可以用来支持并行计算,本文不再赘述。
通过SVE技术,ARM和日本富士通联合发布了A64FX的CPU处理器,用于Fugaku高性能计算机,最终于2020年6月在TOP500超级计算机排名中位列榜首。
SVE技术在ARMv8里是后期增加的扩展,但在ARMv9中SVE2则成为ARM在AI领域的重要技术基石。SVE是为ARMv8增加了高性能计算的能力,但SVE2可以看作是SVE和NEON的超集,在SVE的基础上,把NEON擅长的能力进一步增强,比如DSP能力等。因此可以用于未来各种领域:
前面讲了这么多,其实讲的都是ARM在A系列CPU上的技术投入。但ARM在AI上的布局并不仅仅于此。
CPU是通用处理器,在AI领域的优势是低成本。但除了ARMv9,ARMv8-M也在不断的发展,ARMv8.1-M中已经增加了向量计算能力和各种AI相关的扩展。类似于ARMv9会包括当前所有ARMv8的能力扩展,相信未来发布的ARMv9-M也会包括ARMv8-M中扩展的AI能力,为更低端的嵌入式设备提供AI的解决方案。
ARM在Mali系列GPU上已经耕耘多年,已经有了比较完备的技术能力,具备完整的产品线。而近年来推出的Ethos系列NPU,更是展示了ARM在AI上的决心。其中Ethos-N是针对边缘和云端的AI计算的,Ethos-U则是针对端上AI计算提供的microNPU解决方案。
ARMv9 + ARMv9-M + Mali + Ethos-N + Enthos+U的各种组合,形成了ARM在AI领域的完整解决方案图谱。再加上包括ARM-NN,CMSIS-NN等各种相关软件生态的建设,ARM在AI领域下的一盘大棋,已经万事具备,蓄势待发。
本文主要介绍了ARM在ARMv9中发布的SVE2技术,以及ARM在AI上的其他布局,以期窥探其在AI领域的野心。如果仅从SVE2技术点来看,这个升级并未像ARMv8中的64bit一样,能大大拓展ARM芯片的应用场景,因此我并不觉得足以支撑ARM的新一轮腾飞。
但ARMv9中并不仅仅是AI能力的加强,同时还在安全上做了重要技术更新。下一篇会详细介绍CCA和MTE两个技术。届时我们可以再来看看这个问题,是否能有新的答案。敬请期待。