LINUX.ORG.RU

Как компилятор определяет что можно заинлайнить?

 


0

2
int funca(int * a)
{
  *a += 1;
}

int main(void)
{
   int b = 5;
   funca(&b);
   funca(&b);
   return b;
}

Вот положим компилятор взял и заинлайнил funca.
А мы, положим, определим функцию в другом юните. Или даже так:

//file1.c

int funca(int * a)
{
  *a += 1;
}

int funcb(int * a)
{
  funca(a);
  funca(a);
}
Может компилятор заинлайнить funca в funcb? По идее может, потому что на явное объявление он кладет и сам расставляет inlinе так где ему удобно.

А теперь вопрос:

gcc file1.c -o file1.o
Все, на этом этапе уже он не сможет ее «разынлайнить» обратно.
А я возьму и залинкую эту штуку как библиотеку. И попытаюсь дернуть funca. А нету там больше funca!

Инлайн можно отрубить как сущность в gcc.

Dron ★★★★★
()
Последнее исправление: Dron (всего исправлений: 1)

Компилятор может заинлайнить, но если функция не статическая, то незаинлайненная версия тоже будет доступна.

Sorcerer ★★★★★
()

И попытаюсь дернуть funca. А нету там больше funca!

С тем же успехом ты можешь вызывать funca из, скажем, кирпича. Нет ее там, верно. Не вызывай же. Чтобы вызвать функцию, она должна быть объявлена как extern. Для обычных функций - оно неявно, а inline и extern не совместимы. Но инлайн можно определить в хэдере, и она будет генерироваться в каждой единице компиляции независимо.

JaneDoe
()
Ответ на: комментарий от JaneDoe

Для обычных функций - оно неявно, а inline и extern не совместимы

Проблема не в том что я хочу дернуть inline функцию, а в том что компилятор за меня решил что не смотря на отсутствие inline он функцию funca заинлайнил.

someoneelsenotme
() автор топика
Ответ на: комментарий от Sorcerer

А вот тут товарищ заявляет, что static == static inline. И там же второй чувак говорит что указатели на static функции - основа фабрик. Значит и static (static inline) тоже инлайнится без удаления оригинала.

http://stackoverflow.com/questions/7762731/whats-the-difference-between-stati...

someoneelsenotme
() автор топика

По размеру.
-finline-limit=300

mittorn ★★★★★
()
Ответ на: комментарий от someoneelsenotme

Хм. Работающий бы пример. Предположу, что функция в модуле сохраняется как обычная out-of-line, но для вызовов внутри модуля она может вставляться inline.

JaneDoe
()

А мы, положим, определим функцию в другом юните.

Получим ошибку линковки

По идее может, потому что на явное объявление он кладет и сам расставляет inlinе так где ему удобно.

Не совсем так, явные объявления могут помочь заинлайнить более «сложную» функцию, которая без объявления не инлайнится.

А вообще основное назначение ключевого слова inline - обход ODR.

annulen ★★★★★
()

Как компилятор определяет что можно заинлайнить?

согласно алгоритмам - см сырцы компилятора.

qulinxao ★★☆
()
Ответ на: комментарий от annulen

Получим ошибку линковки

Если хэдер подключить - не получим. Код хэдера опустил по очевидным причинам.

someoneelsenotme
() автор топика
Ответ на: комментарий от someoneelsenotme

Не разожрётся. Реально имеет смысл подставлять тело функции вместо её вызова только для тех функций, стоимость «вызова и исполнения» которых может оказаться заметно больше стоимости «просто исполнения». Оверхед на вызов может оказаться заметным только тогда, когда сама функция очень мала. То есть бинарнику просто некуда сильно разожраться, если размер дублируемых функций невелик.

Функции, которые библиотека предоставляет наружу, компилятор обязан оставлять в ней. Подстановка тела (inline) и ограничение области видимости функции (static) — ортогональные понятия.

Ну и как тут сказали выше, inline реально только на ODR влияет, потому что подстановку тела компилятор вправе и не выполнять.

devsdc ★★
()
Ответ на: комментарий от someoneelsenotme

Проблема не в том что я хочу дернуть inline функцию, а в том что компилятор за меня решил что не смотря на отсутствие inline он функцию funca заинлайнил.

Тоже недавно удивился этому. Я думал, что функция для компилятора, это то, чего он не должен касаться. По крайней мере по тому, что я видел с MS компилятором.

GCC в этом плане совсем не нравится.

fornlr ★★★★★
()
Последнее исправление: fornlr (всего исправлений: 1)
Ответ на: комментарий от someoneelsenotme

Значит и static (static inline) тоже инлайнится без удаления оригинала.

Это, кстати, не значит, что она будет видна извне как символ.

devsdc ★★
()

А я возьму и залинкую эту штуку как библиотеку. И попытаюсь дернуть funca. А нету там больше funca!

Ну так и собирай её как библиотеку, с -shared. Тогда будет. А в исполняемом файле нет никакой нужды funca держать. Кто в здравом уме будет его как библиотеку использовать? Сильно специфические потребности.

i-rinat ★★★★★
()

уж определитесь, либо отключите inline флагами, либо через extern например, чтобы символ оставался.

alwayslate ★★
()
Ответ на: комментарий от someoneelsenotme

