...мало что так стимулирует вернутся к тренировкам, как случайно сделанный в два дротика чекаут в 110 очков: T20+D25 :)
Размышления и впечатления, которые не хочется держать в себе. О программировании в частности. Ну и о творчестве, и о жизни вообще.
суббота, 11 июня 2016 г.
пятница, 10 июня 2016 г.
[prog.thoughts] Расширение run-time мониторинга SObjectizer-а: как бы организовать учет времени работы event-handler-ов...
Есть желание в версии 5.5.17 SObjectizer-а расширить объем мониторинговой информации, включив в нее сведения о том, как долго работают обработчики событий. Пригодиться это может, например, чтобы определять какие обработчики отжирают больше всего времени. И даже ловить ситуации, когда обработчик "повис".
Нижеследующий текст является попыткой зафиксировать некоторые соображения на этот счет. Тем не менее, буду признателен, если кто-нибудь из читателей найдет возможность поделиться своими мыслями или своим опытом в этом вопросе.
[prog.cmake] Как посредством CMake собрать и dynamic- и static-library из одних и тех же исходников?
Потихонечку реализуется список хотелок для SO-5.5.17. На очереди пункт про сборку SObjectizer-а в виде статической библиотеки. Для Mxx_ru проектные файлы уже подправлены и SO-5 через Mxx_ru можно собрать и как dynamic-, и как static-library. А вот как сделать то же самое через CMake не знаю. Буду признателен, если кто-нибудь из читателей подскажет, как вот этот CMakeLists.txt нужно переделать (он же на github-е).
среда, 8 июня 2016 г.
[prog.c++] Сложность C++ как раз и не позволит ему умереть
Глянул сегодня в блог разработчиков MSVC, в заметку, где рассказывается про улучшения поддержки expression SFINAE. И в приведенных там примерах увидел ответ на вопрос: "Сможет ли какой-то язык заменить C++ в ближайшее время".
Не сможет.
Ибо:
template <class Fp, class A0, class ...Args> inline auto invoke(Fp && f, A0 && a0, Args && ...args) -> decltype(((*forward<A0>(a0)).*f)(forward<Args>(args)...)) { return ((*forward(a0)).*f)(forward(args)...); } |
Да, это сложно. Да, это выглядит страшно. Да, нужно потратить огромное количество времени, чтобы разобраться в C++ настолько, чтобы без проблем работать с таким кодом. Ну и скажем прямо, далеко на все действующие C++ники способы на это (включая меня самого).
Однако, тут нужно понять простую вещь: необходимость писать подобный код появилась не на пустом месте. Это не Страуструп сидя за рюмкой чая с Саттером в пьяном угаре решили добавить еще пару-тройку фич, чтобы окончательно превратить C++ во write-only language. Такое усложнение языка произошло из-за повседневного использования C++ в проектах, объем и сложность которых далеко не всегда можно себе представить. Т.е. в реальной жизни есть необходимость писать сложный код. Который еще и должен быть эффективным. Для достижения чего C++ и усложняется. И будет усложняться дальше.
Ну а раз C++ усложняется не просто так и, вбирая в себя все больше и больше, упрощает решение насущных проблем, то какой должна быть вымышленная альтернатива C++, которая бы оставила его не у дел?
Альтернатива должна быть такой, чтобы приведенный выше фрагмент записывался просто и понятно. А стоимость в run-time от подобной конструкции чтобы была не выше, чем у C++ (лучше, если еще ниже).
Т.е. должно появится что-то, что умеет все то, что умеет C++, но делает это намного проще, чем C++.
Есть у меня сомнения в том, что это в принципе возможно. И, тем более, еще более сомнительно, что это реализуемо на практике.
PS. То, что для С++ пока не видно альтернатив вовсе не означает, что для C++ осталось много ниш, где его использование было бы оправдано. Очень многие вещи можно (пока?) делать без C++.
понедельник, 6 июня 2016 г.
[prog.flame] Андрей Александреску о том, что мешает D взлететь
Увидел любопытное на сайте языка D: обсуждение списка причин, которые препятствуют "взлету" D. Этот список Александреску построил на основании общения с разными разработчиками. Данный список содержит пункты, которые не дают потенциальным пользователям выбрать D в качестве рабочего инструмента:
* The garbage collector eliminates probably 60% of potential users right off.
* Tooling is immature and of poorer quality compared to the competition.
* Safety has holes and bugs.
* Hiring people who know D is a problem.
* Documentation and tutorials are weak.
* There's no web services framework (by this time many folks know of D, but of those a shockingly small fraction has even heard of vibe.d)...
* (On Windows) if it doesn't have a compelling Visual Studio plugin, it doesn't exist.
* Let's wait for the "herd effect" (corporate support) to start.
* Not enough advantages over the competition to make up for the weaknesses above.
Т.е. если кратко и по-русски, то получается что-то вроде:
- сборщик мусора мешает 60% потенциальных пользователей;
- набор инструментов так себе, у конкурентов лучше;
- в части безопасности есть и дыры, и баги;
- сложно нанимать людей, которые знали бы D;
- документация и руководства хреновые;
- нет фреймворка для Web-а (и крайне мало кто хотя бы слышал про vibe.d);
- под Windows нет нормальной интеграции с Visual Studio;
- многие ждут, пока за спиной у D встанет какая-нибудь корпорация;
- не так уж много у D преимуществ, чтобы компенсировать вышеперечисленные недостатки.
Общее впечатление от этого списка: наконец-то они начали о чем-то подозревать! :)))
Но вообще несколько удивлен увидеть пункт про сборщик мусора. Походу, ситуация такова:
- для части задач производительность не критична, поэтому можно сидеть на управляемых языках со сборкой мусора (Java, C#, Scala, Python, Ruby, Erlang -- имя им легион). Поэтому смысла связываться с нативным кодом нет от слова совсем;
- для части задач производительность важна, поэтому нужно иметь и нативный код, и сборщик мусора. Но здесь, имхо, получается что-то вроде:
- для части таких задач D со своей сложностью -- это просто оверкилл и вместо D можно использовать гораздо более простые Go и, например, Eiffel;
- для части таких задач с успехом могут использоваться языки "покруче". Скажем, Haskell или OCaml;
- для части задач производительность настолько важна, что нужно иметь и нативный код, и ручное управление памятью. А это вотчина C, Ada и C++ (плюс сюда же нацелен Rust). А языки со сборкой мусора просто идут лесом не смотря ни на какие достоинства.
Так вот, судя по присутствию в списке пункта про сборщик мусора, авторы D очень сильно ошиблись с целевой аудиторией. Нативный язык со сборкой мусора и сложностью, соперничающей со сложностью C++ (в худшем смысле этого слова), оказался мало кому нужен.
Ну и в связи с этим перспективы Rust выглядят не так уж и плохо (по крайней мере в части бодания с plain old C, а не с C++). Тогда как перспективы Nim-а или какого-нибудь Zimbu -- напротив. Ибо ниша нативных языков с GC уже более чем плотно занята Go, Eiffel, Haskell, OCaml, ну и, возможно, Objective-C и Swift-ом.
воскресенье, 5 июня 2016 г.
[prog.c++] Попытка сделать Go-шный select и ограниченные по размеру mchain-ы...
В SObjectizer вот уже более полугода есть аналоги CSP-шных каналов под названием mchains. Первоначально с mchain-ами можно было использовать только операцию receive, которая выбирала для обработки сообщения только из одного канала. Начиная с версии 5.5.16 появилась операция select, которая позволяет выбирать сообщения из нескольких mchain-ов.
SObjectizer-овский select создавался под влиянием Go-шного select-а. Но между SObjectizer-овским и Go-шным select-ами есть важное различие: SObjectizer-овский select выполняет только receive-операции из множества mchain-ов, тогда как Go-шный select может сочетать как receive-, так и send-операции.
Сейчас появилось немного времени для работы над версией 5.5.17 и пытаюсь придумать, можно ли расширить SObjectizer-овский select send-операциями. Вот здесь был показан пример с числами Фибоначчи, который при поддержке send-операций в select-е мог бы выглядеть приблизительно вот так:
void fibonacci( mchain_t values_ch, mchain_t quit_ch ) { int x = 0, y = 1; bool must_continue = true; while( must_continue ) { select( from_all(), send_case< int >( values_ch, x ).then( [&] { auto old_x = x; x = y; y = old_x + y; } ), receive_case( quit_ch, [&](quit) { must_continue = false; } ); } } |
В процессе размышлений вышел на небольшой идеологический тупик. Дело в том, что mchain-ы в SObjectizer подразделяются на две сильно разные категории: mchain-ы ограниченного и неограниченного размера. Тогда как в Go, насколько я понимаю, у каждого канала есть размер связанного с ним буфера (даже если этот размер и не задается программистом явно).
Соответственно, первая составляющая обнаруженного тупика состоит в том, что попытка задействовать send_case для mchain-а неограниченного размера не будет иметь большого смысла. Т.к. операция отсылки сообщения в канал будет всегда завершаться без длительной блокировки отправителя. А ведь ради такой блокировки send_case и нужен в select-е: если потребитель сообщений не успевает их вычитывать, то производитель "засыпает" до тех пор, пока потребитель не справится с нагрузкой, либо пока не возникнут какие-то дополнительные события (например, активизация какого-то из receive_case). Т.е. если в показанном выше примере values_ch окажется неограниченным по размеру mchain-ом, то while с select-ом очень быстро приведет к исчерпанию свободной памяти.
Но и в случае с ограниченными по размеру mchain-ами так же не все просто. Во-первых, в свойствах mchain-а может быть задан максимальный тайм-аут ожидания выполнения send-а на полном mchain-е. Т.е., если mchain полон и кто-то вызывает send, то этот send может усыпить вызвавшую его нить на некоторое ограниченное время. Соответственно, вопрос: должно ли это время оказывать влияние на ситуацию, когда send-операция инициируется внутри select-а? Скажем, для mchain-а установлен тайм-аут ожидания в 0.15s. Тогда как при вызове select-а с send-операцией нас вполне может устроить и тайм-аут в 1.5s.
Во-вторых, для ограниченного по размеру mchain-а есть overflow_reaction, т.е. задано действие, которое должно быть выполнено, если место в mchain-е не появилось даже после тайм-аута. Например, может быть порождено исключение. Или же из mchain-а может быть выброшено самое старое сообщение. Или же будет проигнорировано самое новое сообщение.
Так вот, применение каких-то из этих overflow_reaction для send_case из select-а может иметь нехорошие последствия. Скажем, выброс исключения -- это странное поведение, ведь select должен выполнять send_case только когда есть возможность записать сообщение в mchain. А такие реакции, как выброс самого старого и самого нового сообщения приведут к тому, что send_case всегда будет выполняться без какого-либо ожидания. Ну на самом деле, зачем ждать, если можно что-то выбросить, а на освободившееся место что-то добавить?
Как-то пока выходит, что mchain-ы, которые затачивались под работу с send-ами, из-за своей продвинутой функциональности не сильно хорошо дружат с select-ом в плане ожидания возможности отсылки сообщений. Как вариант, можно в send_case игнорировать настройки самого mchain-а (в частности тайм-аут ожидания на полном mchain-е и тип overflow_reaction). Но тогда получится, что одна и та же сущность ведет себя сильно по-разному в зависимости от того, каким инструментом с ней работают: для send-ов будет свое поведение, для send_case -- свое. Что не есть хорошо.
В общем, нужно думать.
PS. Еще один интересный вопрос. Допустим, если select с несколькими send_case-ами:
select( from_all(), send_case< msg1 >( ch1, ... ), send_case< msg2 >( ch2, ... ), send_case< msg3 >( ch3, ... ) ); |
Должен ли выход из такого select-а происходить после одного успешного send_case (любого из трех)? Или же после завершения всех трех send_case?
[prog.flame] Велосипедостроение как фактор интереса к новым языкам программирования?
Новые языки программирования продолжают притягивать к себе активное внимание. По крайней мере на профильных форумах, где тусуется, пусть и малая часть программистов, но зато довольно инициативная (по крайней мере в области "потрындеть на форуме"). Полагаю, у стремления к чему-то новому есть множество факторов, проистекающих из самой природы человека. А вот по следам двух недавних тем (первая, вторая), похоже, нащупал еще один фактор.
Этот фактор -- велосипедостроение. Для старых языков велосипедить как-то сложно: за время их существования, особенно если язык где-то в мейнстриме или близко к тому, уже написан вагон и маленькая тележка всяческих библиотек. Для разных прикладных областей. Разного объема и совершенно разного качества. Чтобы начать в таких условиях писать свой собственный велосипед, нужно либо очень хорошо понимать, что именно хочется сделать, зачем, почему, в чем будет профит и почему нельзя достичь нужного результата за счет использования чего-то уже существующего. Ну или не иметь мозгов (более политкорректный вариант: не иметь должного кругозора) и, за счет этого полагать, что твой собственный велосипед будет самым-самым велосипедистым велосипедом :)
На самом деле, возьмем близкий мне C++. Нужно быть достаточно самоуверенным разработчиком, чтобы сейчас на C++ начать делать, скажем, GUI-библиотеку. Ведь сразу же пойдут вопросы: а чем твоя библиотека будет лучше плюсовых же Qt, wxWidgets, FLTK, FOX и т.д.? Не говоря уже про plain-C библиотеки вроде GTK или EFL, вокруг которых можно навернуть C++ные обертки.
И попробуйте внятно ответить на этот вопрос самому себе. Уже это будет непросто. А уж донести свою точку зрения до непредвзятой аудитории (в которой будет 20% активнейших воинствующих неадекватов) окажется еще стократ сложнее.
Тогда как сделать GUI-библиотеку для Rust-а или Nim-а -- это же совсем другое дело. Ведь нет же ничего, пустая поляна. Можно брать и делать, а куча народа тебя еще и подбадривать начнет, мол, давай-давай, мы ждем, нам надо.
Полагаю, это может быть совсем немаловажный фактор. Ведь на каком-нибудь C++ или Java приходится брать готовое и с помощью этого готового решать скучные и интересные только заказчику прикладные задачи. Тогда как на Rust-е или Kotlin-е можно сделать свой собственный велосипед (коих для C++ и Java как собак не резанных), а прикладные задачи с помощью этих велосипедов пусть решает кто-нибудь другой ;)