std :: array с агрегатной инициализацией на g ++ генерирует огромный код

На 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;
}

Увеличение 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

Проверка кода сборки показывает, что инициализация 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)

Это происходит только тогда, когда T имеет нетривиальный конструктор, и массив инициализируется с помощью {}, Если я сделаю любое из следующего, g ++ генерирует простой цикл:

  1. Удалить S::S();
  2. Удалить S::S() и инициализировать S::f в классе;
  3. Удалить агрегатную инициализацию (= {});
  4. Компилировать без -O2,

Я все за раскрутку цикла как оптимизацию, но я не думаю, что это очень хороший вариант. Прежде чем я сообщу об этом как об ошибке, может ли кто-нибудь подтвердить, является ли это ожидаемым поведением?

[править: я открыл новая ошибка для этого, потому что другие, кажется, не совпадают. Они были больше о долгом времени компиляции, чем о странном коде.]

20

Решение

Похоже, есть сообщение об ошибке, Ошибка 59659 — слишком большое время компиляции с инициализированным нулем std :: array. Он считался «фиксированным» для 4.9.0, поэтому я рассматриваю этот тестовый случай как регрессию или граничный случай, не покрытый патчем. Для чего это стоит, два тестовых случая отчета об ошибке1, 2 проявлять симптомы для меня как на GCC 4.9.0, так и на 5.3.1

Есть еще два связанных сообщения об ошибках:

Ошибка 68203 — О бесконечном времени компиляции на структуре с вложенным массивом пар с -std = c ++ 11

Эндрю Пински 2015-11-04 07:56:57 UTC

Скорее всего, это боров памяти, который генерирует много по умолчанию
конструкторы, а не петли над ними.

Тот утверждает, что является дубликатом этого:

Ошибка 56671 — Gcc использует большие объемы памяти и мощности процессора с большими битами C ++ 11

Джонатан Уэйкли 2016-01-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 байт) и эту сборку:

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]

С другой стороны, с GCC 5.3.1, без каких-либо оптимизаций, он компилируется очень быстро, но все равно создает исполняемый файл размером 95328. Компилирование с -O2 уменьшает размер исполняемого файла до 53912, но время компиляции занимает 4 секунды. я мог бы определенно сообщить об этом их bugzilla.

12

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]