Intereting Posts
Прочитайте координаты из файлов txt с помощью программы C Можно ли скопировать области в круглые скобки и вернуть значение в C? MPI-matrix-вектор-умножение возвращает иногда правильные иногда странные значения Исключение из правила строгого сглаживания в С от 6.5.2.3. Структура и члены профсоюза Является ли также декларатором (при использовании в объявлении параметра) в C? выражение «break» при использовании фигурных скобок в корпусе переключателя Анонимные декларации структуры законны в Ansi C? висячий указатель, причина изменения стоимости после free ()? Работа с поплавками и целыми числами Верхний регистр на языке C Самый большой и маленький из четырех целых чисел (без массивов, никаких функций, наименьших «если») C Указатель для структуры – segmentation fault Могу ли я переопределить функцию или проверить, существует ли она? Не удалось подключиться к gcc-библиотеке gcc с glibc scanf работает до точки выполнения

Избегайте накладных расходов протектора gcc?

В последнее время я столкнулся с множеством функций, когда gcc генерирует действительно плохой код на x86. Все они соответствуют шаблону:

if (some_condition) { /* do something really simple and return */ } else { /* something complex that needs lots of registers */ } 

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

Есть ли способ заставить gcc быть немного умнее и сделать это сам? Предпочтительно с опциями командной строки, а не уродливыми хаками в источнике …

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

 if (buf->pos end) { return *buf->pos++; } else { /* fill buffer */ } 

и еще один:

 if (!initialized) { /* complex initialization procedure */ } return &initialized_object; 

и другой:

 if (mutex->type == SIMPLE) { return atomic_swap(&mutex->lock, 1); } else { /* deal with ownership, etc. */ } 

Редактировать 2: Я должен был упомянуть для начала: эти функции не могут быть встроены. У них внешняя связь, и они являются библиотечным кодом. Предоставление им возможности быть встроенным в приложение приведет к возникновению всех проблем.

    Возможно, обновите версию gcc? 4.6 только что был выпущен. Насколько я понимаю, у него есть возможность «частичного inline». То есть легко интегрируемая внешняя часть функции встроена, и дорогая часть преобразуется в вызов. Но я должен признать, что я сам не пробовал.

    Изменить: заявление, которое я имел в виду из ChangeLog:

    Частичная вставка теперь поддерживается и включена по умолчанию при -O2 и выше. Функцию можно контролировать через -fpartial-inlining.

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

    Вложение при оптимизации размера (в холодных регионах программы или при компиляции с -О) было улучшено, чтобы лучше обрабатывать программы на C ++ с большим штрафом за абстракцию, что приводит к меньшему и более быстрому коду.

    Обновить

    Чтобы явно подавить вложение для одной функции в gcc, используйте:

     void foo() __attribute__ ((noinline)) { ... } 

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


    Такие функции будут регулярно автоматически вставляться без компиляции -O0 (отключить оптимизацию).

    В C ++ вы можете намекнуть на компилятор, используя ключевое слово inline

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


    Обновление я заметил, что вы добавили факт, что они являются внешними символами. (Пожалуйста, уточните вопрос с этой важной информацией). Ну, в некотором смысле, с внешними функциями, все ставки отключены. Я не могу поверить, что gcc по определению встроит всю сложную функцию в крошечного вызывающего абонента просто потому, что она только вызывается оттуда. Возможно, вы можете дать пример кода, который демонстрирует поведение, и мы можем найти правильные флаги оптимизации, чтобы исправить это?

    Кроме того , это C или C ++? В C ++ я знаю, что распространенное место для включения тривиальных функций решения inline (в основном как члены, определенные в объявлении classа). Это не приведет к конфликту связи, как с простыми (внешними) функциями C.

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

    Надеюсь, вы используете C ++, потому что здесь вы получите массу вариантов.

    Я бы сделал это так:

     static void complex_function() {} void foo() { if(simple_case) { // do whatever return; } else { complex_function(); } } 

    Компилятор мой настаивает на inlining complex_function (), и в этом случае вы можете использовать атрибут noinline на нем.

    Я бы, вероятно, реорганизовал код, чтобы стимулировать простейший случай. Тем не менее, вы можете использовать -finline-limit чтобы gcc рассматривал вложение больших функций или -fomit-frame-pointer -fno-exceptions чтобы минимизировать фрейм стека. (Обратите внимание, что последний может нарушить отладку и вызвать исключения C ++ для плохой работы.)

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

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