c++ - g++ -O3 为循环创建奇怪的指令
问题描述
我正在使用 c++ 编写一些用于数值计算的代码。我需要非常仔细地编写代码来帮助编译器生成好的指令。然后,我发现带有 -O3 标志的 g++ 9.2 有一些奇怪的地方。我不是组装专家,所以我需要有人帮助我或指出我错在哪里。
完整的代码可以在这里找到https://godbolt.org/z/fyuYtq。我在这里复制并粘贴关键片段
void sum_twopointer(Elem *p1, Elem *p2, ptrdiff_t stride, ptrdiff_t start, ptrdiff_t end) {
Elem sm = 0;
for(auto i = start;i != end; ++i) {
p1[0] = p2[0] + p2[0];
p1 += stride;
p2 += stride;
}
}
它是用g++ -O3
. g++ 的版本是 9.2。汇编代码是
sum_twopointer(double*, double*, long, long, long):
cmp rcx, r8
je .L32
lea r9, [0+rdx*8]
xor eax, eax
cmp rdx, 1
jne .L36
.L34:
movsd xmm0, QWORD PTR [rsi+rax]
add rcx, 1
addsd xmm0, xmm0
movsd QWORD PTR [rdi+rax], xmm0
add rax, r9
cmp r8, rcx
jne .L34
.L32:
ret
.L36:
movsd xmm0, QWORD PTR [rsi+rax]
add rcx, 1
addsd xmm0, xmm0
movsd QWORD PTR [rdi+rax], xmm0
add rax, r9
cmp r8, rcx
jne .L36
ret
据我了解,编译器正在尝试对 stride 仅为 1 的特殊情况进行一些优化,因此它为 stride==1 的情况创建了一个新分支,但它没有做任何进一步的事情。请注意, .L34 后面的代码与 .L36 后面的代码完全相同。
我为此做了一些基准测试。stride=1 和 stride=2 的性能如下所示。代码在那里https://gist.github.com/lhprojects/dac3a9fcf15bd5b1ec365ba6a87c679d
g++ -O2
---------------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------------
BM_twopointer/8192/1 3743 ns 3742 ns 185062 stride=1
BM_twopointer/8192/2 1980 ns 1980 ns 328523 stride=2
g++ -O3
---------------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------------
BM_twopointer/8192/1 5006 ns 5001 ns 120725 stride=1
BM_twopointer/8192/2 2043 ns 2041 ns 333914 stride=2
无论如何,对于 stride=1,与 -O2 相比,使用 -O3 的性能会变差。我想知道我的代码发生了什么。我是否在 C++ 中触发了一些未定义的行为?或者简单地说,g++中的代码优化存在缺陷。(如果我的英文写作让你感到很困惑,我很抱歉。)
解决方案
我相信编译器需要知道 p1 和 p2 不重叠......将它们声明为 __restrict 指针应该允许编译器实际使用 simd 指令。对我来说,它会为 stride==1 创建一个特殊情况,但对这些知识不做任何事情,这似乎很奇怪。
推荐阅读
- apache-storm - 运行几秒钟后,风暴拓扑在本地集群中关闭
- mysql - excel VBA更新SQL中的现有记录
- android - valueformatter/getFormattedValue IndexOutOfBoundsException: Index: 7, Size: 7 MpAndroidChart in Barchart
- java - Android 在 TextViews 中显示随机数
- julia - 具有许多功能的 Julia 函数组合
- javascript - 如何识别 Selenium 中没有标签的对象?
- android - 每当我在元数据部分添加某个库并修改 androidmanifest.xml 时,flutter 应用程序都会崩溃
- node.js - Firebase 将字符串转换为时间戳
- oracle - Oracle:动态查询:ORA-01704:字符串文字太长
- python - 获取由代码构建的列表的索引号