ARM汇编:绝对值函数:快两到三行



在我的嵌入式系统课程中,我们被要求将给定的c函数AbsVal重新编码为ARM汇编程序。我们被告知我们最多只能写3行。我决定找到一个两行的解决方案,并最终做到了,但是我现在的问题是,我是否实际上降低了性能还是提高了性能。(特别是在Cortex-M3上)

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;
}

TA/Professor的3行解决方案

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

有几个地方我可以看到潜在的性能差异:

  1. 增加一个额外的算术右移调用
  2. 删除一个内存取回

那么,哪个更快呢?它是否取决于处理器或内存访问速度?

下面是另一个双指令版本:

    cmp     r0, #0
    rsblt   r0, r0, #0

转换成简单的代码:

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

即使在Cortex-A8和A9等现代arm cpu内核上,该代码也应该相当快。

在Thumb模式(Cortex-M3上唯一支持的模式)中,它实际上需要三条指令,因为it (If Then)需要谓词另一条指令。所有这些都是16位指令。

.syntax unified
  cmp r0, #0
  it lt
  rsblt r0, r0 ,#0

转到ARM.com并获取Cortex-M3数据表。第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位的混合指令可用。让我们看看它们是如何组合的:

它们的版本(根据UAL语法调整):

    .syntax unified
    .text
    .thumb
abs:
    asrs r1, r0, #31
    adds r0, r0, r1
    eors r0, r0, r1

组装:

00000000        17c1    asrs    r1, r0, #31
00000002        1840    adds    r0, r0, r1
00000004        4048    eors    r0, r1

3x2 = 6字节

您的版本(再次根据UAL语法调整):

    .syntax unified
    .text
    .thumb
abs:
    add.w r1, r0, r0, asr #31
    eor.w r0, r1, r0, asr #31

组装:

00000000    eb0071e0    add.w   r1, r0, r0, asr #31
00000004    ea8170e0    eor.w   r0, r1, r0, asr #31

这是2x4 = 8字节

所以你并没有删除内存提取,而是增加了代码的大小。

但是这会影响性能吗?我的建议是对进行基准测试

最新更新