代码之家  ›  专栏  ›  技术社区  ›  Dov

索引ARMv8寄存器的最快方法是什么

  •  0
  • Dov  · 技术社区  · 5 年前

    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]
    
    0 回复  |  直到 5 年前
        1
  •  3
  •   fcdt wwesantos    5 年前

    正如评论中所指出的,在内存中使用数组来处理较小的数据集通常更快。在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
    
    推荐文章