Юзал boost::asio с ее интеграцией к boost::coroutines. Теперь ярый фанат, т.к. штука решает целые пласты проблем разом. Из минусов - старт корутины довольно тяжел и потом контекст многовато памяти ест, но тут тоже есть обходные пути
Старт тяжел изза того что нужно создать контекст, выделить стэк итд. Тут есть сравнение http://www.boost.org/.../doc/html/coroutine/performance.html Если кратко то свитч корутины в 1000 раз быстрее, чем старт новой. По памяти у меня вышло корутины boost::asio едят по 32кб каждая. В принципе оба момента можно легко обойти написав пул корутин, куда потом постить джобы.
Самая большая проблема в том, что без поддержки рантайма нет автоматического управления памятью. Приходится либо выделать среднюю по больнице для всех короутин, либо ресайзить вручную в процессе работы. Получается, что для обслуживания 100к подключений уходит 100-250Mb (в зависимости от нужд стека).Базово можно взять boost::context - очень правильная и минималистичная реализация (я там немного помогал автору). boost::coroutine сделан поверх тем же человеком, но мне не подошел интерфейс, я делал свой.
Iurii Krasnoshchok чувак, ты чутка путаешь виртуальную память с физической. Допустим у тебя стек на 100мб и 100к корутин, тогда виртуальной памяти ты запользуешь в районе 10Тб, что просто тьфу для x64. И допустим ты так крутанско код оформил (со штуками типа zero-copy, data replication, и тд), что тебе для обслуживания клиента только 60 байт на стеке надо, тогда суммарный юзаж физической памяти у тебя будет 6Mb
Evgeny Zajcev Еще раз: память мы выделяем явно - malloc / whatever. На Linux это будет виртуальная память до первого page fault (на котором мы получим страницу), на Windows это будет виртуальная память, под которую отмаплена физическая.
Вторая проблема - это кривые либы. Если либа внутри хватает мьютекс на каком-то call и держит до другого call, то можно словить deadlock на попытке взять тот же мьютекс.
Такого рода проблемы были актуальны лет десять назад, когда N тредов с точки зрения прикладного программиста на самом деле представляли собой M потоков исполнения операционной системы и этим нельзя была управлять.В случае корутин это _не_ проблема, это абсолютно нормальный ход вещей, который нужно понимать.
Iurii Krasnoshchok а в чём проблема больших стеков для корутин? Это же виртуальная память - просто адреса. Физическая память будет расходоваться ровно столько, насколько глубоко зайдёшь в стек
Evgeny Zajcev Это утверждение не работает в Windows, где нужно работать только с явно отмапленной (commited) памятью. Конкретно в моем случае короутины выходили довольно прожорливые (~16к на стек), потому я переписал все на lambda, код тоже простой, но памяти расходуется намного меньше.Основной поинт в go - это не goroutines, а channels - готовые примитивы для написания кода в стиле CSP (или Actor) model.
Evgeny Zajcev Вот здесь появляется следующая проблема: если есть channel, и мы гоняем сложную структуру, то кто ей владеет, и кто должен удалять, как не поймать race, когда структура бегает по указателю? И как не фрагментировать память на аллокации таких структур.Я все это проходил. Я больше не хочу ручками, я хочу garbage collector и не включать мозги.У меня все равно 90% обработки запроса проходит в syscall на TCP/IP.
Юзаем давно, под нагрузкой, bare makecontext/swapcontext calls + слой связки с event loop. Впечатления положительные, устранили неопрятный callback hell. Из граблей, что словили - в начале стека нужно обязательно ставить guard page с PROT_NONE, т.к. сторонние библиотеки могут жрать стек непредсказуемо много (jemalloc был у нас).
В яндексе довольно много на них написано включая balancer, раздачу запросов на базовые поиски и spider который интернет качает, яндексная реализация лежит в open source на github.
Кстати, на посмотреть. Есть панковская libcppa. У них есть абсолютно волшебный концепт - atom (слизано с Erlang): мы превращаем lower case alphabet в int64, с помощью 6bit encoding в compile-time. По atom можно делать switch, выходит как pattern matching.https://github.com/actor-framework/actor-framework
Comments