Запретить встраивание функций
Я пытаюсь уменьшить размер скомпилированного кода и заметил, что компилятор встраивает множество функций, даже те, которые я не пометил как «встроенные», но мне не удается это отключить.
р>Похоже, что компилятор использует флаг -Os по умолчанию (оптимизировать для небольшого размера программы), потому что, когда я добавляю его, результирующий размер программы тот же.
Я пробовал добавить к функциям «__attribute__((noinline))», но это не имеет никакого значения. Есть идеи, как предотвратить встраивание функций?
Я использую Visualmicro в сообществе Visual Studio 2017. Arduino 1.8.8. Плата Arduino Uno.
@Björn Morén, 👍0
Обсуждение2 ответа
В моем файле «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
Как я уже писал в комментариях, повторить эту проблему я не могу. Поэтому я прошу прощения за трату времени всех. Но мне все еще кажется странным, как работает флаг -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
- Печать string and integer LCD
- Почему мои часы реального времени показывают неверное время с моего ПК?
- Arduino uno + cnc Shield v3 + драйвер шагового двигателя A4988 + AccelStepper?
- Отправьте несколько значений int из Python в Arduino, используя pySerial
- Глобальные переменные занимают много места в динамической памяти.
- (Код ультразвукового датчика: такого файла или каталога нет)
- rfid_default_keys проверить с помощью RC522
- Команда strtok() с Serial связью
Вы пробовали
__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