首页 > 解决方案 > 为什么循环展开会在 ARM Cortex-a53 上带来如此多的加速?

问题描述

我在以 AArch64 状态运行的 ARM Cortex-a53 处理器上使用以下代码进行循环展开:

void do_something(uint16_t* a, uint16_t* b, uint16_t* c, size_t array_size)
{
  for (int i = 0; i < array_size; i++)
  {
    a[i] = a[i] + b[i];
    c[i] = a[i] * 2;
  }
}

使用标志-O1,我得到了以下程序集,

.L3:
    ldrh    w3, [x0, x4]
    ldrh    w5, [x1, x4]
    add     w3, w3, w5
    and     w3, w3, 65535
    strh    w3, [x0, x4]
    ubfiz   w3, w3, 1, 15
    strh    w3, [x2, x4]
.LVL2:
    add x4, x4, 2
.LVL3:
    cmp x4, x6
    bne .L3

在 162 毫秒内完成(a、b、c 的大小很大)。为简单起见,我在循环之前省略了一些序言和结语代码,但它们仅用于堆栈设置等。

然后我展开循环,生成如下代码:

void add1_opt1(uint16_t* a, uint16_t* b, uint16_t* c, size_t array_size)
{
  for (int i = 0; i < array_size/4; i+=4)
  {
    a[i]   = a[i] + b[i];
    c[i]   = a[i] * 2;
    a[i+1] = a[i+1] + b[i+1];
    c[i+1] = a[i+1] * 2;
    a[i+2] = a[i+2] + b[i+2];
    c[i+2] = a[i+2] * 2;
    a[i+3] = a[i+3] + b[i+3];
    c[i+3] = a[i+3] * 2;
  }
}

它给出了如下的程序集(仍然使用-O1,因为使用-O0,编译器正在做一些愚蠢的事情):

.L7:
    ldrh    w1, [x0]
    ldrh    w5, [x3]
    add w1, w1, w5
    and w1, w1, 65535
    strh    w1, [x0]
    ubfiz   w1, w1, 1, 15
    strh    w1, [x2]
    ldrh    w1, [x0, 2]
    ldrh    w5, [x3, 2]
    add w1, w1, w5
    and w1, w1, 65535
    strh    w1, [x0, 2]
    ubfiz   w1, w1, 1, 15
    strh    w1, [x2, 2]
    ldrh    w1, [x0, 4]
    ldrh    w5, [x3, 4]
    add w1, w1, w5
    and w1, w1, 65535
    strh    w1, [x0, 4]
    ubfiz   w1, w1, 1, 15
    strh    w1, [x2, 4]
    ldrh    w1, [x0, 6]
    ldrh    w5, [x3, 6]
    add w1, w1, w5
    and w1, w1, 65535
    strh    w1, [x0, 6]
    ubfiz   w1, w1, 1, 15
    strh    w1, [x2, 6]
.LVL8:
    add x4, x4, 4
.LVL9:
    add x0, x0, 8
    add x3, x3, 8
    add x2, x2, 8
    cmp x4, x6
    bcc .L7

这几乎就像复制和粘贴其他汇编代码 4 次一样。问题是,为什么这段代码只需要 28 毫秒就可以运行,这就像 5 倍的速度。像这样简单的循环条件,我认为分支预测应该在两个代码中都做得很好,对吧?在第二个汇编代码中,商店也是交错的。所以我无法想象这样的代码如何能得到这么多的加速。

标签: assemblyarmloop-unrolling

解决方案


问题出在这里:for (int i = 0; i < array_size/4; i+=4)

循环直到array_size/4将完成四分之一的工作。

它应该是for (int i = 0; i < array_size; i+=4)

然后你应该看到几个百分比的更可解释的加速。


推荐阅读