четверг, 6 июня 2019 г.

[prog.thoughts] Немного рефлексии на тему применимости SObjectizer-а и возможных путей его эволюции

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

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

С одной стороны, данное общение подтвердило то, что я рассказывал год назад на C++Russia 2018: какое бы проектное решение мы не выбрали, оно обязательно отторгнет от нас часть потенциальных пользователей.

Действительно, в SObjectizer, например, активно используется динамическая память и средства стандартной библиотеки для работы с многопоточностью. И, вполне ожидаемо, это работает против SObjectizer-а, когда речь заходит о специфике встраиваемых систем жесткого реального времени. Где людям хотелось бы задействовать какие-то специфические системные вызовы из RTOS API, а так же избежать динамических аллокаций после инициализации приложения.

С другой стороны, если бы SObjectizer был завязан на специфику систем жесткого реального времени, то вряд ли его можно было бы применять, например, при разработке нагруженных back-end-ов или GUI приложений. И вряд ли студент-дипломник, который не очень хорошо знает C++, смог бы использовать SObjectizer для управления модельками в ML.

Посему что имеем, то и имеем.

Но теперь хотелось бы придумать, как от того, что имеем, прийти к чему-то большему. А именно: как отхватить кусочек "рынка" встроенных систем и систем реального времени? Понятно, что нет смысла доводить SObjectizer до состояния, когда он сможет работать на 16-ти битовом микроконтроллере с 16K RAM. Но вот системы с десятками мегабайт RAM на борту и частотой в несколько сотен мегагерц -- почему бы и нет?

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

Но этот вариант, по очевидным причинам, не интересен. Big rewriting -- это жутко дорогая и жутко рискованная штука. Во-первых, на это уйдет куча времени, сил и средств. Во-вторых, придется продвигать в массы совершенно новую разработку. Которая вообще еще нигде не использовалась. Для SObjectizer-5 хотя бы истории применения есть, пусть их и немного. В-третьих, имхо, кардинально переписывать SObjectizer на C++ нет смысла. Вот переписать на какой-нибудь Rust -- это еще может быть оправдано. Но вот на C++... Сильно сомневаюсь. В прочем, на эту тему осенью прошлого года я уже более чем обстоятельно высказался.

Остается вариант перерабатывать существующий SObjectizer-5. Причем не кардинально, а аккуратно, постепенными небольшими хирургическими вмешательствами. И в этом направлении привлекательными начинают выглядеть следующие идеи:

  • Разделение SObjectizer-а на компоненты.

    Т.е. сам SObjectizer Core, который содержит только механизмы работы с агентами/кооперациями, mbox-ы, mchain-ы.

    Причем чтобы по умолчанию SObjectizer Core шел только с однопоточными env_infrastructures, т.е. чтобы всю работу SObjectizer делал только на той нити, на которой был вызван метод launch.

    Отдельно набор диспетчеров. Т.е. нужна тебе полноценная работа с агентами и разнообразными диспетчерами, то берешь SObjectizer Core и Dispatcher Pack. Получаешь то, что имеешь в SObjectizer-5.5/5.6 сейчас. Нужны только mchain-ы -- берешь только SObjectizer Core и все.

  • Сделать возможным использовать SObjectizer не только с std::mutex, std::condition_variable и std::thread. Но чтобы пользователь мог подсунуть SObjectizer-у свои аналоги, разработанные под конкретную ОС или специфическое окружение.

    Причем эта возможность должна распространяться как на SObjectizer Core, так и на Dispatcher Pack. Т.е. чтобы можно было взять любой готовый диспетчер из SObjectizer-а и заставить этот диспетчер работать не на std::thread, а на самописной обертке вокруг pthreads.

  • Дать пользователю возможность задавать собственные аллокаторы. Например, вызывает пользователь send и указывает собственный аллокатор, посредством которого должна быть выделена память для отсылаемого сообщения.

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

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

Но вот что очевидно, так это то, что двигаясь в этом направлении придется, во-первых, двигаться от SO-5.6 к SO-5.7, затем к SO-5.8 и т.д., нарушая совместимость между версиями (хоть по чуть-чуть, но нарушая). И, во-вторых, SObjectizer будет потихоньку превращаться в header-only библиотеку с heavy templates унутри. Что кому-нибудь точно не понравится.


Стоит ли двигаться по этому пути?

Отличный вопрос. Есть о чем подумать.

