1

我正在学习 MicroPython 的汇编程序(PyBoard 的 ARM Thumb2 指令集)。

有没有比这更快的方法来检查 FPU 寄存器(s0)的符号(正/负)?

@micropython.asm_thumb
def float_array_abs(r0, r1):
    label(LOOP)
    vldr(s0, [r0, 0])
    vmov(r2, s0)         # 1
    cmp(r2, 0)           # 2
    itt(mi)              # 3
    vneg(s0, s0)
    vstr(s0, [r0, 0])
    add(r0, 4)
    sub(r1, 1)
    bgt(LOOP)

这可行,但它似乎不是“正确”的解决方案(不确定 的符号r2总是与 的符号匹配s0),我怀疑它必须在少于两个指令中是可能的。

更新 1:

根据评论(谢谢),我进一步提高了代码的速度:

@micropython.asm_thumb
def float_array_abs1(r0, r1):
    label(LOOP)
    ldr(r2, [r0, 0])
    cmp(r2, 0)         # this works for some reason
    bge(SKIP)
    vmov(s0, r2)
    vneg(s0, s0)
    vstr(s0, [r0, 0])  # this can be skipped if not negative
    label(SKIP)
    add(r0, 4)
    sub(r1, 1)
    bgt(LOOP)

但它仍然留下一个问题,这是确定 FP 值符号的可靠方法吗?

以下是我系统上四个浮点值的字节表示形式供参考:

-1.0 0xbf800000
-0.0 0x80000000
 0.0 0x00000000
 1.0 0x3f800000

我想如果这取决于硬件,那么我不应该依赖它来确定标志......

我认为这可能是“正确”的做法(即正确的 FPU 比较):

def float_array_abs2(r0, r1):
    mov(r2, 0)
    vmov(s1, r2)
    label(LOOP)
    vldr(s0, [r0, 0])
    vcmp(s0, s1)
    vmrs(APSR_nzcv, FPSCR)
    itt(mi)
    vneg(s0, s0)
    vstr(s0, [r0, 0])
    add(r0, 4)
    sub(r1, 1)
    bgt(LOOP)

但我对此进行了计时,它比上面的代码慢了 11% ( float_array_abs1)。因此,如果它是一个可靠的解决方案,那么使用早期的代码会很好。

更新 2:

@Ped7g 提出了该方法and 0x7FFFFFFF(见评论)。

我对此进行了测试,它确实有效。这是代码:

@micropython.asm_thumb
def float_array_abs3(r0, r1):
    movwt(r3, 0x7FFFFFFF)
    label(LOOP)
    ldr(r2, [r0, 0])
    and_(r2, r3)
    str(r2, [r0, 0])
    add(r0, 4)
    sub(r1, 1)
    bgt(LOOP)

更正:它比float_array_abs1上面更快。这似乎是最好的解决方案,但它是否强大?

4

1 回答 1

1

and对于 IEEE 754 二进制浮点格式,如float和 ,将符号位掩码为 0是安全且最佳的double

它将根据需要将 -Inf 转换为 +Inf。它将转换-NaN+NaN,但它仍然是 NaN。

NaN 由全为 1 的指数和非零有效数表示。Inf 是有效数为零的全1 指数。(https://en.wikipedia.org/wiki/Single-precision_floating-point_format

大多数代码不关心 NaN 的有效负载或符号,只关心它NaN,因此清除符号位就可以了。


ARM 可以使用整数 SIMD NEON 指令一次执行 4 个单精度浮点数。我不知道 VFP(非 NEON 硬件 FPU)是否支持 AND 指令。

相关:使用 SSE AND 计算绝对值的最快方法也是 x86 上的最佳方法。


顺便说一句,在单独的循环中执行此操作可能会浪费内存带宽。在读取数组的循环中动态执行绝对值可能是最好的,除非您在写入一次后多次读取该数组。至少如果您可以在 FP 寄存器中执行 AND。将 AND 加载到整数寄存器中,然后将数学指令从整数移动到 FP 会很糟糕。

通常,您希望循环中的计算强度更高(为每个内存负载做更多的 ALU 工作)。

于 2018-04-05T22:16:38.383 回答