Performance 手臂装配:绝对值功能:两行还是三行更快?

Performance 手臂装配:绝对值功能:两行还是三行更快?,performance,optimization,assembly,arm,cortex-m3,Performance,Optimization,Assembly,Arm,Cortex M3,在我的嵌入式系统课程中,我们被要求将给定的C函数AbsVal重新编码到ARM汇编中。 我们被告知,我们能做的最好的是三行。我下定决心要找到一个双线解决方案,并最终找到了,但我现在的问题是,我到底是降低了性能还是提高了性能 C代码: unsigned long absval(signed long x){ unsigned long int signext; signext = (x >= 0) ? 0 : -1; //This can be done with an ASR

在我的嵌入式系统课程中,我们被要求将给定的C函数AbsVal重新编码到ARM汇编中。 我们被告知,我们能做的最好的是三行。我下定决心要找到一个双线解决方案,并最终找到了,但我现在的问题是,我到底是降低了性能还是提高了性能

C代码:

unsigned long absval(signed long x){
    unsigned long int signext;
    signext = (x >= 0) ? 0 : -1; //This can be done with an ASR instruction
    return (x + signet) ^ signext;
}
助教/教授的三线解决方案

ASR R1, R0, #31         ; R1 <- (x >= 0) ? 0 : -1
ADD R0, R0, R1          ; R0 <- R0 + R1
EOR R0, R0, R1          ; R0 <- R0 ^ R1
ADD R1, R0, R0, ASR #31 ; R1 <- x  + (x >= 0) ? 0 : -1
EOR R0, R1, R0, ASR #31 ; R0 <- R1 ^ (x >= 0) ? 0 : -1
asr1,R0,#31;R1=0)?0 : -1

加上R0,R0,R1;R0这里是另一个两种指令版本:

    cmp     r0, #0
    rsblt   r0, r0, #0
将其转换为简单代码:

  if (r0 < 0)
  {
    r0 = 0-r0;
  }
if(r0<0)
{
r0=0-r0;
}

即使在Cortex-A8和A9这样的现代ARM-CPU内核上,该代码也应该非常快。

浏览ARM.com并获取最新版本。第3-4页第3.3.1节说明了指令时间。幸运的是,它们在Cortex-M3上非常简单

我们可以从这些计时中看出,在一个完美的“无等待状态”系统中,您教授的示例需要3个周期:

ASR R1, R0, #31         ; 1 cycle
ADD R0, R0, R1          ; 1 cycle
EOR R0, R0, R1          ; 1 cycle
                        ; total: 3 cycles
您的版本需要两个周期:

ADD R1, R0, R0, ASR #31 ; 1 cycle
EOR R0, R1, R0, ASR #31 ; 1 cycle
                        ; total: 2 cycles
因此,理论上,你的速度更快

您提到“删除一次内存提取”,但这是真的吗?各自的程序有多大?因为我们使用的是Thumb-2,所以我们有16位和32位的混合指令。让我们看看它们是如何组装的:

其版本(根据语法调整):

集合到:

00000000        17c1    asrs    r1, r0, #31
00000002        1840    adds    r0, r0, r1
00000004        4048    eors    r0, r1
00000000    eb0071e0    add.w   r1, r0, r0, asr #31
00000004    ea8170e0    eor.w   r0, r1, r0, asr #31
也就是3x2=6字节

您的版本(同样,根据语法进行了调整):

集合到:

00000000        17c1    asrs    r1, r0, #31
00000002        1840    adds    r0, r0, r1
00000004        4048    eors    r0, r1
00000000    eb0071e0    add.w   r1, r0, r0, asr #31
00000004    ea8170e0    eor.w   r0, r1, r0, asr #31
这是2x4=8字节

因此,您实际上增加了代码的大小,而不是删除内存提取


但这会影响性能吗?我的建议是对它进行基准测试,为什么不进行基准测试呢。这是了解性能差异的最可靠的方法。我完全愿意这样做,但我只是被介绍这些概念。我可以要求教授帮我制定一个基准,但更重要的是,我的目标是知道为什么基准会更快或更慢。不确定基准测试是否显示获取/执行阶段时间或寄存器/ALU访问它取决于实现。A8之前的内核可能会更快地运行代码,移位通常是免费的,除非移位值在寄存器中。A8和更新的内核有多个管道,这可能允许一些并行执行,即使在循环中也可以实现,您可以尝试一下。@KenW(至少对于像x86这样的CISC芯片),基准测试是理解性能的唯一明智的方法。(性能概要基本上是不确定的)。也就是说,我对手臂知之甚少;祝你好运。反向条件减法是最有效的方法。由于ARM状态寄存器仅可选择性地更新,因此可以更早地计算符号位作为副作用。实际上,rsbne可以被视为计算abs的单一指令方法。人们可以使用movs r0,variable
从内存中同时加载值并测试符号。下一步我将在实验室进行测试,并对你的和我的进行基准测试。如果速度更快,那么我会接受答案:)很遗憾,这不能在Thumb-2上组装,但对于arm来说,它会工作得很好,我相信语法已经改变了,它应该是这样的(注释中的wow代码真的不起作用?):
2800 cmp r0,#0;BFB8-it-lt;4240 rsblt r0,r0,#0