【问题标题】:ARM Assembly: Absolute Value Function: Are two or three lines faster?ARM 汇编:绝对值函数:两行还是三行更快?
【发布时间】:2013-05-06 03:42:10
【问题描述】:

在我的嵌入式系统课程中,我们被要求将给定的 C 函数 AbsVal 重新编码为 ARM 程序集。 有人告诉我们,我们能做的最好的就是 3 行。我决心找到一个 2 线解决方案并最终做到了,但 我现在的问题是我实际上是降低了性能还是提高了性能

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/教授的三线解决方案

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

我的 2 线解决方案

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. 删除一个内存提取

那么,哪一个实际上更快?是否取决于处理器或内存访问速度?

【问题讨论】:

  • 为什么不对其进行基准测试。这是了解性能差异的最可靠方法。
  • 我完全会这样做,但我只是被介绍给这些概念。我可以请求教授帮助我进行基准测试,但更重要的是了解 为什么 它会更快或更慢是我的目标。不确定基准测试是否显示获取/执行阶段时间或寄存器/ALU 访问
  • 这取决于实现。 A8 之前的内核可能会更快地运行您的代码,移位通常是免费的,除非您的移位值在寄存器中。 A8 和更新的内核有多个管道,可能允许一些并行执行,这可以使两者都在循环中,你可以试试pulsar.webshaker.net/ccc/result.php?lng=us
  • @KenW 越来越多(至少对于 x86 等 CISC 芯片而言),基准测试是能够理解性能的唯一明智的方法。 (性能配置文件本质上是不确定的)。也就是说,我对 ARM 知之甚少;祝你好运。
  • 反向条件减法是 IMO 的做法。由于 ARM 状态寄存器只是可选地更新,因此可以更早地计算符号位作为副作用。实际上 rsbne 可以被认为是计算 abs 的单指令方法。一个可以使用例如movs r0, variable 同时从内存中加载值并测试符号。

标签: performance optimization assembly arm cortex-m3


【解决方案1】:

访问 ARM.com 并获取 Cortex-M3 datasheet。第 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 个字节。

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

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

【讨论】:

    【解决方案2】:

    这是另外两个指令版本:

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

    翻译成简单的代码:

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

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

    【讨论】:

    • 接下来我会在实验室进行测试,然后对你的和我的进行基准测试。如果它更快,那么我会接受答案:)
    • 不幸的是,这不能在 Thumb-2 上组装,但对于 arm 来说它可以工作
    • 好吧,我相信语法已经改变了,它应该看起来像这样(哇,cmets 中的代码真的不起作用吗?):2800 cmp r0, #0; BFB8 it lt; 4240 rsblt r0, r0 ,#0
    猜你喜欢
    • 1970-01-01
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 2017-07-19
    • 1970-01-01
    • 2014-04-02
    相关资源
    最近更新 更多