Оптимизация для пространства вместо скорости в C++

Когда Вы говорите, что "оптимизация", люди склонны думать "скорость". Но что относительно встроенных систем, где скорость не все, что очень важный, но память основное ограничение? Каковы некоторые инструкции, методы и приемы, которые могут использоваться для бритья тех дополнительных килобайтов в ROM и RAM? Как каждый "представляет" код для наблюдения, где чрезмерное увеличение размера памяти?

P.S. Можно было утверждать, что "преждевременно" оптимизация для пространства во встроенных системах не все это зло, потому что Вы оставляете себя большим количеством места для хранения данных и излишнего усложнения. Это также позволяет Вам сокращать аппаратные производственные затраты, потому что Ваш код может работать на ROM/RAM меньшего размера.

P.P.S. Ссылки на статьи и книги приветствуются также!

P.P.P.S. Эти вопросы тесно связаны: 404615, 1561629

42
задан Community 23 May 2017 в 12:10
поделиться

15 ответов

Мой опыт от чрезвычайно ограничена встроенная среда памяти:

  • Используйте буферы фиксированного размера. Не используйте указатели или динамическое распределение, потому что у них слишком много накладных расходов.
  • Используйте наименьший тип данных INT, который работает.
  • никогда не используют рекурсию. Всегда используйте цикл.
  • Не пропускайте много параметров функций. Вместо этого используйте глобалы. :)
30
ответ дан 26 November 2019 в 23:37
поделиться

попробуйте что-то подобное

# vote _ links a поймает все идентификаторы внутри ссылок голоса div id...

