Здравствуйте, Videoman, Вы писали:
N>>Если посмотреть на userver, который делает всё то, что тебе надо, то видно, что он C++17 требует. То есть его достаточно.
V>Мне показалось, что там во всю стекфул корутины используются. Подход понятен, но мне хотелось бы получить что-то в стиле node.js. Один основной поток, в котором логика строго однопоточная, без необходимости синхронизации. Мне показалось, что со стекфул корутинами, если мы используем сразу кучу ядер,
Именно что показалось.
Корутина — это не поток. Это просто возможность прервать выполнение функции и потом продолжить с того же места. Когда прервать и когда продолжить, решать программисту, т.е. тебе.
V>Уточню. У меня задача не запускать паралльно несколько независимых веток исполнения, а делать все по очереди (псевдопараллельно(, как будто в одном потоке, без синхронизации. Если на низком уровне рассматривать, похоже на большой автомат, но который каждый цикл выполняет одну и ту же логику, но она огромная размазана между кучи ожиданий IO, иногда вложенных. Похоже на то, как работает node.js. Ощущение, что безстековые корутины тут лучше подходят или я не прав?
Тут подходят безстековые корутины. И стековые тоже подходят.
Разница между ними в том, что в случае безстековых корутин сохранение контекста либо делается вручную, либо должно поддерживаться языком/средой (в случае 20х корутин это делает компилятор, как правило выделяя память на куче). Для стековых корутин должен выделяться отдельный стек, и запуск/выход из них связан с необходимостью сохранения и восстановления всех регистров, что относительно дорого. Из плюсов стековых корутин можно назвать то, что переключение контекста возможно из любой функции в стеке вызовов, при этом функции выше по стеку вообще могут не знать, что они выполняются в контексте корутины.
Здравствуйте, Videoman, Вы писали:
V>Большая просьба по возможности воздержаться от тыканья в готовые библиотеки, в плане бери вот это, там всё уже есть и не думай. Хочется понять основные идеи и подходы, а не максимально быстро начать писать код в продакшене.
Для ожидания события в сокете можно использовать poll/epoll/select/kqueue (сокет должен быть не блокируемым). Но кросс-платформенный из этого только select.
В кроссплатформенных библиотеках boost::asio, libevent и ещё нескольких именно эти механизмы и используются.
Можно коллбек прикрутить, можно в отдельном потоке ответ ждать, причём можно ждать ответ более, чем на одном сокете.
Здравствуйте, Videoman, Вы писали: V> ... кто как подходит к IO-bound задачам, где CPU, будем считать для простоты, почти не используется и в основном приходится ждать завершение операций.
Я бы просто запускал новый поток и ожидал сообщения от него о завершении обмена данными. Ядер у современных процессоров гораздо больше одного и в наши дни неразумно обрабатывать все задачи сопрограммами на одном ядре процессора вместо многопоточной, многоядерной обработки.
Здравствуйте, Pitirimov, Вы писали:
P>Здравствуйте, Videoman, Вы писали: V>> ... кто как подходит к IO-bound задачам, где CPU, будем считать для простоты, почти не используется и в основном приходится ждать завершение операций.
P>Я бы просто запускал новый поток и ожидал сообщения от него о завершении обмена данными. Ядер у современных процессоров гораздо больше одного и в наши дни неразумно обрабатывать все задачи сопрограммами на одном ядре процессора вместо многопоточной, многоядерной обработки.
Дык если потоков много очень большие накладные расходы по памяти и на переключение, да и кэш бодро вымывается.
Поэтому вместо миллиона тактов на переключения потока использоую сотни тактов на планировщик и вместа 4мб стека на поток мелкие стуктуры и coroutine-ы.
В результате выигрыш по производительности может быть очень ощутимым. Если бы он был не значительным миллионы мух не заморачивались бы.
Здравствуйте, Pitirimov, Вы писали:
P>Здравствуйте, Videoman, Вы писали: V>> ... кто как подходит к IO-bound задачам, где CPU, будем считать для простоты, почти не используется и в основном приходится ждать завершение операций.
P>Я бы просто запускал новый поток и ожидал сообщения от него о завершении обмена данными.
Довелось лет 5 назад понаблюдать за тем, как Linux вставал колом на такой модели thread per connection когда количество одновременно обслуживаемых соединений превышало 32K штук.
Здравствуйте, Pitirimov, Вы писали:
P>Я бы просто запускал новый поток и ожидал сообщения от него о завершении обмена данными. Ядер у современных процессоров гораздо больше одного и в наши дни неразумно обрабатывать все задачи сопрограммами на одном ядре процессора вместо многопоточной, многоядерной обработки.
В моих новых задачах мне такое не подходит. Говорю же CPU-bound. Основному потоку нечего желать, только логику распределять, всё на ожидании висит. Вот только автомат по логике получается безумный. Поэтому хочется всё это выпрямить. Зачем тут стрелять из пушки.
Здравствуйте, landerhigh, Вы писали:
L>Именно что показалось. L>Корутина — это не поток. Это просто возможность прервать выполнение функции и потом продолжить с того же места. Когда прервать и когда продолжить, решать программисту, т.е. тебе.
А я где-то написал что корутина это — поток . Корутина это не поток, но выполняется она не в вакуме, а в контексте потока ОС.
L>Тут подходят безстековые корутины. И стековые тоже подходят. L>Разница между ними в том, что в случае безстековых корутин сохранение контекста либо делается вручную, либо должно поддерживаться языком/средой (в случае 20х корутин это делает компилятор, как правило выделяя память на куче). Для стековых корутин должен выделяться отдельный стек, и запуск/выход из них связан с необходимостью сохранения и восстановления всех регистров, что относительно дорого. Из плюсов стековых корутин можно назвать то, что переключение контекста возможно из любой функции в стеке вызовов, при этом функции выше по стеку вообще могут не знать, что они выполняются в контексте корутины.
Я знаю чем стековые корутины отличаются от безстековых, только вот разница большая, на мой взгляд. Как я понимаю, в случае стековых корутин мне придется всё равно писать в многопоточном стиле, пофигу что физически поток один, нити то разные и стеки тоже. Потом придется как-то контекст руками размазывать по нитям, делать аналог join. Отличий от потоков почти никакой, с той лишь разницей, что мы оптимизируем переключение контекста оптимизирую по CPU, а мне это не нужно, хотелось бы логику выпрямить.
Меня интересуют безстековые корутины или их заменители, когда вся логика в основном потоке. Еще большой плюс, что можно использовать пул потоков, в которых выполнять асинхронные операции изолированно, а результат возвращать основному потоку. Видимо придется использовать 20-й стандарт.
А на 20-м стандарте есть какие-нибудь библиотеки, которые использую корутины оттуда? Была бы идеальна обертка libuv в С++20 корутины, кажется.
Здравствуйте, Videoman, Вы писали:
V>А на 20-м стандарте есть какие-нибудь библиотеки, которые использую корутины оттуда? Была бы идеальна обертка libuv в С++20 корутины, кажется.
Здравствуйте, Videoman, Вы писали:
V>Что в идеале хочется получить: V>
V>data = await read(...);
V>await write(data);
V>
V>Понятно, что скорее всего должен быть некий механизм, который следит за завершенными операциями и продолжает выполнение с точки ожидания. V>Как такое можно организовать проще всего? Обязательно нужно кроссплатформенное решение, без погружения в дебри с ассемблером, регистрами и т.л.
Здравствуйте, sergii.p, Вы писали:
BFE>>std::future, std::async SP>это же создаст сонм потоков-бездельников
Или не создаст.
Зависит от имплементации.
Имплементация может использовать thread pool.
Так как у меня задачи совсем не укладываются в описанный топикастером подход, то на практике я эти функции не использовал, но, теоретически, как мне кажется, связка future + async укладывается в описанную задачу, как я её понял.