Optimization 快速饱和和移位ARM asm中的两个半字

Optimization 快速饱和和移位ARM asm中的两个半字,optimization,assembly,arm,bit-manipulation,simd,Optimization,Assembly,Arm,Bit Manipulation,Simd,我在一个32位字中有两个有符号的16位值,我需要将它们右移(除以)到常量值(可以是1到6),然后饱和到字节(0..0xFF) 比如说, 0x FFE1 00AA具有shift=5的必须变为0x 0000 0005 0x 2345 1234必须变成0x 00FF 0091 我试图同时饱和这些值,类似于下面的伪代码: AND RT, R0, 0x80008000; - mask high bits to get negatives ORR RT, RT, LSR #1 ORR RT, RT, L

我在一个32位字中有两个有符号的16位值,我需要将它们右移(除以)到常量值(可以是1到6),然后饱和到字节(0..0xFF)

比如说,

  • 0x FFE1 00AA具有shift=5的必须变为0x 0000 0005
  • 0x 2345 1234必须变成0x 00FF 0091
我试图同时饱和这些值,类似于下面的伪代码:

AND RT, R0, 0x80008000; - mask high bits to get negatives
ORR RT, RT, LSR #1
ORR RT, RT, LSR #2
ORR RT, RT, LSR #4
ORR RT, RT, LSR #8; - now its expanded signs in each halfword
MVN RT, RT
AND R0, RT; now negative values are zero
; here something to saturate high overflow and shift after
但是我得到的代码非常难看而且速度很慢 我现在拥有的最好(最快)的东西是每一半的单独饱和度,如下所示:

MOV RT, R0, LSL #16
MOVS RT, RT, ASR #16+5
MOVMI RT, #0
CMP RT, RT, #256
MOVCS RT, #255
MOVS R0, R0, ASR #16+5
MOVMI R0, #0
CMP R0, R0, #256
MOVCS R0, #255
ORR R0, RT, R0, LSL #16
TST R0, 0xE000E000
BEQ no_saturation_needed
... ; saturation ops here
但它有10个周期(能快一点吗?

p、 美国:后来我找到了这方面的USAT16指令,但它只适用于ARMv6。我需要代码来处理ARMv5TE和ARMv4


编辑:现在我重写我的第一个代码:

ANDS RT, 0x10000, R0 << 1;      // 0x10000 is in register. Sign (HI) moves to C flag, Sign (LO) is masked
SUBNE RT, RT, 1;            // Mask LO with 0xFFFF if it's negative
SUBCS RT, RT, 0x10000;      // Mask HI with 0xFFFF if it's negative
BIC R0, R0, RT;         // Negatives are 0 now. The mask can be used as XOR too
TST R0, 0xE0000000;         // check HI overflow             
ORRNE R0, R0, 0x1FE00000        // set HI to 0xFF (shifted) if so
TST R0, 0x0000E000          // check LO overflow             
ORRNE R0, R0, 0x00001FE0        // set LO to 0xFF if so          
AND R0, 0x00FF00FF, R0 >> 5;    // 0x00FF00FF is in register     
andsrt,0x10000,R0>5;//0x00FF00FF在寄存器中

但是它并不漂亮。

你所拥有的和你将要为解决这个问题所做的一样好。如果你在一个紧密的循环中对大量数据这样做,并且可以负担一些寄存器来保存掩码,你可能可以节省一两个周期,但这不会是一个很大的改进。这种类型的掩码没有很好的支持在v6架构之前,ARM上的“小矢量”饱和操作


基本上,除非这是程序中唯一的瓶颈,否则是时候把它放在一边,转到下一个热点了。

使用一个检查为两个操作设置标志是一个好主意。但我不能为第二部分做这件事。我可以做一些其他的事情:)这是一个通用的变体,用于从1到6的任何转换:

;prepare:
MOV RMask, ((0xFF00 << shift) & 0xFF00) << 16;  Mask overflow bits
MOV R_0xFF00FF, 0xFF;
ORR R_0xFF00FF, 0xFF000000;
;...
; innerloop:
;....
TST R0, RMask, R0 << 16;            Set flags for LO half
ORRNE R0, R0, 0xFF << shift;        It is overflow. First try positive
BICMI R0, R0, 0xFF << shift;        Fix it if negative. LO half is ready
TST R0, RMask, R0;              Set flags for HI half. Can TST R0, R0, #Mask also
ORRNE R0, R0, 0xFF << (shift+16)
BICNE R0, R0, 0xFF << (shift+16)
AND R0, R_0xFF00FF, R0 >> shift;        Shift and mask

你有没有试过用C写,然后看看编译器产生了什么?史蒂夫,我不知道如何用C写,而不进行单独的一半处理。但我的大脑产生了一些想法:)其中一个是“异或掩码”。它必须包含0(每一半单独),如果数字是确定的。如果数字是负数,它将包含自身。如果是正溢出,它将包含数字^0xFFFF。因此结果将是源代码^mask。但我不知道如何同时执行。是的,你是对的。我的代码在内部循环中。我优化了一些数据处理,使其不使用字节,而是使用字节对。一切都很好,但不需要现在,innerloop占用了大约35%的时间:)这个任务对我来说很有趣,所以我想尝试对它进行更多优化。我喜欢下面这样的复杂代码片段:或者,也许我们可以从这个任务中创建一些漂亮的代码:)