2016-05-16 9 views
20

++ 4.9.2と5.3.1を生成++グラム上の集約初期の配列は、このコードはコンパイルに数秒かかり、52776バイトの実行ファイルを生成します。のstd :: G上の巨大なコード

#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; 
} 

sizeを増やすには、ように見えますコンパイル時間と実行可能なサイズを直線的に増やします。私はclang 3.5またはVisual C++ 2015でこの動作を再現することはできません。-Osを使用すると違いはありません。

アセンブリコードが movl命令生成、 aの初期化が展開されていることが明らかになった点検
$ time g++ -O2 -std=c++11 test.cpp 
real 0m4.178s 
user 0m4.060s 
sys  0m0.068s 

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) 

Tが非自明なコンストラクタを有し、アレイが初期化されるときにのみ起こると{}を使用してください。以下のいずれかを実行すると、g ++は単純なループを生成します。

  1. S::S()を削除します。
  2. S::S()を削除し、クラス内でS::fを初期化します。
  3. 集約初期化(= {})を削除します。
  4. -O2なしでコンパイルします。

私は最適化としてループをアンロールしていますが、これは非常に良いとは思いません。これをバグとして報告する前に、誰かがこれが期待される行動かどうかを確認できますか?

[編集:私はa new bugを開こうとしました。これは他のものとは一致しないようです。彼らは奇妙なコードジェネよりも長いコンパイル時間になりました。]

+4

ワウ。 g ++は6.1でも同様です。コンパイラにクラッシュさせて、godboltに関するバグ報告を提出する:https://godbolt.org/g/Ae75GH – NathanOliver

+0

@ NathanOliver Welp、それはちょっと確認する。ありがとう。 – isanae

+2

gccのconstexpr配列の処理も疑わしいです。 constexpr std :: arrayを初期化するときも同様です。 = make_array(...)make_array()はconstexprです。 –

答えて

12

関連するバグレポートBug 59659 - large zero-initialized std::array compile time excessiveがあるようです。これは4.9.0で「固定」と見なされていたので、私はこのテストケースを、パッチでカバーされていない回帰またはエッジゲージのいずれかと見なします。

Bug 68203 - Аbout infinite compilation time on struct with nested array of pairs with -std=c++11:GCC 4.9.0と同様に5.3.1

2件の以上の関連のバグ報告がありますが、両方の私のために何が価値があるのは、221バグレポートのテスト・ケースの症状を示すためこれは、最も可能性が高いというデフォルト コンストラクタの多くを生成しているメモリを独り占めです

アンドリューPinski 2015年11月4日7時56分57秒UTC

それらの上のループよりも。

1はこの1つの複製であることを主張

Bug 56671 - Gcc uses large amounts of memory and processor power with large C++11 bitsets

ジョナサンWakely 2016年1月26日15時12分27秒UTC

の配列の初期化の生成このconstexprコンストラクタは 問題です。

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 

これは、誰もが、GCCの開発者にあまり意味がないだろうが、それはそんなにコンパイル時間を取っているかを見ることはまだ面白いです。


Clang 3.7.0はGCCよりもはるかに優れた仕事をしています。 -O2で、それははるかに小さい実行可能ファイル(8960バイト)と、このアセンブリ、コンパイルする秒未満かかる生成:それは非常に迅速にコンパイルし、GCC 5.3.1と一方

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 <[email protected]> 
    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 <[email protected]> 
    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] 

、なし最適化をしなく依然として95328サイズの実行可能ファイルを生成します。 -O2を使用してコンパイルすると、実行可能ファイルのサイズは53912になりますが、コンパイルには4秒かかります。私は間違いなく彼らのbugzillaにこれを報告します。

+1

ありがとうございます。 clangはそれほどスマートではありません。 'f'を0以外の値に初期化すると、' memset'とループの両方を行います。しかし、それは何も展開しません。 – isanae

+0

実際、このバグレポートの[コメントの1つ](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59659#c6)のテストケースは、同様の症状で失敗しています。 – isanae

+2

@isanae [This one](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59659#c2)も同様です。私は問題が4.9.xで症状を示すことを考慮して、問題が「修正」されたとは思わない。したがって、おそらく回帰ではなく無効な修正です。 – user6342117