assembly - 为什么循环展开会在 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 倍的速度。像这样简单的循环条件,我认为分支预测应该在两个代码中都做得很好,对吧?在第二个汇编代码中,商店也是交错的。所以我无法想象这样的代码如何能得到这么多的加速。
解决方案
问题出在这里:for (int i = 0; i < array_size/4; i+=4)
。
循环直到array_size/4
将完成四分之一的工作。
它应该是for (int i = 0; i < array_size; i+=4)
。
然后你应该看到几个百分比的更可解释的加速。
推荐阅读
- docker - 使用 Terraform 设置 Docker 容器和网络
- javascript - 带有 vuejs 的“ts 1109”
- python - 如何根据平均值、中位数、第 1 和第 9 个十分位值生成数据集?
- java - Android Webview 不会从按钮侦听器加载 Url
- android - 如何在android studio中找到我的密钥文件路径
- postgresql - 是否可以从 PL/PGSQL 函数内部监控 PostgreSQL 服务器的性能?
- python - 基本函数中一个简单的python代码中的错误,它为接收到的数字提供动力
- sql - 关于为市场目录服务设计 SQL 数据库的建议
- c - 访问 typedef 也在外部定义的外部数组
- python-3.x - Fusepy“nothreads”参数解释