<script type="text/javascript">

  jQuery(document).ready(function() {
  jQuery(\'#vote_links a\').click(function() {// alert(\'vote clicked\');
    var det = jQuery(this).get(0).id.split("-");// alert(jQuery(this).get(0).id);
    var votes_id = det[0];


   $("#about-button").css({
    opacity: 0.3
   });
   $("#contact-button").css({
    opacity: 0.3
   });

   $("#page-wrap div.button").click(function(){
-121--1052168-

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

Я не знаю, что делает ваша программа, поэтому я не могу посоветовать алгоритмы. Многие другие писали о компиляторе. Итак, вот несколько советов по коду и данным:

  • Устраните избыточность кода. Любой повторенный код длиной три или более строк, повторенный три раза в коде, должен быть изменен на вызов функции.
  • Устраните избыточность данных. Найти наиболее компактное представление: объединить данные, доступные только для чтения, и рассмотреть возможность использования кодов сжатия.
  • Запустите код через обычный профилировщик; исключить все неиспользуемые коды.
-121--1158388-

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

2
ответ дан 26 November 2019 в 23:37
поделиться

Узнайте стоимость реализации некоторых функций C ++, таких как таблицы виртуальных функций и перегруженные операторы, которые создают временные объекты.

0
ответ дан 26 November 2019 в 23:37
поделиться

Компиляция в против с / ОС. Часто это даже быстрее, чем оптимизация для скорости в любом случае, потому что меньший размер кода == меньше пейджинга.

Скантовка COMDAT должна быть включена в компоновке (по умолчанию по умолчанию в выпуске сборки)

Будьте осторожны на упаковке структуры данных; Часто этот приводит к тому, что этот приводит к созданию компилятора (== больше памяти) для создания сборки для доступа к безветочной памяти. Использование 1 бита для логического флага является классическим примером.

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

4
ответ дан 26 November 2019 в 23:37
поделиться

Во-первых, сообщите своему компилятору, чтобы оптимизировать для размера кода. GCC имеет флаг -OS .

Все остальное находится на алгоритмическом уровне - используйте аналогичные инструменты, которые вы должны найти утечки памяти, но вместо этого ищите AlloCs и Frees, которые вы могли бы избежать.

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

2
ответ дан 26 November 2019 в 23:37
поделиться

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

  • Параметры компилятора для уменьшения размера кода (в том числе --OS и Параметры упаковки / выравнивания)

  • Параметры линейки для разбивки мертвого кода

  • , если вы загружаетесь от Flash (или ROM) в RAM, чтобы выполнить (вместо того, чтобы выполнить из Flash), затем используйте сжатый флэш-изображение и декомпресс Это с вашим загрузчиком.

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

  • Инструменты для поиска стека High-Watermark (обычно они заполняют стек с рисунком, выполняют программу, затем посмотрите, где шаблон остается), чтобы вы могли оптимально установить размер (ы) стека

  • и, конечно, , оптимизация алгоритмов, которые вы используете для следа памяти (часто за счет скорости)

13
ответ дан 26 November 2019 в 23:37
поделиться

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

также следит за исключением. С GCC я не верю, что есть растущий размер для каждого блока Try-Catch (кроме 2 функции Call S для каждого Try-Catch), но есть функция фиксированного размера, которая должна быть связана в котором можно тратить драгоценные байты

0
ответ дан 26 November 2019 в 23:37
поделиться

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

2
ответ дан 26 November 2019 в 23:37
поделиться

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

Я не знаю, что делает ваша программа, поэтому я не могу совет по алгоритмам. Многие другие написали о компиляторе. Итак, вот несколько советов по коду и данным:

  • Устранить избыточность в вашем коде. Любой повторный код, который длиной три или более строк, повторяется три раза в вашем коде, следует изменить на вызов функции.
  • Устранить избыточность в ваших данных. Найдите самое компактное представление: объединить данные только для чтения, и рассмотрите возможность использования кодов сжатия.
  • Запустите код через обычный профилировщик; Устранить весь код, который не используется.
7
ответ дан 26 November 2019 в 23:37
поделиться

Наверху того, что предлагают другие:

Предельное использование функций C ++, напишите как в ANSI C с незначительными расширениями. Стандарт (STD: :) Шаблоны используют большую систему динамического распределения. Если вы можете, избегайте шаблонов в целом. В то время как не по своей природе вредно, они делают его слишком легко создавать лоты и много машинного кода из простого, чистого, элегантных инструкций высокого уровня. Это поощряет написание таким образом, что - несмотря на все преимущества «чистый код» - очень память голоден.

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

Совместите свои структуры вручную или используйте пакет #Pragma

{char a; long b; char c; long d; char e; char f; } //is 18 bytes, 
{char a; char c; char d; char f; long b; long d; } //is 12 bytes.

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

Разумное равновесие использование Malloc () / новых и статических структур.

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

разверните короткие петли.

for(i=0;i<3;i++){ transform_vector[i]; }

дольше, чем

transform_vector[0];
transform_vector[1];
transform_vector[2];

, не делайте этого на более длинные.

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

2
ответ дан 26 November 2019 в 23:37
поделиться

Несколько очевидных

  • , если скорость не имеет критической, выполнить код непосредственно из Flash.
  • Объявление постоянных таблиц данных с использованием const . Это позволит избежать скопированных данных от Flash в RAM
  • , упаковывать большие таблицы данных плотно с использованием наименьших типов данных и в правильном порядке, чтобы избежать прокладки.
  • Используйте сжатие для больших наборов данных (до тех пор, пока код сжатия не перевешивает данные)
  • Выключите обработку исключения и RTTI.
  • Кто-нибудь упомянул с использованием -OS? ; -)

Складные знания в данные

Одним из правил философии UNIX Philosophy может помочь сделать код более компактным:

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

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

Коды журнала + двоичные данные вместо текста

вместо регистрации простого текста, кодов событий журнала и двоичных данных. Затем используйте «скатербюр», чтобы восстановить сообщения о событиях. Сообщения в развеселении могут даже содержать спецификаторы формата в стиле PrintF, чтобы значения данных событий аккуратно отображаются в тексте.

Минимизируйте количество потоков

Каждый поток нуждается в его блоке памяти для стека и TSS. Там, где вам не нужно вытеснение, рассмотрите возможность выполнить ваши задачи выполнять совместно в рамках одного и того же потока ( совместное многозадачность ).

Используйте пулы памяти вместо накопления

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

Динамическое распределение только при инициализации

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

12
ответ дан 26 November 2019 в 23:37
поделиться

Создайте файл карты с вашего линкера. Он покажет, как распределяется память. Это хорошее начало при оптимизации для использования памяти. Он также покажет все функции и о том, как выложено кодовое пространство.

7
ответ дан 26 November 2019 в 23:37
поделиться

Похоже, что мой .inf. Вот что сработало для меня:

[version]
Signature="$CHICAGO$"
AdvancedINF=2.0

[Setup Hooks]
hook1=hook1

[hook1]
run=msiexec.exe /i "%EXTRACT_DIR%\MySetup.msi" /qn

Чтобы сделать cab:

CABARC.EXE N MyActiveX.cab MySetup.msi setup.inf
-121--4460497-

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

-121--1683697-

Многие уже были упомянуты, но вот мой список:

  • Узнайте, что может сделать компилятор. Читать документацию компилятора, экспериментировать с примерами кода. Проверьте настройки.
  • Проверьте сгенерированный код на целевом уровне оптимизации. Иногда результаты удивляют и часто получается, что оптимизация на самом деле замедляет ситуацию (или просто занимает слишком много места).
  • выберите подходящую модель памяти . Если вы нацелены на действительно небольшую плотную систему, большая или огромная модель памяти может быть не лучшим выбором (но обычно проще всего программировать для...)
  • Предпочитайте статическое распределение . Использовать динамическое выделение только при запуске или более статически выделенный буфер (статический буфер пула или максимального размера экземпляра).
  • Используйте типы данных стиля C99 . Используйте наименьший достаточный тип данных для типов места хранения. Локальные переменные, такие как переменные цикла, иногда более эффективны с «быстрыми» типами данных.
  • Выберите входящих кандидатов. Некоторые параметры тяжелой функции с относительно простых тел лучше, когда встроены. Или рассмотрим проходную структуру параметров. Глобалы тоже вариант, но будьте осторожны - тесты и обслуживание могут стать трудными, если кто-то в них недостаточно дисциплинирован.
  • Используйте ключевое слово const , помните о последствиях инициализации массива.
  • Файл сопоставления , идеально также с размерами модулей. Проверьте также, что включено из crt (это действительно необходимо?).
  • Рекурсия просто сказать нет (ограниченное пространство стека)
  • Плавающие пункты числа - предпочитает фиксированную точку math. имеет тенденцию включать и вызывать много кода (даже для простого сложения или умножения).
  • C++ вы должны знать C++ ОЧЕНЬ ХОРОШО. Если вы этого не сделаете, програмируйте ограниченные встраиваемые системы в C, пожалуйста. Те, кто осмеливается, должны быть осторожны со всеми расширенными конструкциями C++ (наследование, шаблоны, исключения, перегрузка и т.д.). Рассмотреть возможность приближения к коду HW Скорее Super-C и C++ используются там, где он считается: в высокоуровневой логике, GUI и т. д.
  • Отключить все, что вам не нужно в настройках компилятора (будь то части библиотек, языковые конструкции и т. д.)

Последнее, но не менее важное - при поиске наименьшего возможного размера кода - не переусердствуйте . Следите также за производительностью и ремонтопригодностью. Избыточно оптимизированный код имеет тенденцию к очень быстрому распаду.

3
ответ дан 26 November 2019 в 23:37
поделиться

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

Но будьте осторожны! Знайте, что вы пишете такие вещи (я писал случайно, я сам), и документирую то, что вы делаете. Оригинальные разработчики, похоже, не осознают то, что они делали, поэтому намного сложнее управлять, чем должно быть.

1
ответ дан 26 November 2019 в 23:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: