Как я могу сказать gcc не встраивать функцию?

126

Скажем, у меня есть эта небольшая функция в исходном файле

static void foo() {}

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

vehomzzz
источник
Спасибо за вопрос! Я профилировал с помощью oprofile, когда функция не отображалась, ответы здесь исправили это.
Саймон А. Эугстер,
c ++: stackoverflow.com/questions/3329214/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Ответы:

149

Вам нужен атрибут gcc-specific noinline.

Этот атрибут функции предотвращает рассмотрение функции для встраивания. Если функция не имеет побочных эффектов, есть оптимизации, отличные от встраивания, которые вызывают оптимизацию вызовов функций, хотя вызов функции активен. Чтобы такие вызовы не оптимизировались, поставьте asm ("");

Используйте это так:

void __attribute__ ((noinline)) foo() 
{
  ...
}
Алекс Тингл
источник
32
Используя gcc 4.4.3 в Arch Linux, я получаю синтаксическую ошибку с указанным выше атрибутом. Он работает правильно, когда он предшествует функции (например, attribute ((noinline)) void foo () {})
mrkj
2
Ардуино также хотел, чтобы его поместили перед функцией.
Питер Н Льюис,
2
Отредактировано для исправления синтаксиса атрибута.
Quuxplusone
1
Конструкция asm ("") на самом деле довольно кроссплатформенная и выполняет свою работу. Я сделал это для x86 Linux, и это не вызвало проблем со сборкой на PowerPC AIX. Спасибо за это полезное предложение!
Марти
1
Подход, требующий повсеместного изменения кода, нельзя с полным основанием считать приемлемым ответом.
ajeh
31

В GCC есть переключатель под названием

-fno-inline-small-functions

Так что используйте это при вызове gcc. Но побочным эффектом является то, что все другие небольшие функции также не встроены.

lukmac
источник
Не работает на уровне компилятора. Использовал gcc 5.2.1 20150902 (Red Hat 5.2.1-2)
Джон Грин
Либо текущий GCC 6.4 сломан, либо этот и более простой -fno-inlineне работают вообще. gdbеще входит в методы на step-over. Что-то сломано, и я сомневаюсь, что это так gdb.
ajeh
Он отключит встроенную оптимизацию для всех, а не только для указанной функции.
где23
@ajeh Отсутствие встраивания функций означает, что они вызываются нормально, не так ли?
Мелебиус
21

Переносимый способ сделать это - вызвать функцию через указатель:

void (*foo_ptr)() = foo;
foo_ptr();

Хотя это дает разные инструкции для ветвления, что может не быть вашей целью. Что приводит точку хорошего: что это ваша цель?

Энди Росс
источник
2
Если указатель определен в области видимости файла, а не статичен, он должен работать, поскольку компилятор не может предположить, что он имеет начальное значение во время использования. Если это локальный (как показано), он почти наверняка обрабатывается так же, как foo (). («В этом десятилетии», - добавил он, глядя на даты)
greggo
16

Я знаю, что вопрос касается GCC, но я подумал, что было бы полезно получить некоторую информацию о компиляторах и других компиляторах.

noinline Атрибут функции GCC также очень популярен среди других компиляторов. Его поддерживают как минимум:

  • Clang (сверьтесь с __has_attribute(noinline))
  • Компилятор Intel C / C ++ (их документация ужасна, но я уверен, что он работает на 16.0+)
  • Oracle Solaris Studio возвращается как минимум к версии 12.2
  • Компилятор ARM C / C ++ вернулся как минимум к версии 4.1
  • IBM XL C / C ++ вернулся как минимум к 10.1
  • TI 8.0+ (или 7.3+ с --gcc, который определит __TI_GNU_ATTRIBUTE_SUPPORT__)

Кроме того, MSVC поддерживает возврат __declspec(noinline) к Visual Studio 7.1. Intel, вероятно, тоже поддерживает это (они пытаются быть совместимыми как с GCC, так и с MSVC), но я не удосужился проверить это. Синтаксис в основном такой же:

__declspec(noinline)
static void foo(void) { }

PGI 10.2+ (и, вероятно, старше) поддерживает noinlineпрагму, которая применяется к следующей функции:

#pragma noinline
static void foo(void) { }

TI 6.0+ поддерживает FUNC_CANNOT_INLINE прагму, которая (что досадно) по-разному работает в C и C ++. В C ++ он похож на PGI:

#pragma FUNC_CANNOT_INLINE;
static void foo(void) { }

В C, однако, имя функции обязательно:

#pragma FUNC_CANNOT_INLINE(foo);
static void foo(void) { }

Cray 6.4+ (и, возможно, ранее) использует аналогичный подход, требуя имя функции:

#pragma _CRI inline_never foo
static void foo(void) { }

Oracle Developer Studio также поддерживает прагму, которая принимает имя функции, по крайней мере, в Forte Developer 6 , но обратите внимание, что она должна идти после объявления, даже в последних версиях:

static void foo(void);
#pragma no_inline(foo)

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

Если, OTOH, вас устраивает то, что подходит большинству людей, вам может сойдет с рук то, что немного более эстетично и не требует повторения. Это подход, который я использовал для Hedley , где текущая версия HEDLEY_NEVER_INLINE выглядит так:

#if \
  HEDLEY_GNUC_HAS_ATTRIBUTE(noinline,4,0,0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
#  define HEDLEY_NEVER_INLINE __attribute__((__noinline__))
#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0)
#  define HEDLEY_NEVER_INLINE __declspec(noinline)
#elif HEDLEY_PGI_VERSION_CHECK(10,2,0)
#  define HEDLEY_NEVER_INLINE _Pragma("noinline")
#elif HEDLEY_TI_VERSION_CHECK(6,0,0)
#  define HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
#else
#  define HEDLEY_NEVER_INLINE HEDLEY_INLINE
#endif

Если вы не хотите использовать Hedley (это единственный общедоступный заголовок / CC0), вы можете преобразовать макросы проверки версии без особых усилий, но больше, чем я готов вложить ☺.

nemequ
источник
Спасибо за ссылку на ваш проект @nemequ. Я попросил других наших разработчиков оценить его для нашего использования. У нас разные архитектуры.
Daisuke
Мне было бы очень интересно узнать, что они говорят, особенно если им это не интересно. И, конечно же, я готов ответить на вопросы (система отслеживания ошибок GitHub, электронная почта и т. Д.).
nemequ
14

Если вы получили ошибку компилятора для __attribute__((noinline)), вы можете просто попробовать:

noinline int func(int arg)
{
    ....
}
Сэм Ляо
источник
10
static __attribute__ ((noinline))  void foo()
{

}

Это то, что у меня сработало.

KenBee
источник
8

Используйте noinline атрибут :

int func(int arg) __attribute__((noinline))
{
}

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

Крис Лутц
источник
2

Работаю с gcc 7.2. Мне особенно нужно было, чтобы функция не была встроена, потому что ее нужно было создать в библиотеке. Я попробовал и __attribute__((noinline))ответ, и asm("")ответ. Ни один из них не решил проблему.

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

Это своего рода подвох, но он работает.

Офри Садовски
источник
Вы можете определить свою функцию inline void foo(void) { ... }в заголовке и объявить ее extern inline void foo(void);в исходном файле библиотеки. Следуя семантике C99, компилятору будет разрешено встраивать функцию, когда ему угодно, И выдавать объектный код в вашу библиотеку. См. "Встроенный" без "static" или "extern" когда-либо полезен в C99? ,
diapir