assembly - 索引到 ARMv8 寄存器的最快方法是什么
问题描述
ARMv8 指令集允许访问指令中内置的任何整数寄存器,如下所示:
add x0, x1, x2 @ x0 = x1 + x2, 64 bit arithmetic
但是,有没有办法将寄存器从 0 加载到 15,例如,使用寄存器中的值?
例如,假设寄存器 x16 包含数字 5。在这种情况下,我想要 x5。
这当然可以在内存(数组)中完成,但这要慢得多。
ldr x19, [x17, x16, lsl #3]
其中 x17 是某个基地址,x16 是索引,但这需要进入内存。如果缓存,这会更慢。如果写回该值,则写入可能需要更多时间。
我能想到的唯一其他方法是某种计算的 goto:
add x18, x18, x16, lsl #6
bx x18
1:
mov x19, x0
...
2:
mov x19, x1
...
3:
mov x19, x2
...
这甚至比数组访问还要慢。
理想情况下会有一个索引模式,如:
mov x19, x[x16]
解决方案
如评论中所述,使用内存中的数组为较小的数据集执行此操作通常更快。在 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
将被 ingored 而不是归零的值:
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
指令都有一个)。如果超出范围,则返回零,如果结果在最后一起 -edtbl
则无效。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
推荐阅读
- google-chrome - Chrome 扩展 webRequest 侦听器在 Chrome 启动时不起作用
- android - 无法解析 Google 登录类
- r - 通过查找 r 中 2 个日期之间的差异来聚合列
- facebook-graph-api - Instagram 图形 API 测试阶段
- docker - 在 Google Kubernetes Engine 中启用 Stackdriver 调试
- jquery - 如果 owl-item 内部没有图像,则删除它
- selenium - 搜索某个项目并单击与之相关的另一个项目?
- javascript - 带有 Swiper 滑块的 Javascript 可滑动堆叠卡片
- c# - 表单中的 C# 分数计算器
- database - 如何保护 Firestore 上的数据库访问?