Embedded C ++: использовать STL или нет? - PullRequest
69 голосов
/ 09 февраля 2010

Я всегда был инженером по встроенному программному обеспечению, но обычно на уровне 3 или 2 стека OSI. Я на самом деле не аппаратный парень. Я обычно всегда делал телекоммуникационные продукты, обычно ручные / мобильные телефоны, что обычно означает что-то вроде процессора ARM 7.

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

Я довольно много читал о дебатах об использовании STL в C ++ во встроенных системах, и однозначного ответа нет. Есть некоторые небольшие проблемы с переносимостью, а некоторые - с размером кода и временем выполнения, но у меня есть две основные проблемы:
1 - обработка исключений; Я до сих пор не уверен, стоит ли его использовать (см. Embedded C ++: использовать исключения или нет? )
2 - Я сильно не люблю динамическое распределение памяти во встроенных системах из-за проблем, которые он может создать. У меня обычно есть пул буферов, который статически выделяется во время компиляции и который обслуживает только буферы фиксированного размера (если нет буферов, сброс системы). STL, конечно, делает много динамического распределения.

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

Каким образом я прыгаю? Супер-безопасный и потеряет большую часть того, что составляет C ++ (то есть, это больше, чем просто определение языка) и может столкнуться с проблемами позже или придется добавить много обработки исключений и, возможно, какой-то другой код сейчас?

У меня возникает соблазн просто пойти с Boost , но 1) я не уверен, будет ли он портировать на каждый встроенный процессор, который я мог бы использовать, и 2) на их сайте они говорят, что они не Не гарантирую / не рекомендую определенные его части для встраиваемых систем (особенно для FSM, что кажется странным). Если я пойду на повышение, и мы обнаружим проблему позже ....

Ответы [ 11 ]

43 голосов
/ 09 февраля 2010

Я работаю над встроенными системами в режиме реального времени каждый день. Конечно, мое определение встроенной системы может отличаться от вашего. Но мы в полной мере используем STL и исключения и не испытываем никаких неуправляемых проблем. Мы также используем динамическую память (с очень высокой скоростью; выделяем много пакетов в секунду и т. Д.), И нам пока не нужно прибегать к каким-либо пользовательским распределителям или пулам памяти. Мы даже использовали C ++ в обработчиках прерываний. Мы не используем повышение, но только потому, что какое-то государственное учреждение не позволит нам.

По нашему опыту, вы действительно можете использовать многие современные функции C ++ во встроенной среде, если вы используете свою голову и проводите собственные тесты. Я настоятельно рекомендую вам использовать Скотт Мейер Effective C ++ 3-е издание, а также C ++ Стандарты кодирования Саттера и Александреску , чтобы помочь вам использовать C ++ с нормальным стилем программирования.

Редактировать: После того, как через 2 года я получил ответ, позвольте мне опубликовать обновление. Мы далеко продвинулись в нашей разработке, и мы наконец-то нашли точки в нашем коде, где стандартные контейнеры библиотеки работают слишком медленно в условиях высокой производительности. Здесь мы фактически использовали собственные алгоритмы, пулы памяти и упрощенные контейнеры. В этом и заключается прелесть C ++: вы можете использовать стандартную библиотеку и получить все то хорошее, что она обеспечивает для 90% ваших вариантов использования. Вы не выбрасываете все это, когда сталкиваетесь с проблемами, вы просто оптимизируете проблемные места вручную.

33 голосов
/ 09 февраля 2010

Супер-сейф и потерять многое из того, что составляет C ++ (IMO, это больше, чем только определение языка) и может столкнуться с проблемами позже или добавить много обработки исключений и может быть, какой-то другой код сейчас?

У нас схожие дебаты в игровом мире, и люди спускаются с обеих сторон. Что касается процитированной части, почему вы обеспокоены потерей «большей части того, что составляет С ++»? Если это не прагматично, не используйте это. Не должно иметь значения, "C ++" или нет.

