Я немного поиграл с сборкой x86-64, пытаясь узнать больше о различных расширениях SIMD, доступных (MMX, SSE, AVX).Большие различия в генерации кода GCC при компиляции как C++ vs C
Чтобы увидеть, как различные C или C++-конструкции преобразуются в машинный код GCC, я использовал Compiler Explorer, что является превосходным инструментом.
Во время одной из моих «сессий игры» я хотел увидеть, как GCC может оптимизировать простую инициализацию времени выполнения целочисленного массива. В этом случае я попытался записать числа от 0 до 2047 в массив из 2048 целых без знака.
Код выглядит следующим образом:
unsigned int buffer[2048];
void setup()
{
for (unsigned int i = 0; i < 2048; ++i)
{
buffer[i] = i;
}
}
Если включить оптимизацию и AVX-512 инструкций -O3 -mavx512f -mtune=intel
GCC 6.3 создает некоторые действительно умный код :)
setup():
mov eax, OFFSET FLAT:buffer
mov edx, OFFSET FLAT:buffer+8192
vmovdqa64 zmm0, ZMMWORD PTR .LC0[rip]
vmovdqa64 zmm1, ZMMWORD PTR .LC1[rip]
.L2:
vmovdqa64 ZMMWORD PTR [rax], zmm0
add rax, 64
cmp rdx, rax
vpaddd zmm0, zmm0, zmm1
jne .L2
ret
buffer:
.zero 8192
.LC0:
.long 0
.long 1
.long 2
.long 3
.long 4
.long 5
.long 6
.long 7
.long 8
.long 9
.long 10
.long 11
.long 12
.long 13
.long 14
.long 15
.LC1:
.long 16
.long 16
.long 16
.long 16
.long 16
.long 16
.long 16
.long 16
.long 16
.long 16
.long 16
.long 16
.long 16
.long 16
.long 16
.long 16
Однако, когда я проверил, что бы быть сгенерированным, если тот же код был скомпилирован с использованием C-компилятора GCC, добавив флаги -x c
. Я был очень удивлен.
Я ожидал, что аналогичные, если не идентичные, результаты, но С-компилятор, похоже, генерирует много более сложный и предположительно также гораздо более медленный машинный код. Полученная сборка слишком велика, чтобы вставить здесь полностью, но ее можно просмотреть на сайте godbolt.org, указав ссылку this.
Фрагмент сгенерированного кода, строки 58 до 83, можно увидеть ниже:
.L2:
vpbroadcastd zmm0, r8d
lea rsi, buffer[0+rcx*4]
vmovdqa64 zmm1, ZMMWORD PTR .LC1[rip]
vpaddd zmm0, zmm0, ZMMWORD PTR .LC0[rip]
xor ecx, ecx
.L4:
add ecx, 1
add rsi, 64
vmovdqa64 ZMMWORD PTR [rsi-64], zmm0
cmp ecx, edi
vpaddd zmm0, zmm0, zmm1
jb .L4
sub edx, r10d
cmp r9d, r10d
lea eax, [r8+r10]
je .L1
mov ecx, eax
cmp edx, 1
mov DWORD PTR buffer[0+rcx*4], eax
lea ecx, [rax+1]
je .L1
mov esi, ecx
cmp edx, 2
mov DWORD PTR buffer[0+rsi*4], ecx
lea ecx, [rax+2]
Как вы можете видеть, этот код имеет много сложных движений и прыжков и в целом чувствует себя очень сложный способ выполнения простой инициализации массива.
Почему существует такая большая разница в сгенерированном коде?
Является ли компилятор GCC C++ лучше вообще при оптимизации кода, который действителен как на C, так и на C++ по сравнению с C-компилятором?
Дополнительная точка данных: использование 'static unsigned int buffer [2048];' делает код C похожим. Вы должны будете фактически использовать «буфер», чтобы он не был полностью устранен.Похоже, что это проблема выравнивания, дополнительный код предназначен для устранения несоосности. – Jester
Идентичный синтаксис/грамматика не подразумевает идентичную семантику. Почему вы ожидаете, что ** разные ** языки C и C++ должны генерировать один и тот же код? Этот вопрос лучше задавать на форуме gcc. – Olaf
@ Олаф, возможно, вы можете наполнить нас различием между семантикой в C и C++ для этой части кода –