четверг, 7 июля 2011 г.

Client-side Boost.Asio: быть или не быть? ;)

Предлагаю читателям обсудить в комментариях к этому сообщению следующую тему: стоит ли использовать Boost.Asio на стороне клиента. С серверами все понятно - там вменяемой альтернативы (на C++) нет. А вот с клиентами как?

Для "затравки" мое мнение: то, что клиенты до сих пор не используют асинхронный ввод/вывод - "дело времени" и связано с привычкой и ленью.
Да, можно выделить весь синхронный ввод-вывод в отдельный поток (thread). Но (!) как Вы собираетесь передавать данные в этот поток и получать данные из него? Конечно же через очереди. Ага, получаем как раз тот вариант (io_service + io_service::strand), что описан в проекте nmea_client. Т.е. без Asio это будет просто очередной "велосипед".

Я могу ошибаться. Мне очень интересны (контр)аргументы противоположной стороны. Буду рад их услышать и сделать какие-то выводы для себя.

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

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

Марат, здравствуйте.

Мне, к примеру, нечего кроме как asio использовать и там, и там. Как Вы правильно заметили: там вменяемой альтернативы (на C++) нет. Но а на клиенте что использовать если не asio? У меня нет проектов которые бы не использовали boost. Посему, нет смысла тащить в проект что-то еще.
Есть конечно коллеги, которые пишут с использованием Qt. Но им(по их словам), использовать asio несподручно, ибо что-то еще учить нужно. Другие прямо говорят: ну не понимаю я что там и как... как-то все сложно и мудрено... и т.д.

Я не представляю что еще использовать %)
Не BSD-сокеты или win-сокеты+IOCP же...

asio, ИМХО.

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

На днях мне привели доводы как раз против Boost.Asio (по крайней мере, асинхронной его части) в client-side. И надо же... я что-то растерялся. Ну не умею я рисовать код в воздухе и объяснять на пальцах.
Может быть, удастся продолжить дискуссию здесь. Вполне возможно, что я слишком "повернут" в сторону Asio и не замечаю чего-то.

Есть конечно коллеги, которые пишут с использованием Qt. Но им(по их словам), использовать asio несподручно

Это было одним из аргументов. Кстати, как соединять Qt и Boost.Asio я уже написал - ничего там сложного нет.

Еще были доводы: сложность и "code bloat", связанный с Asio. Но это слишком спорно, чтобы повернуть меня в другую сторону.

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

Еще были доводы: сложность и "code bloat", связанный с Asio
Ну а скажите(если вдруг замечали), какого уровня программисты в основном встречаются на Qt форумах? ;)
Все верно - те, кто учил программирование по документации Qt. Но откуда им знать что такое байндер и функциональный объект? Для большинства, слово "шаблон" отбивает всякое желание лезть туда.

Нет, это не стеб. Говорю о сформировавшемся впечатлении.

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

Ну а скажите(если вдруг замечали), какого уровня программисты в основном встречаются на Qt форумах

Мои, кхм, "оппоненты" (понравились, что скрывать) рассуждали весьма разумно. И Boost.Asio они используют, но только в server-side (это вообще их специализация). Просто досадно, что такой простой ответ, который уже давно готовил (nmea_client), вылетел из головы.

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

Другой пример.
Я всегда разделяю приложение на GUI и реализацию потому, что в моем понимании, реализация, обязана работать без "обложки". А Qt - это такой монстр, который не впихнешь куда угодно. И "отвязаться" от него не получится. Именно по этой причине я не использую Qt в реализации. Только STL+boost.

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

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

Вот-вот - таких комментариев мне и надо. Спасибо.

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

Я всегда разделяю приложение на GUI и реализацию потому, что в моем понимании, реализация, обязана работать без "обложки". А Qt - это такой монстр, который не впихнешь куда угодно.
Да, согласен. Но если проект был сразу направлен на кроссплатформенность с GUI(Qt), то глубокое разделение интерфейса от реализации выльется в оверкод.

Если отвязаться от GUI(Qt), то для асинхронного вывода (о чем и стоял вопрос в первую очередь) бесспорно (для меня) asio на первом месте.

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

Если отвязаться от GUI(Qt)

В том то и дело, что я бы не стал рисковать (в т.ч. и будущим code reuse - а он точно будет) и завязываться на Qt - Вы посмотрите, что они делают с QtSvg в Qt5 (какая была жаркая дискуссия, пока ее не закрыли насильно). Не к месту будет сказано, но я был бы только рад, если подразделение Qt Software перешло в Intel.

WxWidgets? Они, насколько я помню, одно время и сами смотрели в сторону Asio.

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

выльется в оверкод

Сомнительно. Уж больно это понятие относительное. Написал бы кто ввод/вывод на Qt в отдельном потоке, чтобы сравнить (?). Например, переписал бы nmea_client.

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

Т.е. "оверкод" конечно будет. Но я предполагаю, что он будет в приемлемых рамках, которые с лихвой покрываются всеми плюсами подхода с разделением GUI (Qt) + network logic (Boost, Boost.Asio).

Анонимный комментирует...

Вам не кажется, что код на boost::asio получается очень переусложнённым? (понимаю, что постепенно его писать легко - но как потом читать?) Например тот же пример nmea-client - его же нужно хорошо поковырять, чтобы собрать общую картину что там происходит и какая логика там заложена. А это очень плохо для больших проектов - там код либо читается сразу - либо является тревожным звоночком. Я уже видел несколько таких проектов, погибших под тяжестью кода. А сложность nmea-client, чтобы показать лишь чтение из порта - уже говорит не в пользу этой библиотеки.

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

Вам не кажется, что код на boost::asio получается очень переусложнённым?

Очень даже кажется.

Например тот же пример nmea-client

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

А это очень плохо для больших проектов - там код либо читается сразу - либо является тревожным звоночком.

Полностью с Вами согласен. Вот несколько причин сложности nmea-client:

1) Общая "многословность", обусловленная желанием написать код, читаемый как текст (и нежеланием долго думать над названиями).

2) Обобщенность - произвольные функторы вместо тех же boost::function или boost::future/boost::promise.

3) Форсированное использование (и поддержка) Asio custom memory allocation.

4) Дополнительная поддержка move semantic (мне и самому не нравятся "навесы" из #if-#endif).

5) task-based параллелизм (активный объект не владеет выделенным потоком, а использует существующий пул).

Пункты 1-4 вполне решабельны. Особенно в свете недавно принятого стандарта - многое из пространства имен boost переместится в std, да и лямбды могуть помочь с "липосакцией".

А вот пункт 5 вызывает сомнения - нужен ли task-based параллелизм в client-side (где проходит грань целесообразности?). В общем случае task-based параллелизм сложнее варианта "каждому страждующему по отдельному потоку".

Помнится, Джон Кармак на GDC говорил, что они отказались от task-based варианта в пользу потоков в виду высоких накладных расходов и неэффективности (могу ошибаться - чуть позже поищу этот текст и дам здесь ссылку).

С другой стороны, Intel активно продвигает именно task-based "движки" (см. ISN Blogs) и инструментарии (TBB).

Мое мнение таково: task-based параллелизм пока довольно сложен и не дает достаточно преимуществ, чтобы быть однозначным лидером. Но только пока. Предполагаю, что с ростом кол-ва ядер и развитием инструментариев будет наблюдаться переход большей части прикладного параллелизма на task-based.

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

Вам не кажется, что код на boost::asio получается очень переусложнённым?

Перечитал Ваш комментарий и увидел его под другим углом.

Asio samples - это лишь "весьма замороченный" вариант использования Asio. Один из множества вариантов (см. документацию Boost.Asio).

Сама библиотека (не framework!) наоборот максимально упрощает программирование асинхронного ввода-вывода. Иногда упрощает почти до уровня C# Async CTP: "A potted guide to stackless coroutines".

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

Вам не кажется, что код на boost::asio получается очень переусложнённым?
шаблоны в с++ вам тоже кажутся сложными? - тогда проходите мимо ;)

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

Мое мнение таково: task-based параллелизм пока довольно сложен и не дает достаточно преимуществ, чтобы быть однозначным лидером.
а как на счет того, чтоб использовать для подобных задач boost.phoenix+OpenMP ;)
нет, конечно, найдутся и те, кто скажет что это все сложно.. ответ - пишите на пыхыпы

Timur R. комментирует...

Здравствуйте Марат!

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

Ещё раз БОЛЬШОЕ СПАСИБО Марат!

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

а как на счет того, чтоб использовать для подобных задач boost.phoenix+OpenMP

К сожалению, OpenMP я не знаю. По тому, что встречалось, складывается ощущение, что OpenMP отлично подходит для распараллеливания вычислительных задач - task-based/event-driven логику (game engine) на нем не построишь (разве что местами использовать).

Все, что видел, по сути сводится к parallel for. Этого очень мало (например, не хватает continuations). Cilk Plus мне кажется гораздо гибче и интереснее.

От коллег слышал, что OpenMP + MPI - это что-то вроде стандарта де-факто для кластерных вычислений.

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

OpenMP отлично подходит для распараллеливания вычислительных задач - task-based/event-driven логику (game engine) на нем не построишь (разве что местами использовать).

не очень понял смысл предложения...



(например, не хватает continuations)

это что?



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

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

не очень понял смысл предложения...

Я как-то плохо выразился. Пожалуй вот это по-лучше - Generic Parallel Algorithms in
Intel Threading Building Blocks
см. Family Tree.

это что?

Asynchronous Fork/Join using Asio.