пятница, 22 мая 2020 г.

[work.sandness] Отказался от предложения Яндекс.Практикум поучаствовать в разработке нового обучающего курса по C++

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

Но, главное, старого пса новым трюкам не обучишь. За многие годы программирования на C++ я привык к этому языку, набил шишки, научился не наступать на разбросанные там повсюду грабли. Уже даже свои мысли и идеи чуть ли не автоматически облекаешь в C++ные конструкции.

Но при этом всем я не знаю толком C++.

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

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

Да и, по большому счету, накопилась изрядная усталость от C++. Если бы не тот большой объем наработок, которые наша маленькая компания успела сделать за последние годы именно на C++, которые, к моему удивлению и радости, постепенно получают признание, то я бы лично рискнул бы сменить C++ на что-то другое. На тот же Rust, скорее всего. Или же на C#.

Но столько сил, времени и средств было вложено в наши плюсовые разработки, что выгоднее оставаться в C++. Так что это больше "выбор по неволе", а не веление сердца.

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

Но вот обучать кого-то C++... Это уже нет. Староват-с

среда, 20 мая 2020 г.

[prog.c++] Собственные ощущения от выбора C++17 в 2018, 2019 и 2020 годах

Интересно, как меняется собственное ощущение от применимости C++17 с течением времени.

Два года назад, в 2018-ом, когда мы начинали делать демо-проект Shrimp, то выбор в пользу C++17 был достаточно рискованным. Типа, ну это же изначально эксперимент, не для продакшена, да и C++17 в условиях, максимально приближенных к боевым, надо осваивать.

Год назад, в 2019-ом, когда принималось решение делать SObjectizer-5.6 уже исключительно с расчетом на C++17, было ощущение, что это не самое лучше решение и часть потенциальных пользователей от SObjectizer-а это отвернет. Но зато оно оправдано с точки зрения перспективы. Все равно популяризация SObjectizer-а идет медленно, поэтому когда информация о SObjectizer-е разойдется более-менее широко, то C++17 уже не будет проблемой. Т.е. уже в 2019-ом выбор C++17 был не то, чтобы однозначно хорошим решением, но уж точно не рискованным.

Сейчас, в 2020-ом, мы начинаем делать прототип для нового решения сразу на C++17. И это уже воспринимается как само собой разумеющийся выбор. Благо у заказчика всего одна целевая платформа и на ней по дефолту доступен gcc-8.

Ну и еще раз повторю сказанное раньше неоднократно: попрограммировав немного на C++14 уже сложно возвращаться на C++11, хотя 14-е плюсы от 11-ых не сильно отличаются. Тоже самое ощущение и после программирования на C++17: на 14-й стандарт уже некоторая ломка :)

Какой из этого вывод?

Наверное, их два.

Во-первых, пора уже переставать считать C++11 современным C++ ;)

Во-вторых, времена меняются, сидеть по 10-15 лет на древних компиляторах C++, наверное, можно. Но тут нужно отдавать себе трезвый ответ на вопрос "А зачем мне это все нужно и оправдано ли это?"

пятница, 15 мая 2020 г.

[prog.c++] Любопытное про производительность разных диспетчеров в SObjectizer

Занимаюсь сейчас разработкой нового диспетчера для SObjectizer в составе so5extra. Это asio_one_thread диспетчер. Т.е. создается отдельный экземпляр asio::io_context и на отдельной нити для этого экземпляра запускается цикл обработки событий (т.е. дергается метод io_context::run()). Вся обработка событий агентов, привязанных к такому диспетчеру, выполняется только средствами Asio. По сути, собщения на агентов на этом диспетчере доставляются через asio::post.

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

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

Когда pinger и ponger работают на контекте одной нити (т.е. привязаны к одному io_context-у):

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5_extra.disp.asio_one_thread.ping_pong -r 2700000
Configuration: separate dispatchers: no, requests: 2700000

real    0m1,056s
user    0m1,042s
sys     0m0,005s

Т.е. несколько меньше 3M ping-pong/sec, что весьма неплохо.

На штатном one_thread-диспетчере из SObjectizer-а выходит чуть больше 3M msg/sec:

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5.ping_pong -r 2700000
Configuration: active objects: no, requests: 2700000

real    0m0,768s
user    0m0,750s
sys     0m0,008s

Самая веселуха начинается когда агентов разносишь на разные нити. В случае штатного one_thread-диспетчера получается:

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5.ping_pong -r 2700000 -a
Configuration: active objects: yes, requests: 2700000

real    0m3,479s
user    0m5,092s
sys     0m1,795s

Т.е. порядка 776K ping-pong/sec.

А вот у нового asio_one_thread-диспетчера лучший результат вот такой:

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5_extra.disp.asio_one_thread.ping_pong -r 2700000 -s
Configuration: separate dispatchers: yes, requests: 2700000

real    0m18,394s
user    0m11,465s
sys     0m14,112s

Порядка 146K ping-pong/sec или почти в пять раз хуже.

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

пятница, 8 мая 2020 г.

[prog.c++] Теперь и SObjectizer, и RESTinio перечислены в списке Awesome C++

В общем-то мелочь (на самом деле нет), а очень приятно: теперь и SObjectizer, и RESTinio перечислены в важном для C++сообщества списке Awesome C++.

