суббота, 28 сентября 2019 г.

[comp.prog.thoughts] Праздное про ноутбуки и производительность софта

Позволю себе еще раз продолжить тему ноутбуков, ибо давеча был несколько удивлен. Дело в том, что когда у меня летом обострились проблемы с моими старыми рабочими ноутами и начались поиски новой рабочей машинки, я разыскал у себя древний Lenovo ThinkPad E130. Что-то типа вот этого, с двухядерным Pentium, работающем на частоте 1.4MHz, с 4GiB RAM и 320Gb HDD.

Купил я его в начале осени 2012-го года (т.е. ровно 7 лет назад) в качестве рабочей лошадки для менеджера, коим я тогда был. Ноут маленький, не тяжелый, прочный и надежный (я как-то на него чай пролил и ему хоть бы хны, т.к. у него оказалась влагозащищенная клавиатура). Но, самое важное -- он был дешевым и его было не жалко таскать с собой повсюду. Уже не помню, во сколько он мне тогда обошелся, что-то в районе $500 что по тем временам для ноутбука было немного.

[prog.c++] В склерозник: пособия по метапрограммированию на C++11

В догонку ко вчерашнему посту зафиксирую в склерознике пару ссылок на материалы, которые помогли мы разобраться с метапрограммированием на шаблонах в современном C++:

Peter Dimov. Simple C++11 metaprogramming

Peter Dimov. Simple C++11 metaprogramming, part 2

Мне понравилось. Все коротко и по делу. Даже я смог более-менее разобраться, что уже о чем-то да говорит.

пятница, 27 сентября 2019 г.

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

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

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

Итак, потихонечку развитие RESTinio дошло до стадии, когда стало ощущаться отсутствие некоторых полезных вещей. Например, RESTinio позволяет получить доступ к значению HTTP-поля из заголовка (вроде поля "Content-Type"), но зато со значением этого поля уже нужно будет разбираться самостоятельно. Т.е. если вы берете поле "Content-Type", а там лежит "text/plain; charset=utf-8", то парсить эту строку вам нужно будет самим, вручную.

Подумалось, что это не есть хорошо и что RESTinio должно предоставлять что-то пользователю. Но что именно?

В результате было решено не делать в RESTinio полную поддержку полноценного разбора HTTP-полей, а предоставить пару простых функций, которые могут взять содержимое HTTP-поля и разбить его на отдельные значения. Так, из строки "text/plain; charset=utf-8" пользователь может с помощью RESTinio получить отдельные подстроки "text/plain" и "charset=utf-8". А с подстроками уже работать несколько проще.

Но тут возникает вопрос: а как именно должен выглядеть API для такого вот парсинга?

Первый вариант, который мне более-менее понравился и который был достаточно быстро и просто реализован, выглядит так:

вторник, 24 сентября 2019 г.

[prog] Еще один наглядный пример зачем нужен SObjectizer и подобные фреймворки

Свежая тема на LOR-е: "Тредпул и таски, таски, таски". Описание задачи в заголовке темы сумбурное и не очень понятное, но вот этот комментарий лучше поясняет что именно нужно вопрошающему. Позволю себе этот комментарий скопипастить:

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

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

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

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

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

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

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

ИМХО, полезность инструментов вроде SObjectizer, CAF, cpp-taskflow, Boost.Fiber и им подобных в том, что вы можете тупо взять и быстро набросать черновое решение вашей задачи, чтобы посмотреть что в принципе получается. Получается ли вообще. Или вообще не получается. Если более-менее получается, то что у вас с производительностью и другими показателями.

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

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

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

Делать свой велосипед можно только если вы четко, на 146%, убедились в том, что существующие чужие разработки вас совершенно не устраивают. И только если у вас есть ресурсы на это занятие. Причем не только на первоначальную реализацию, но и на последующую поддержку.

Так что, если вы не Yandex, не Kaspersky Lab, не Mail.ru и, уж тем более, не Google/Facebook/Amazon/Aliexpress, то не делайте свои велосипеды. Попробуйте сперва что-нибудь готовое. Благо, есть что попробовать.


Если у вас есть задача, но недостаточно опыта, чтобы понять, как к ней подступиться и что использовать для ее решения, то возьмите SObjectizer спросите у нас ;)

понедельник, 23 сентября 2019 г.

[prog] Наглядный пример роста объема функции при эволюции кода

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

Речь о том, что далеко не всегда объем функций/методов удается держать под жестким контролем. Даже мне, давнему фанату принципа, что код функции должен умещаться на один экран. В качестве примера приведу код функции, над которой как раз таки мне самому довелось поработать. Сначала то, что было изначально. Затем то, что получилось после начала эксплуатации, исправлении одной серьезной проблемы и добавления новых хотелок после накопления некоторого опыта эксплуатации. Код из реального проекта, поэтому все комментарии удалены.

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

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

Итак, вот с чего все начиналось: