Запретить встраивание функций

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

р>

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

Я пробовал добавить к функциям «__attribute__((noinline))», но это не имеет никакого значения. Есть идеи, как предотвратить встраивание функций?

Я использую Visualmicro в сообществе Visual Studio 2017. Arduino 1.8.8. Плата Arduino Uno.

, 👍0

Обсуждение

Вы пробовали __declspec(noinline)? https://docs.microsoft.com/pl-pl/cpp/cpp/noinline?view=vs-2017, @Filip Franik

@FilipFranik Это только для компилятора Microsoft. Arduino использует GCC, который не использует специальный cr@p от Microsoft., @Majenko

@BjörnMorén Как вы определили, что он встраивает функции?, @Majenko

См. SO https://stackoverflow.com/questions/7244363/how-to-prevent-gcc-to-inline, @RamblinRose

Предполагаете ли вы, что отказ от встраивания функции уменьшает размер скомпилированного кода? Это может быть неправильно. Компилятор gcc использует флаг -Os. Принудительный вызов функции может увеличить размер кода. Вы можете показать нам свой код, и мы уменьшим его. Для Arduino Uno вы также можете переместить данные из кода в EEPROM., @Jot

Спасибо за ответы! Я создал класс с функцией без аргументов, содержащей фиктивный код. Затем я попробовал вызвать эту функцию из секции main(), сначала один раз и зафиксировал размер, затем два раза подряд и посмотрел, как она увеличила размер скомпилированного кода на 60 байт. Затем я применил атрибут noinline, и это не имело никакого значения. Однако теперь, когда я делаю то же самое, компилятор ведет себя так, как и должен. Так что прошу прощения, что потратил на это ваше время., @Björn Morén

Но с этим компилятором определенно происходит что-то подозрительное. Еще один тест, который я провел, заключался в том, чтобы сделать все частные переменные общедоступными. Размер скомпилированного кода уменьшился, что и следовало ожидать, поскольку можно было провести некоторую оптимизацию. Затем я снова сделал их частными. Скомпилированный размер остался в уменьшенном размере. Как будто компилятору нужно несколько попыток, чтобы по-настоящему оптимизировать код., @Björn Morén

Также странно, что использование флага -Os по-прежнему встраивает функции, даже если они не помечены как «встроенные». -Os означает оптимизацию по размеру. Я могу ясно сказать, что он это делает, потому что, когда я применяю к функциям атрибут noinline, размер моего скомпилированного кода уменьшается. Мне бы хотелось, чтобы был флаг компилятора, который означал бы «нет встраивания, за исключением случаев, когда используется ключевое слово inline»., @Björn Morén

То, что вы называете странным, на самом деле является чрезвычайно оптимизированным кодом, который создает компилятор gcc. GCC может (почти) делать все, что захочет. Оптимизация «lto» оптимизирует глобально, и результат трудно предсказать. Иногда переменные являются временными в регистрах и никогда не занимают место в памяти. Существуют параметры для управления встраиванием: http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html. Вы можете добавить их в эскиз с помощью #pragma. Я взял один из своих больших эскизов и попробовал несколько встроенных вариантов, но размер кода так и не стал меньше. Можете ли вы дать небольшой набросок, который станет меньше?, @Jot


2 ответа


-1

В моем файле «local.h» — макросы & другие определения, которые я часто использую, например, то, о котором вы спрашиваете, - я сохраняю это:

// Не компилируйте эту функцию встроенно
// '__NOINLINE void foo(void){ ... }'
#ifdef __GNUC__
#define __NOINLINE  __attribute__ ((noinline))
#else
#define NOINLINE error 'NOINLINE' not defined for this compiler
#endif

который дает мне довольно простой способ вызвать отсутствие встраивания и предупреждение, если я попытаюсь использовать его с компилятором, для которого я не определил его использование. Вам действительно не нужен макрос; вы можете использовать синтаксис, зависящий от gcc - я просто предпочитаю более простой (и переносимый) символ, который можно определить для любого нестрочного синтаксиса компилятора.

,

Отличная идея. Я думал сделать то же самое, чтобы избежать уродливой строки атрибута noinline., @Björn Morén

Будет ли забота о проезжающем мимо избирателе назидать остальных из нас своими причинами, почему этот ответ не отвечает на вопрос: «Есть идеи, как предотвратить встраивание функций?», @JRobert


1

Как я уже писал в комментариях, повторить эту проблему я не могу. Поэтому я прошу прощения за трату времени всех. Но мне все еще кажется странным, как работает флаг -Os. Я предположил, что это означает минимально возможный размер. Но теперь я думаю, что это означает «довольно маленький, но при этом быстрый». Посмотрите на код ниже и узнайте, как использование атрибута noinline уменьшает размер кода. На всякий случай я добавил флаг компилятора -Os, но думаю, что он уже включен по умолчанию.

class InlineClass
{
public:
    void __attribute__((noinline)) testFunc(int arg1)       // 570 байт
    //void testFunc(int arg1) // 602 байта
    //встроенный void testFunc(int arg1) // 602 байта
    {
        _var1 = arg1 + 1;
        _var2 = arg1 + 2;
        _var3 = arg1 + 3;
        _var4 = arg1 + 4;
        _var5 = arg1 + 5;
    }

private:
    volatile int _var1 = 0;
    volatile int _var2 = 0;
    volatile int _var3 = 0;
    volatile int _var4 = 0;
    volatile int _var5 = 0;
};

void setup()
{
    InlineClass ic;
    ic.testFunc(1);
    ic.testFunc(2);
    ic.testFunc(3);
    ic.testFunc(4);
    ic.testFunc(5);
}

,

Вы проверили все встроенные параметры по этой ссылке? https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html -Os включает некоторые из них, и компилятор использует эвристику, чтобы решить, что делать. Например, с опцией -finline-small-functions. У меня получается размер 570 и 602 для arduino uno, значит вы обнаружили просчет эвристики. Это впечатляющее достижение! Я попытался отключить встроенные параметры, чтобы иметь возможность управлять ими с помощью параметра компилятора, но это не сработало. Когда testFunc увеличивается, размеры остаются прежними., @Jot

@Jot Спасибо за ответ. Я поигрался с флагами компилятора, относящимися к встроенным функциям в документе, на который вы ссылаетесь, но не смог заставить его работать должным образом. Для меня это все загадка, потому что когда я выборочно применяю "__attribute__((noinline))" к определенным функциям в моем скетче, я могу сжать его на 578 байт, с 32492 до 31914 байт, что очень много, когда вы находитесь близко до предела. Может быть, компилятор неправильно адаптирован для процессора AVR? Я предполагаю, что для того, чтобы он работал с определенным набором команд, требуется большая настройка, которая, возможно, была сделана плохо?, @Björn Morén

@Jot Я также задал этот вопрос людям на Arduino.cc, посмотрим, что они скажут. Возможно, это проблема GCC или, возможно, проблема, связанная с Arduino. https://forum.arduino.cc/index.php?topic=607719.0, @Björn Morén