正如评论中所指出的,在内存中使用数组来处理较小的数据集通常更快。在ARM上,也有可能使用查表指令来更有效地执行此操作
数额较大
数据类型:
最多可以将四个16字节SIMD寄存器传输到
tbl
指示对于一个条目的16个字节中的每一个字节,都从部分寄存器中取相应的数字,否则为零(类似的指令)
tbx
但是,保留该值不变)。举个例子:
input: v0 = [0x00, 0x01, 0x08, 0x10, 0x12, 0x20, 0x21, 0x30, 0x3F, 0x40, ...]
tables: v4 = [0x40, 0x41, 0x42, ..., 0x4F]
v5 = [0x50, 0x51, 0x52, ..., 0x5F]
v6 = [0x60, 0x61, 0x62, ..., 0x6F]
v7 = [0x70, 0x71, 0x72, ..., 0x7F]
执行
tbl v1.16b, {v4.16b, v5.16b, v6.16b, v7.16b}, v0.16b
给出以下内容:
output: v1 = [0x40, 0x41, 0x48, 0x50, 0x52, 0x60, 0x61, 0x70, 0x7F, 0x00, ...]
使用
tbx
所有值都大于
0x3F
将被输入而不是归零:
output: v1 = [0x40, 0x41, 0x48, 0x50, 0x52, 0x60, 0x61, 0x70, 0x7F, 0x40, ...]
如何使用它来索引到寄存器中?
由于只能进行字节查找,因此需要做一些准备工作:将通用寄存器中的索引传输到SIMD寄存器,然后再传输到第二个寄存器,以便对这两个寄存器进行调整。
input: x0 = [index, 0, 0, ..., 0]
first SIMD register: v0 = [index*8, index*8+1, ..., index*8+7, 0, 0, ..., 0]
second SIMD register: v1 = [index*8-64, index*8-63, ..., index*8-57, 0, 0, ..., 0]
这是为了满足这样一个事实,即查找值必须始终在0到15(或31、47或63)之间,并且应该在这里对八个连续字节进行查找。
因此,索引将转换为每个查找表(每个
tbl
指令有一个)。如果超出范围,
tbl
结果为零,如果结果为
orr
-最后,他们在一起。
工作示例:
需要定义以下数据:
modifier: .byte 0, 1, 2, 3, 4, 5, 6, 7, -64, -63, -62, -61, -60, -59, -58, -57
输入值为
x0
。查找的值取自
lookup_table
内存位置。结果存储在
x0
:
// Load lookup table from memory
adr x1, lookup_table
ldp q8, q9, [x1]
ldp q10, q11, [x1, 32]
ldp q12, q13, [x1, 64]
ldp q14, q15, [x1, 96]
// Take value to be looked up from general-purpose register
dup v0.8b, w0
// Prepare index before lookup
adr x1, modifier
ldp d2, d3, [x1]
shl v0.8b, v0.8b, 3
add v2.8b, v0.8b, v2.8b
add v3.8b, v0.8b, v3.8b
// Do Lookup
tbl v2.8b, {v8.16b, v9.16b, v10.16b, v11.16b}, v0.8b
tbl v3.8b, {v12.16b, v13.16b, v14.16b, v15.16b}, v1.8b
orr v0.8b, v2.8b, v3.8b
// Load the result back into a general-purpose register
umov x0, v0.2d[0]
如果真的没有其他方法,也可以从通用寄存器中获取值
x8
到
x23
:
ins v8.2d[0], x8
ins v9.2d[0], x10
ins v10.2d[0], x12
// ...
ins v15.2d[0], x22
ins v8.2d[1], x9
ins v9.2d[1], x11
ins v10.2d[1], x13
// ...
ins v15.2d[1], x23