Запустите несколько тестов. Можете ли вы обойти управление памятью STL способами, которые удовлетворяют вас? Если так, стоило ли это усилий? Многие проблемы STL и boost предназначены для простого решения проблемы. Если вы разрабатываете, чтобы избежать случайного динамического распределения памяти ... STL решает конкретную проблему, с которой вы столкнулись?

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

25 голосов
/ 09 февраля 2010

В других статьях были рассмотрены важные вопросы динамического распределения памяти, исключений и возможного распространения кода. Я просто хочу добавить: не забывайте о <algorithm>! Независимо от того, используете ли вы векторы STL или простые массивы и указатели C, вы все равно можете использовать sort(), binary_search(), random_shuffle(), функции для построения и управления кучами и т. Д. Эти процедуры почти наверняка будут быстрее и менее глючными чем версии вы создаете сами.

Пример: если вы не подумаете об этом тщательно, алгоритм случайного выбора, который вы создадите самостоятельно , вероятно, приведет к перекосам ; random_shuffle() не будет.

19 голосов
/ 09 февраля 2010

Electronic Arts написала длинный трактат о том, почему STL не подходит для разработки встроенных консолей и почему им приходится писать свои собственные. Это подробная статья, но наиболее важными причинами были:

  1. Распределители STL медленные, раздутые, и неэффективно
  2. Компиляторы на самом деле не очень хороши для включения всех этих глубоких вызовов функций
  3. Распределители STL не поддерживают явное выравнивание
  4. Алгоритмы STL, поставляемые с GCC и STL MSVC, не очень производительны, потому что они очень независимы от платформы и поэтому пропускают множество микрооптимизаций, которые могут иметь большое значение.

Несколько лет назад наша компания приняла решение вообще не использовать STL, вместо этого внедрив собственную систему контейнеров, которые максимально производительны, проще в отладке и более консервативны в отношении памяти. Это была большая работа, но она окупилась много раз. Но у нас есть пространство, в котором продукты соревнуются за то, сколько они могут втиснуть в 16,6 мс при данном процессоре и объеме памяти.

Что касается исключений: они медленны на консолях, и любой, кто говорит вам иначе, не пытался их синхронизировать. Простая компиляция с их включением замедлит всю программу из-за необходимого кода пролога / эпилога - измерьте это сами, если не верите мне. На обычных процессорах это даже хуже, чем на x86. По этой причине используемый нами компилятор даже не поддерживает исключения C ++.

Повышение производительности не так много, если избегать затрат на исключение броска & mdash; это от полного отключения исключений.

14 голосов
/ 09 февраля 2010

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

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

STL не создает исключений самостоятельно, так что это не должно вызывать беспокойства. Если ваши классы не бросают, вы должны быть в безопасности. Разделите инициализацию вашего объекта на две части, позвольте конструктору создать пустой объект, а затем выполнить любую инициализацию, которая может завершиться с ошибкой в ​​функции-члене, которая возвращает код ошибки.

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

5 голосов
/ 09 февраля 2010
  1. для управления памятью вы можете реализовать свой собственный распределитель, который запрашивает память из пула. И все контейнеры STL имеют шаблон для распределителя.

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

  3. так что, я думаю, вы можете использовать STL во встроенной системе:)

4 голосов
/ 10 ноября 2016

Проект с открытым исходным кодом «Встроенная библиотека шаблонов (ETL)» предназначается для обычных проблем с STL, используемым во встроенных приложениях, путем предоставления / реализации библиотеки:

  • детерминированное поведение
  • "Создайте набор контейнеров, размер или максимальный размер которых определяется во время компиляции. Эти контейнеры должны быть в значительной степени эквивалентны тем, которые поставляются в STL, с совместимым API."
  • нет динамического выделения памяти
  • RTTI не требуется
  • незначительное использование виртуальных функций (только когда это абсолютно необходимо)
  • набор контейнеров фиксированной вместимости
  • кеш-хранилище контейнеров как непрерывно выделяемый блок памяти
  • уменьшенный размер кода контейнера
  • типобезопасные умные перечисления
  • CRC расчеты
  • контрольные суммы и хэш-функции
  • вариантов = сортировка типов безопасных соединений
  • Выбор утверждений, исключений, обработчика ошибок или отсутствие проверок на ошибки
  • Тщательно проверенный блок
  • хорошо документированный исходный код
  • и другие функции ...