Благо, прямо сейчас ответ на него давать не обязательно. Пока есть задачка по RESTinio. А вот потом... Потом предстоит сделать непростой выбор.

9 комментариев:

Eugeniy комментирует...

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

eao197 комментирует...

@Eugeniy
Так не только для микроконтроллеров делается. Например, для большущей библиотеки ACE в свое время пользователь должен был сделать свой ace/config.h, в котором делались нужные настройки.

Мне такой подход не очень нравится. Допустим, есть большая программа, в которой модуль M1 использует библиотеку X с одними настройками, а модуль M125 -- библиотеку X с другими настройками. При статической линковке хорошо, если мы получим ошибку линкера.

ssko комментирует...

Мне такой подход не очень нравится. Допустим, есть большая программа, в которой модуль M1 использует библиотеку X с одними настройками, а модуль M125 -- библиотеку X с другими настройками.
Мне кажется, что с точки зрения C/C++ в этом случае разработчик сам себе злобный Буратино.

А если по смыслу поста: SObjectazer вполне может оказаться на месте в случае одноплатников типа BeagleBone и выше. С точки зрения всякой IoT это интеграционные хабы, там обычный Linux, в худшем случае с mucl, а не с глибцами. Но стоит ли довольно большой объем работы выделки, тут вам решать. Сейчас всю эту интеграционную обвязку принято делать на nodejs. Ограничения по ресурсам понятны. Кроме того, из-за цены до сих пор актуальны одноплатники, поддерживающие только gnueabi (без полноценной плавающей точки, в частности), а в этом случае с nodejs могут быть проблемы.
По сути то, чем занимаются такие хабы, понятно: принять данные по куче конкретных протоколов и отправить дальше по MQTT, разослать настройки, отправить команды на эффекторы, предоставить web-интерфейс, думаю, сами понимаете. С моей точки зрения, использование таких Linux-одноплатников будет только расти и даже преобладать.
Но и работы тоже немало, прежде всего, тестирование и оптимизация под кучу разных платформ и вариантов Linux и стандартной библиотеки C. Одна радость - это все можно делать off-target в QEMU.
Такие у меня 5 копеек.

eao197 комментирует...

@ssko
> Мне кажется, что с точки зрения C/C++ в этом случае разработчик сам себе злобный Буратино.

Как раз если использовать C++ные шаблоны (вроде того, что мы в RESTinio делаем через traits для http-сервера), то проблем особых и нет.

За исключением того, что библиотека X будет header-only и в dll-ку ее не засунешь. А это значит, что скорость компиляции кода с ее использованием будет страдать.

ssko комментирует...

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

eao197 комментирует...

@ssko
> Ну, скорости компиляции часто придается неоправданно большое значение: часто важно, а часто никакой роли не играет.

По меньшей мере есть в C++ мире отдельные личности (и в немалом количестве), которые header-only библиотеки сильно не любят и сильно разбухают по этому поводу. Очень часто аргументируя это замедлением скорости компиляции и потреблением ресурсов компилятором. Мол, на машине с 4GiB RAM уже ничего путного не скомпилируешь.

И когда начинаешь продвигать header-only либо, то с такими вещами сталкиваешься регулярно.

ssko комментирует...

Есть "рынок" статических приложений, там header-only вполне себе на месте.
Есть другой "рынок", где требуются динамические библиотеки (в случае C++ это, с моей точки зрения, то еще удовольствие). В этом случае использование header-only сопряжено с проблемами.
Критиковать header-only с позиций второго рынка - с моей точки зрения это причуда бальзаковского возраста.

zamazan4ik комментирует...

Я как раз являюсь человеком, которому очень не нравятся header-only библиотеки. Основная причина, по которым их используют в настоящее время и ранее, на мой взгляд - простота распространения и встраивания в проект. Сейчас это становится всё менее и менее актуальным с популяризацией пакетных менеджеров, которые делают очень много грязной работы за программиста. И я не хочу платить увеличенным временем компиляции только лишь потому, что разработчику библиотеки так захотелось и была возможность сделать нормальную библиотеку и опакетировать её.

eao197 комментирует...

@zamazan4ik:

Ну вот у нас и RESTinio, и SObjectizer опакечены и для conan, и для vcpkg. Но это вообще никак не влияет на то, что RESTinio является header-only библиотекой. И если в SObjectizer нужно будет добавить возможность работать и с std::mutex/std::thread, и с предоставленными пользователем аналогами, то придется переходить к header-only просто из-за повсеместного использования шаблонов.

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