А вот тут товарищ заявляет, что static == static inline.

inline - это всего лишь хинт компилятору, а не требование. И да, для современных компиляторов static == static inline для функций в c-файле.

второй чувак говорит что указатели на static функции - основа фабрик. Значит и static (static inline) тоже инлайнится без удаления оригинала.

Ну да, я озвучил только одно достаточное условие. Очевидно, что если код использует указатель на static-функцию, передавая его куда-то наружу (т.е. компилятор не сможет это оптимизировать), то оригинал удалён не будет.

Sorcerer ★★★★★
()

тоже кстати столкнулся с такой проблемой, в цланге правда.

есть, грубо говоря, либа libfoo с внешними функциями foo1, foo2. foo2 вызывает foo1. обе функции снаружи доступны, но foo1 инлайнится в foo2. поэтому, когда для тестов мокаю foo1 (с помощью LD_PRELOAD), поведение foo2 не меняется.

Как с этим бороться не совсем ясно, совсем отключать инлайнинг не очень хочется, руками расставлять __attribute__ ((noinline)) тоже не нравится (т.к. функций таких значительно больше, чем 2). мокать foo2 и foo1 — копи-паст разводить.

есть ли красивое универсальное решение хотя бы для gcc и clang'а?

Novel ★★★★
()

Если два объектных файла экспортируют один и тот же символ, то будет ошибка линковки (если один из них weak, то не будет, просто будет приоритет у того, который не weak, инлайн в таких условиях, очевидно, будет невозможен). После же линковки все символы, которые линковщик смог найти в объектных файлах, будут заменены на вызовы конкретных функций. Так что из so-шников они уже импортироваться не могут, потому что о них не останется информации в elf (кроме отладочной информации).

Если функция инлайнится, но не объявлена как static, то компилятор в любом случае запихнёт в объектный файл её нормальную версию. Просто внутри самого модуля она будет инланйнится. Так что никаких проблем с экспортом нет.

KivApple ★★★★★
()
Ответ на: комментарий от Novel

есть ли красивое универсальное решение хотя бы для gcc и clang'а?

Можно разместить foo1 в отдельном .c-модуле. Но это может снизить производительность, т.к. компилятор не просто так инлайнит. В sqlite решили эту проблему с производительностью сурово - см. их amalgamation.

Можно для тестирования через LD_PRELOAD собирать спец.версию с отключенным инлайнингом.

Можно попробовать переписать логику работы функций так, чтобы использовать LD_PRELOAD для тестирования не требовалось (ну, не всегда можно, конечно).

Sorcerer ★★★★★
()

конфликт у тебя будет, если две одинаковые функции в разных объектниках. линкер заругается и пошлёт тебя лесом.

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 1)
Ответ на: комментарий от fornlr

и объявляли вы эту функцию так:


class A
{
  void method()
  {
  }
};

лопата, да

а вообще, проблемы того, как функцию определять — ответственность компилятора: может конкретное железо вообще инструкцию call не поддерживает и что тогда?

anonymous
()
Ответ на: комментарий от anonymous

может конкретное железо вообще инструкцию call не поддерживает и что тогда?

Тогда для этого железа нет компилятора C.

utf8nowhere ★★★
()
Ответ на: комментарий от Sorcerer

Можно разместить foo1 в отдельном .c-модуле. Но это может снизить производительность, т.к. компилятор не просто так инлайнит.

ну это так себе вариант по двум причинам:

1. во-первых, ухудшается читаемость, т.к. модулей в проекте и так в районе ста, если по одной-две функции начинать выносить, но будет сложно ориентироваться и поддерживать.

2. во-вторых, проблема такая же, как с noinline аттрибутом — кто знает, сколько потенциально придется выносить таких функций

В sqlite решили эту проблему с производительностью сурово - см. их amalgamation.

спасибо, посмотрю.

Можно для тестирования через LD_PRELOAD собирать спец.версию с отключенным инлайнингом.

это тоже не очень красивый вариант, потому что время сборки увеличится значительно.

Можно попробовать переписать логику работы функций так, чтобы использовать LD_PRELOAD для тестирования не требовалось (ну, не всегда можно, конечно).

имеется ввиду вариант типа создания foo_enable_mock(), который меняет поведение функции?

Novel ★★★★
()
Ответ на: комментарий от someoneelsenotme

В двух? Ты не обкурился? Инлайнутая функция уже в 100500 местах инлайнута, ещё один инстанс в виде оригинала погоды не сделает.

anonymous
()
Ответ на: комментарий от Pavval

Вот этот неожиданно прав. Что и когда инлайнить — это строго не решается, только эвристиками.

anonymous
()
Ответ на: комментарий от Novel

имеется ввиду вариант типа создания foo_enable_mock(), который меняет поведение функции?

Нет, я скорей о том, чтобы foo2 перестала вызывать foo1. :)

Ну и вы же всё равно все функции так не протестируете, ведь есть ещё неэкспортируемые. А значит, для тестирования всё равно нужно так или иначе собирать модифицированную версию программы/библиотеки (как сmocka предлагает, например). Ну а раз так, то уж заодно можно собирать и без всяких оптимизаций и инлайнов. А в обычной сборке можно оставить только тесты без mock.

Sorcerer ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.