我正在学习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
更快。 这似乎是最好的解决方案 ,但它是否健壮?
答案 0 :(得分:1)
使用and
将符号位屏蔽为0对于{75}二进制浮点格式(如float
和double
)是安全且最佳的。
它会根据需要将-Inf转换为+ Inf。它会将-NaN
转换为+NaN
,但它仍然是NaN。
NaN由全1指数和非零有效数表示。 Inf是全有指数,零有效数。 (https://en.wikipedia.org/wiki/Single-precision_floating-point_format)
大多数代码都不关心NaN的有效负载或符号,只是它 NaN,所以清除符号位就好了。
ARM可以使用整数SIMD NEON指令一次执行4个单精度浮点数。我不知道VFP(非NEON硬件FPU)是否支持AND指令。
相关:Fastest way to compute absolute value using SSE AND也是x86的最佳方式。
BTW,在单独的循环中执行此操作可能会浪费内存带宽。在读取数组的循环中动态执行绝对值可能是最好的,除非您在写入一次后读取此数组许多次。至少如果你能在FP寄存器中进行AND操作。加载到AND的整数寄存器然后从整数移动到FP以获得数学指令会很糟糕。通常你需要在你的循环中有更多的计算强度(从内存中为每个负载做更多的ALU工作)。