原文链接:[Coding for Neon - Part 5: Rearranging Vectors]
https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/coding-for-neon—part-5-rearranging-vectors
前文:第1部分,第2部分,第3部分,第4部分
Introduction(介绍)
??写Neon代码的时候,有时候你会发现,在寄存器中的数据格式和你算法的要求不太符合。你可能需要重新排列向量中的元素,好让后面的算法可以将正确的部分相加。或者传给函数的格式很奇怪,必须重新排序,让高速的SIMD代码能够处理。
这种重新排序(reordering)的操作叫做置换(permutation)。Permutation指令重新排列单个元素,这些元素来自单个或多个寄存器,组成一个新的向量。
Before we begin(在开始之前)
??在使用Neon提供的重排指令之前,考虑一下是不是真的需要使用。置换指令和移动(move)指令很像,因为它们通常表示准备数据而不是处理数据所消耗的CPU周期。
在使用最少的周期来完成一项任务之前,你的代码速度不是最优的。重新安排指令的顺序通常能够优化速度。(第3部分的调度章节有提到过)
Alternatives(可选方案)
??如何避免不必要的重排?下面有一些方案:
- Rearrange your input data(重新安排输入数据)。用更合适的格式存储数据通常不需要花费任何成本,避免了在读取或者存储时置换数据。然而,在修改数据结构之前,请考虑到数据的局部性,以及对缓存性能的影响。
- Redesign your algorithm(重新设计算法)。可以考虑使用不同的算法,它用差不多相同数量的处理步骤,但是可以处理不同格式的数据。
- Modify the previous processing stage(修改前面的处理策略)。在早期处理阶段的一个小改动,调整数据存储到内存的方式,可以减少或者消除对置换的需求。
- Use interleaving loads and stores(使用交错加载和存储)。像前面讲的,加载和存储指令有交错(interleave)和反交错(deinterleave)功能。即使这不能完全消除置换的需要,但也能减少额外的指令。
- Combine approaches(组合方法)。使用上面提到的一种或多种技术仍然比置换(permutation)指令有效。
??要是这些方案都考虑过了,还是无法合适地处理数据,那就要用置换指令(permutation instructions)了。
Instructions(指令)
??Neon提供了一系列置换(permutation)指令,从基本的翻转到任意的向量重建。简单的重排可以通过单个周期的指令来实现,复杂的操作就要通过多个周期,还可能用到额外的寄存器。与往常一样,请定期对代码进行基准测试或分析,并检查处理器的技术参考手册(Cortex-A8,Cortex-A9)以获取性能详细信息。
VMOV and VSWP: Move and Swap(移动和交换)
??VMOV
和VSWP
是最简单的置换(permute)指令,拷贝整个寄存器的内容到另外的寄存器,或者交换一对寄存器的值。
尽管你可能不认为他们是置换指令,但可以用来改变两个D寄存器的值,这两个寄存器构成一个Q寄存器。例如VSWP d0, d1
交换了q0
寄存器的高64和低64位。
VREV: Reverse(反转)
??VREV
反转向量中8、16或32位元素的顺序,有3种不同的方式:
VREV16
反转每对8位子元素,组成向量中的16位元素。VREV32
反转4个8位子元素或者2个16位子元素,组成向量中的32位元素。VREV64
同理,可反转8个8、4个16或者2个32位子元素。
??使用VREV
可以反转数据的字节序,重新排列颜色分量或交换音频样本的通道。
VEXT: Extract(提取)
??VEXT
从一对现有的向量中提取字节,组成一个新的向量。这允许你生成一个新的向量,其中包含的元素跨越了一对现有的向量。VEXT
可以用来在两个向量上实现滑动窗口,在FIR滤波器上非常有用。对于置换(permutation),当两个输入操作数使用相同的向量时,也可以用来模拟按字节旋转的操作。
VTRN: Transpose(转置)
??VTRN
在一对向量之间转置8、16或32位元素。 它将向量的元素视为2x2矩阵,并转置每个矩阵。
使用多个VTRN
指令来转置较大的矩阵。例如,由16位元素组成的4x4矩阵可以使用三条VTRN
指令进行转置。
在加载向量之后或存储向量之前,这与VLD4
和VST4
执行的操作相同。 由于它们需要较少的指令,因此请尽可能尝试使用这些结构化的内存访问功能,而不要使用一系列VTRN
指令。
VZIP and VUZP: Zip and Unzip(压缩和解压)
??VZIP
交错(interleaves)一对向量的8、16或32位元素。在存储数据之前,该操作与VST2
执行的操作相同。所以如果需要在写回内存之前立即压缩数据,请使用VST2
,而非VZIP
。
VUZP
是VZIP
的反向操作,反交错(deinterleaving)一对向量的8位,16位或32位元素。在从内存加载数据之后,该操作与VLD2
所做操作相同。
VTBL, VTBX: Table and Table Extend
??VTBL
从向量表和索引向量构造一个新的向量。这是一个逐字节的表查找操作。
该表由1到4个相邻的D寄存器组成。 索引向量中的每个字节都用于索引向量表中的一个字节。 将索引值插入到结果向量中与索引向量中原始索引位置相对应的位置。
VTBL
和VTBX
在处理超出范围索引的方式上有所不同。如果一个索引超过了表的长度,VTBL
会在结果向量的相应位置插入0,但是VTBX
保持结果向量中的值不变(如上图,d2
原来是多少就是多少)。
如果将单个源向量用作表,则VTBL
允许您实现向量的任意排列,但要以设置索引寄存器为代价。 如果是在一个循环里面使用该操作,并且排列的类型不变,则可以在循环的外部初始化索引寄存器,并消除设置开销。
Others
??虽然还有其他方法可以实现类似置换的操作,例如使用加载和存储指令对单个向量元素进行操作,但是这些方法所需的重复内存访问会使它们的速度明显变慢,因此不建议使用这些方法。
Conclusion(总结)
??明智的做法是仔细考虑代码是否真的需要做数据置换(permute)。但是,当您的算法需要时,置换指令提供了一种有效的方法来将数据转换为正确的格式。
=============================================================================
本系列到此结束,欢迎光临Arm开发者网站,查看更多关于Neon的信息。