Я пытался реорганизовать какой-то низкоуровневый код среднего размера, и я не могу сказать, что я слишком доволен тем, как оптимизаторы компилятора встраивают код.
Я не совсем понимаю, какВстроенный код gcc, но для моего конкретного случая я получаю скорость выполнения, эквивалентную рукописному коду в gcc 8.2.1, используя следующие параметры:
-std=c++17 -Winline
-Ofast -march=native -DNDEBUG
-finline-limit=100000 --param large-function-insns=10000 --param large-stack-frame-growth=1000
--param inline-unit-growth=1000 --param early-inlining-insns=150 --param max-early-inliner-iterations=1000
-fopenmp -fPIC
Без встроенных параметров моя программав 3 раза медленнее.Я бы ожидал более простой вариант, чтобы сказать компилятору: «Поверьте мне, когда я говорю inline, вы ДОЛЖНЫ включить его».Есть ли такая опция компилятора?
Примечания:
- Некоторые подробности о коде: есть 3 вложенных цикла для циклов, третий - SIMD, каждая итерация вычисляет сложный фиксированныйразмер линейной алгебры.Сам материал линейной алгебры не SIMD (так как цикл выше).Большая часть абстракции имеет дело с многомерными массивами и плотной линейной алгеброй (для которой нужны шаблоны выражений).
- Все функции, которые я хочу встроить, определены в модуле компиляции.У меня нет ни рекурсивных, ни виртуальных функций, я не выкидываю исключения.Мои функции являются constexpr, а не встроенными, но constexpr подразумевает неявное inline.Сторонних библиотечных вызовов нет (все вызовы - математические функции, такие как std :: sqrt).Параллелизма нет, кроме SIMD.
- В начале рефакторинга, когда еще мало функций для встраивания, проблем не возникает.Но по мере того, как я добавляю все больше и больше встроенных функций для абстрагирования кода, компилятор начинает бороться со встраиванием (и, похоже, с другими вещами, такими как SROA).Я не определяю функции ради этого, но мне нужно определить их много, чтобы они были общими.
- Я работаю над реалистичным тестом для измерения производительности.Это не микро-эталон, поэтому я уверен, что я измеряю то, что я действительно хочу измерить.
- Если функция в моем горячем цикле не встроена, я измеряю снижение производительности x2 (конечно, из-затот факт, что это предотвращает много дальнейших оптимизаций, в частности, с векторизацией и SROA)
- Я начал работать над этим с компилятором intel, который мучительно медленен и глючит в коде шаблона.Ведение рукописного исполнения было очень сложным, поэтому я переключился на gcc.
Теперь я заметил странное поведение:
- В некоторых случаях не используется
-fPIC
madegcc выдает -Winline
предупреждение о том, что оно не встроено.Я не понимаю связи между -fPIC
и встраиванием вообще. - Я не понимаю необходимости указывать ранние проходы встраивания для gcc.Я бы подумал, что
--param early-inlining-insns=150
следует использовать только для оптимизации времени компиляции, а не кода, сгенерированного gcc.Но дело в том, что если значение 50
, я получаю тихий плохой встраивание (нет предупреждения от gcc), а если значение 1000
, я также получаю плохое встраивание (gcc предупреждает меня на этот раз).Что происходит? - Я немного неохотно использую
__attribute__((always_inline))
, потому что это было бы уродливо делать для каждой маленькой функции, но мне кажется, что даже с этим атрибутом gcc иногда не включаетсяфункция.Действительно ли gcc всегда встроенные функции с этим атрибутом?
Как заставить gcc встроить все мои inline
функции?Я даже концептуально не понимаю, почему компилятору так тяжело встраивать, когда это кажется таким простым сделать вручную.Есть ли проблемы с оптимизацией масштабируемости при встраивании?