Также можно рассмотреть коммерческий C ++ STL для разработчиков встраиваемых систем , предоставленный E.S.R. Labs.

3 голосов
/ 09 февраля 2010

В дополнение ко всем комментариям я бы предложил вам прочитать Технический отчет о производительности C ++ , в котором конкретно рассматриваются темы, которые вас интересуют: использование C ++ во встроенных (включая жесткие системы реального времени); как обычно выполняется обработка исключений и какие накладные расходы у нее есть; накладные расходы на бесплатное распределение магазина.

Отчет действительно хорош, так как опровергает многие популярные сведения о производительности C ++.

3 голосов
/ 09 февраля 2010

Это в основном зависит от вашего компилятора и объема вашей памяти. Если у вас есть несколько килобайт оперативной памяти, динамическое распределение памяти очень помогает. Если реализация malloc из имеющейся у вас стандартной библиотеки не настроена на ваш объем памяти, вы можете написать свой собственный, или есть хорошие примеры, такие как mm_malloc от Ralph Hempel , которые вы можете использовать для написания своего новые и удаляемые операторы сверху.

Я не согласен с теми, которые повторяют мем о том, что исключения и контейнеры stl слишком медленные, или слишком раздутые и т. Д. Конечно, он добавляет немного больше кода, чем просто malloc простого C, но разумное использование исключений может сделать код очень ясно и избегайте слишком много ошибок проверки ошибок в C.

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

Если у вас есть большой буфер в векторе, например, в какой-то момент он может перераспределиться и закончится использованием в 1,5 раза большего объема памяти, который вы намереваетесь использовать в какой-то момент при перераспределении и перемещении данных. (Например, в какой-то момент у него есть N выделенных байтов, вы добавляете данные через метод добавления или итератора, и он выделяет 2N байтов, копирует первые N и освобождает N. У вас есть 3N байтов, выделенных в какой-то момент).

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

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

Исключение, создаваемое в ARM realview 3.1:

--- OSD\#1504 throw fapi_error("OSDHANDLER_BitBlitFill",res);
   S:218E72F0 E1A00000  MOV      r0,r0
   S:218E72F4 E58D0004  STR      r0,[sp,#4]
   S:218E72F8 E1A02000  MOV      r2,r0
   S:218E72FC E24F109C  ADR      r1,{pc}-0x94 ; 0x218e7268
   S:218E7300 E28D0010  ADD      r0,sp,#0x10
   S:218E7304 FA0621E3  BLX      _ZNSsC1EPKcRKSaIcE       <0x21a6fa98>
   S:218E7308 E1A0B000  MOV      r11,r0
   S:218E730C E1A0200A  MOV      r2,r10
   S:218E7310 E1A01000  MOV      r1,r0
   S:218E7314 E28D0014  ADD      r0,sp,#0x14
   S:218E7318 EB05C35F  BL       fapi_error::fapi_error   <0x21a5809c>
   S:218E731C E3A00008  MOV      r0,#8
   S:218E7320 FA056C58  BLX      __cxa_allocate_exception <0x21a42488>
   S:218E7324 E58D0008  STR      r0,[sp,#8]
   S:218E7328 E28D1014  ADD      r1,sp,#0x14
   S:218E732C EB05C340  BL       _ZN10fapi_errorC1ERKS_   <0x21a58034>
   S:218E7330 E58D0008  STR      r0,[sp,#8]
   S:218E7334 E28D0014  ADD      r0,sp,#0x14
   S:218E7338 EB05C36E  BL       _ZN10fapi_errorD1Ev      <0x21a580f8>
   S:218E733C E51F2F98  LDR      r2,0x218e63ac            <OSD\#1126>
   S:218E7340 E51F1F98  LDR      r1,0x218e63b0            <OSD\#1126>
   S:218E7344 E59D0008  LDR      r0,[sp,#8]
   S:218E7348 FB056D05  BLX      __cxa_throw              <0x21a42766>

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

1 голос
/ 19 февраля 2014

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

...