isa*_*nae 24 c++ optimization g++ loop-unrolling stdarray
在g ++ 4.9.2和5.3.1上,此代码需要几秒钟的时间来编译并生成52,776字节的可执行文件:
#include <array>
#include <iostream>
int main()
{
constexpr std::size_t size = 4096;
struct S
{
float f;
S() : f(0.0f) {}
};
std::array<S, size> a = {}; // <-- note aggregate initialization
for (auto& e : a)
std::cerr << e.f;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
增加size似乎线性增加编译时间和可执行文件大小.我无法使用clang 3.5或Visual C++ 2015重现此行为.使用-Os没有区别.
$ time g++ -O2 -std=c++11 test.cpp
real 0m4.178s
user 0m4.060s
sys 0m0.068s
Run Code Online (Sandbox Code Playgroud)
检查汇编代码显示初始化a已展开,生成4096 movl条指令:
main:
.LFB1313:
.cfi_startproc
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset 3, -16
subq $16384, %rsp
.cfi_def_cfa_offset 16400
movl $0x00000000, (%rsp)
movl $0x00000000, 4(%rsp)
movq %rsp, %rbx
movl $0x00000000, 8(%rsp)
movl $0x00000000, 12(%rsp)
movl $0x00000000, 16(%rsp)
[...skipping 4000 lines...]
movl $0x00000000, 16376(%rsp)
movl $0x00000000, 16380(%rsp)
Run Code Online (Sandbox Code Playgroud)
这只有在T具有非平凡的构造函数并且使用时初始化数组时才会发生{}.如果我执行以下任何操作,g ++会生成一个简单的循环:
S::S();S::S()并初始化S::f;= {});-O2.我将循环展开作为优化,但我不认为这是一个非常好的.在我将此报告为错误之前,有人可以确认这是否是预期的行为?
[编辑:我为此开了一个新的bug,因为其他的似乎不匹配.他们更多的是编译时间长于奇怪的codegen.
use*_*117 16
似乎有一个相关的错误报告,错误59659 - 大型零初始化std :: array编译时间过多.它被认为是4.9.0的"固定",所以我认为这个测试用例要么是回归,要么是补丁未涵盖的边缘.对于它的价值,两者的bug报告的测试用例1,2周表现出的症状,我在两个GCC 4.9.0以及5.3.1
还有两个相关的错误报告:
错误68203 - 使用嵌套数组的结构在-std = c ++ 11上的struct上的无限编译时间
Andrew Pinski 2015-11-04 07:56:57 UTC
这很可能是一个内存生成器,它会生成许多默认构造函数而不是它们的循环.
那个人声称是这个副本:
错误56671 - Gcc使用大量内存和处理器能力与大型C++ 11位集
Jonathan Wakely 2016-01-26 15:12:27 UTC
生成此constexpr构造函数的数组初始化是问题:
Run Code Online (Sandbox Code Playgroud)constexpr _Base_bitset(unsigned long long __val) noexcept : _M_w{ _WordT(__val) } { }
事实上,如果我们改变它,S a[4096] {};我们就不会遇到问题.
使用perf我们可以看到GCC花费大部分时间的地方.第一:
perf record g++ -std=c++11 -O2 test.cpp
然后perf report:
10.33% cc1plus cc1plus [.] get_ref_base_and_extent
6.36% cc1plus cc1plus [.] memrefs_conflict_p
6.25% cc1plus cc1plus [.] vn_reference_lookup_2
6.16% cc1plus cc1plus [.] exp_equiv_p
5.99% cc1plus cc1plus [.] walk_non_aliased_vuses
5.02% cc1plus cc1plus [.] find_base_term
4.98% cc1plus cc1plus [.] invalidate
4.73% cc1plus cc1plus [.] write_dependence_p
4.68% cc1plus cc1plus [.] estimate_calls_size_and_time
4.11% cc1plus cc1plus [.] ix86_find_base_term
3.41% cc1plus cc1plus [.] rtx_equal_p
2.87% cc1plus cc1plus [.] cse_insn
2.77% cc1plus cc1plus [.] record_store
2.66% cc1plus cc1plus [.] vn_reference_eq
2.48% cc1plus cc1plus [.] operand_equal_p
1.21% cc1plus cc1plus [.] integer_zerop
1.00% cc1plus cc1plus [.] base_alias_check
Run Code Online (Sandbox Code Playgroud)
除了GCC开发人员之外,对于任何人来说这都没有多大意义,但是看到正在占用如此多的编译时间仍然很有意思.
Clang 3.7.0在这方面做得比GCC好得多.在-O2花费不到一秒钟进行编译,产生一个更小的可执行文件(8960个字节),并且该组件:
0000000000400810 <main>:
400810: 53 push rbx
400811: 48 81 ec 00 40 00 00 sub rsp,0x4000
400818: 48 8d 3c 24 lea rdi,[rsp]
40081c: 31 db xor ebx,ebx
40081e: 31 f6 xor esi,esi
400820: ba 00 40 00 00 mov edx,0x4000
400825: e8 56 fe ff ff call 400680 <memset@plt>
40082a: 66 0f 1f 44 00 00 nop WORD PTR [rax+rax*1+0x0]
400830: f3 0f 10 04 1c movss xmm0,DWORD PTR [rsp+rbx*1]
400835: f3 0f 5a c0 cvtss2sd xmm0,xmm0
400839: bf 60 10 60 00 mov edi,0x601060
40083e: e8 9d fe ff ff call 4006e0 <_ZNSo9_M_insertIdEERSoT_@plt>
400843: 48 83 c3 04 add rbx,0x4
400847: 48 81 fb 00 40 00 00 cmp rbx,0x4000
40084e: 75 e0 jne 400830 <main+0x20>
400850: 31 c0 xor eax,eax
400852: 48 81 c4 00 40 00 00 add rsp,0x4000
400859: 5b pop rbx
40085a: c3 ret
40085b: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0]
Run Code Online (Sandbox Code Playgroud)
另一方面,对于GCC 5.3.1,没有优化,它编译速度非常快,但仍然生成95328大小的可执行文件.编译时-O2将可执行文件大小减少到53912,但编译时间需要4秒.我肯定会向他们的bugzilla报告这个问题.