Для нас, как разработчиков SObjectizer и RESTinio, это важно потому, что в мире C++ люди нередко начинают поиск подходящих для себя инструментов именно с Awesome C++. В этом смысле Awesome C++ -- это нечто вроде "желтых страниц". И теперь оба наших основных OpenSource продукта на этих "желтых страницах" перечислены.

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


Как обычно добавлю, что мы очень внимательно относимся ко всем пожеланиям и предложениям наших пользователей. Поэтому если вам чего-то не хватает в SObjectizer/so5extra, RESTinio или json_dto, то дайте нам знать. Постараемся учесть ваше мнение.

Так же, если у кого-то есть сложности в освоении и/или использовании SObjectizer и RESTinio, то не стесняйтесь, спрашивайте. Обязательно поможем. И еще с большим энтузиазмом поможем вам, если вы решите заказать у нас разработку, в которой будут использованы SObjectizer или RESTinio ;)

четверг, 7 мая 2020 г.

[prog.c++.fantasy] А что, если кто-то возьмет и форкнет C++?

И будет развивать свой диалект независимо от комитета. Причем вменяемый диалект, с преферансом и куртизанками прямо сейчас, а не после принятия C++26 или C++29?

Звучит вроде бы дико. Но вот мне давеча попалась ссылка на экспериментальный язык Circle. Это форк C++17 с дополнительными возможностями по метапрограммированию, но не только. Поскольку сейчас в C++ мне больше всего не хватает паттерн-матчинга для удобной и надежной работы с optional, variant и expected, то я пробежался по описанию того, что в Circle сделано в плане поддержки паттерн-матчинга.

И знаете что? Выглядит впечатляюще.

Вот пример:

#include <cstdio>

int main() {

  struct foo_t {
    int x, y, z;
  };
  foo_t obj { 345 };

  int Z = 6;
  @match(obj) {
    // Test an expression against the initializer.
    [_, _, 3]    => printf(".z is 3\n");  // structured binding
    [  .z: 4]    => printf(".z is 4\n");  // designated binding

    // Is Z a test/expression or a binding? If the clause fails, it's got to
    // be a test.
    [_, _, Z]    => printf("Z must be a binding\n");
    _            => printf("Z must be an expression\n");
  };

  return 0;
}

И вот какая мысль внезапно посетила мою голову: а вот что, если автора Circle возьмет "под крыло" какой-нибудь крупный игрок на рынке? Типа Facebook-а, Amazon-а, Bloomberg-а или IBM... И проект превратится из усилий энтузиаста-одиночки во вполне себе живой OpenSource продукт с солидной финансовой поддержкой за плечами.

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

Но вот если форк обещает совместимость с C++17 (т.е. старые кодовые базы на него перетащить не проблема) и, при этом, более оперативное и гладкое развитие в будущем? Развитие под управлением "мягкого диктатора", а не в результате компромиссов комитета? Когда вещи, типа метапрограммирования, паттерн-матчинга и дешевых исключений в нем появляются с темпом в 6-9 месяцев, а не спустя 4-5-6 лет обсуждений в комитете?

Лично я сильно сомневаюсь, что найдется кто-то, кто вложится в поддержку Circle. Но, думаю, если бы такой нашелся, то перспективы бы открылись интересные.

воскресенье, 3 мая 2020 г.

[prog.c++] По поводу стенаний на счет сложности выразительного C++ кода

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

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

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

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

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

Можно сказать, что на собственной шкуре проверил историю, которую когда-то рассказывал Максим Янченко (aka jazzer с RSDN), про быструю и дешевую реализацию парсеров на Boost.Spirit.


Какова мораль?

Переусложнить можно все что угодно.

Но фичи C++ действительно дают возможность писать меньше, а результат получать качественно.

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

пятница, 1 мая 2020 г.

[prog.c++] К сожалению, не всегда есть возможность применять SObjectizer, а хотелось бы...

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

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

Ну а раз это старый чужой код, то SObjectizer-а там нет и в помине.

И вот на этом фоне временами становится очевидно, насколько бы проще оказалось бы доработка/сопровождение, если бы SObjectizer или что-то подобное в проекте использовалось бы.

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

Если бы данный ресурс можно было оформить в виде агента/актора или хотя бы подобия CSP-шного процесса, то все решалось бы элементарно: агент получает запросы, N из них может обрабатывать параллельно, остальные ждут в очереди. Причем очередь можно сделать с учетом приоритетов и делать любые политики обслуживания его содержимого (например, можно выявлять дубликаты запросов, т.е. если один и тот же запрос прилетел несколько раз, то его можно обработать лишь однажды).

Но ни акторов, ни CSP в проекте не было, запросы к ресурсу шли из разных рабочих нитей, причем сам ресурc не был отдельной нитью, это был объект, методы которого можно было дергать из разных тредов одновременно. Поэтому пришлось делать систему очередей на mutex-ах и куче condition_variables. Что оказалось сильно сложнее, чем если бы использовались SObjectizer-овские агенты.

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

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

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

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


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

Поэтому еще раз предлагаю тем, кто пишет код на C++, посмотреть в сторону SObjectizer-а.

Это бесплатный и свободный инструмент с большой историей. С поддержкой. С возможностью доработать под ваши нужды.

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

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


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

Ну и да, если вам нужна помощь в разработке на C++, то вы можете обратиться к нам. Даже если это не связано, ни с SObjectizer, ни с RESTinio.