Встраиваемый С++: использовать STL или нет?

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

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

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

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

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

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

Ответ 1

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

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

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

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

Ответ 2

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

Это наш опыт, который вы действительно можете использовать во многих современных функциях С++ во встроенной среде, пока вы используете свою голову и проводите собственные тесты. Я настоятельно рекомендую вам использовать третье издание Scott Meyer Effective С++, а также стандарты кодирования Sutter и Alexandrescu С++, которые помогут вам использовать С++ с разумным стилем программирования.

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

Ответ 3

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

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

Ответ 4

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

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

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

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

Увеличение производительности не столько из-за того, что вы избегаете стоимости throw — он полностью отключает исключения.

Ответ 5

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

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

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

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

Ответ 6

  • для управления памятью вы можете реализовать свой собственный распределитель, который запрашивает память из пула. И все контейнеры STL имеют шаблон для распределителя.

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

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

Ответ 7

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

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

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

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

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

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

Исключение составляет 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>

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

Ответ 8

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

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

Ответ 9

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

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

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

Ответ 10

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

Я бы серьезно исследовал создание собственного управления памятью, созданного путем переопределения операторов new/delete. Я почти уверен, что с небольшим количеством времени это можно сделать, и это почти наверняка стоит того.

Что касается исключения, я бы туда не поехал. Исключения составляют серьезный спад вашего кода, потому что они заставляют каждый отдельный блок ({ }) иметь код до и после, позволяя поймать исключение и уничтожить любые объекты, содержащиеся внутри. У меня нет жестких данных об этом, но каждый раз, когда я видел, что эта проблема возникает, я видел подавляющее свидетельство массового замедления, вызванного использованием исключений.

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

Причина обработки исключений замедляет ваш код, потому что компилятор должен убедиться, что каждый блок ({}), с места, куда выбрано исключение, должен освободить любые объекты внутри него. Это код, который добавляется к каждому блоку, независимо от того, кто-либо когда-либо генерирует исключение или нет (поскольку компилятор не может сказать во время компиляции, является ли этот блок частью цепочки исключений).

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

Ответ 11

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