LINUX.ORG.RU

При каких нагрузках не обойтсь без использования неблокирующих подходов (использовать только потоки и процессы ОС)?

 , ,


0

4

Понятно, что я не говорю о точных цифрах. Меня волнует, если проект имеет солидную нагрузку (возьмем, например, «вконтакте», «одноклассники», «твиттер») может ли он существовать (и возможно сейчас существует), только на таких подходах к многозадачности, как потоки и процессы? Естественно, в счет не берется используемые внутри проекта nginx, redis и т.п. подобные инструменты, использующие неблокирующий код. Речь только о самом приложении. Приложение в основном будет I/O bound (будем считать, что речь о веб-приложении типа социальной сети).

В университете проходим тему построения высоконагруженных систем. Хотел бы узнать у знающих людей, при каких нагрузках становиться необходимым переходить на различные неблокирующие технологии (легкие потоки, event loop-ы) и уже нельзя обходится обычными потоками операционной системы? Или же можно обойтись вообще без технологий «легких потоков» для того, чтобы строить достаточно сильно нагруженные проекты?

Я не могу понять, зачем нужен такой код, который пишется на «легких потоках». Разве это не переизобретение велосипеда, когда уже есть потоки операционной системы? Я знаю, что потоки вызывают переключения контекста, что создает накладные расходы. Но эти накладные расходы не выглядят слишком большими (максимум я видел оценку в 30 микросекунд на переключение), к тому же у переключения «легких потоков» тоже должны быть накладные расходы.

P.S. Я в принципе понимаю разницы, как я думаю, между «легкими потоками», event loop-ами и различными неблокирующими технологиями. Возможно, где-то я выражаюсь не совсем корректно. Прошу меня извинить. Надеюсь все-таки понятно о чем я говорю.

Вопрос также задан тут: https://toster.ru/q/361268

С уважением, Дмитрий

Но эти накладные расходы не выглядят слишком большими (максимум я видел оценку в 30 микросекунд на переключение)

Задержки 30мс не слишком большие? Nuff said.

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

Очевидно же, что они гораздо меньше.

Deleted
()
Ответ на: комментарий от Deleted

Задержки 30мс не слишком большие? Nuff said.

Ну если на запрос в базу я трачу, допустим 10 миллисекунд, 30 микросекунд на переключение контекста в этом случае не выглядит большой цифрой. Конечно, я не отрицаю, что, если приложение обращается только в memcached 30 микросекунд могут существенно его замедлить (хотя оно все равно будет супер-быстрым), но не в случае обращений в базу. При этом напоминаю, что 30 микросекунд - пессиместичная оценка, взятая для процессов. Для потоков это все наверняка в разы быстрее.

aetolicus
() автор топика

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

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

Legioner ★★★★★
()
Ответ на: комментарий от aetolicus

Ну если на запрос в базу я трачу, допустим 10 миллисекунд, 30 микросекунд на переключение контекста в этом случае не выглядит большой цифрой. Конечно, я не отрицаю, что, если приложение обращается только в memcached 30 микросекунд могут существенно его замедлить (хотя оно все равно будет супер-быстрым), но не в случае обращений в базу. При этом напоминаю, что 30 микросекунд - пессиместичная оценка, взятая для процессов. Для потоков это все наверняка в разы быстрее.

Процессы и потоки для ОС по сути одинаковые сущности. Легковесные потоки обычно плодятся миллионами и общаются в первую очередь друг с другом с помощью сообщений. Попробуй сделать на потоках простое приложение — каждый поток выбирает себе случайного соседа, посылает ему пинг, ждёт от него понг (и отвечает на пинги других в это время) и так в цикле. И проверь — сколько потоков ты сможешь запустить, сохраняя 99% ответов на пинги в пределах одной миллисекунды, например. А потом сделай ту же задачу на ерланге или голанге.

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

Legioner ★★★★★
()

Переключения может и проблема, но есть еще более серьезная проблема. Минимальный размер стека в JVM в Linux x86_64 - 128 КБ. Даже тысяча таких потоков - уже 128 MB минимум, но это минимальные стеки со всякой пургой, а надо еще считать память переменных и обьектов на которые они ссылаются. А теперь подумай о 10k problem, это уже больше 1ГБ по сути ни за что, только за поддержку стеков. Зеленые потоки, goрутины, лапша из колбеков в стиле JavaScript - все это будет тратить память в основном на полезные вещи, а в поточной модели ты будешь платить за то, что тебе не надо. А в веб-приложении будет висеть в кажтом стеке еще отдельный парсер и все это счастье будет гоняться сборщиком мусора между зонами памяти. В эпоху микросервисов количество памяти начинает играть роль, как с точки зрения уменьшения вот таких глупых расходов на один JVM, ведь будет запущено много JVM. Так и с точки зрения попытки держать размеры контейнеров маленькими чтобы их было удобно паковать в большом количестве в физические машины.

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 2)
Ответ на: комментарий от vertexua

А теперь подумай о 10k problem, это уже больше 1ГБ по сути ни за что, только за поддержку стеков. Зеленые потоки, goрутины, лапша из колбеков в стиле JavaScript - все это будет тратить память в основном на полезные вещи, а в поточной модели ты будешь платить за то, что тебе не надо.

Разумно. Возможно, будь хорошие зеленые потоки на java - это было бы справедливо. Но использовать функциональный подход Erlang-а или писать лапшу callback-ов ради экономии такого дешевого ресурса как память вряд ли логично.

aetolicus
() автор топика
Ответ на: комментарий от aetolicus

Возможно, будь хорошие зеленые потоки на java - это было бы справедливо

Увы их сложно реализовать без поддержки JVM. Насколько мне известно там нету ничего похожего на <ucontext.h>. Многие языки вроде Scala могут запрятать лапшу из колбеков в монады и будет все выглядеть как обычный код, хотя каждая строчка по асинхронному колбеку вызывает следующую.

Но использовать функциональный подход Erlang-а или писать лапшу callback-ов ради экономии такого дешевого ресурса как память вряд ли логично.

Дешевый до первого терабайта.

vertexua ★★★★★
()

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

Не на каких. Зависит от постановки задачи. Возможно тебе как раз нужно блокироваться.

уже нельзя обходится обычными потоками операционной системы? Или же можно обойтись вообще без технологий «легких потоков» для того, чтобы строить достаточно сильно нагруженные проекты?

В линуксе нет потоков. POSIX Threads минимально описаны тут http://man7.org/linux/man-pages/man7/pthreads.7.html Фактически у тебя bss/data/text шарятся между процессами.

Я не могу понять, зачем нужен такой код, который пишется на «легких потоках». Разве это не переизобретение велосипеда, когда уже есть потоки операционной системы? Я знаю, что потоки вызывают переключения контекста, что создает накладные расходы. Но эти накладные расходы не выглядят слишком большими (максимум я видел оценку в 30 микросекунд на переключение), к тому же у переключения «легких потоков» тоже должны быть накладные расходы.

Между тредами и потоками в линуксе особой разницы нет. Переключение зависит от того затронет ли это TLB/L1/L2/L3 кэши, т.к. процессор у тебя не один.

Общая суть такова, что невозможно решить все задачи с помощью event loop. Иногда дешевле купить 10к серверов. Ты еще не забывай что у тебя есть сетевая карта, которая будет вызывать context switch.

xpahos ★★★★★
()
Ответ на: комментарий от vertexua

Зеленые потоки, goрутины, лапша из колбеков в стиле JavaScript - все это будет тратить

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

10k problem, это уже больше 1ГБ по сути ни за что

Пофиг. Докупить еще одну банку с 64GB и поставить балансер. Это тупо дешевле времени программиста.

А в веб-приложении будет висеть в кажтом стеке еще отдельный парсер

Как и в асинхронной модели. Точно так же будет висеть парсер.

В эпоху микросервисов количество памяти начинает играть роль

В эпоху микросервисов с REST-интерфейсом количество нод можно добавлять без каких либо проблем. «Всё свое несу с собой, контекста сессии не существует».

А теперь плюшечки - вся асинхронная х..та встает колом как только возникает непредвиденная ситуация. Или если ты облажался, предположил «вот тут будет быстро» и не стал рожать в этом месте коллбэк и асинхронный вызов - и оказался неправ. Например, у тебя отвалился NFS-том с которого ты раздаешь контент. Как только все треды из твоего пула влезут на этот том, твоя асинхронщина встанет. Модель «один запрос - один поток» свободна от этой проблемы.

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

время на разработку.

Только если язык хреновенький.

Miguel ★★★★★
()
Ответ на: комментарий от no-dashi

не стал рожать в этом месте коллбэк и асинхронный вызов

Обычно так нельзя сделать, в том то и идея что все асинхронно по умолчанию. В таком коде обычно безусловный бан на синхронные операции ввода-вывода.

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

Зависит от того, как это абстрагируется. Явная лапша - да, зеленые потоки - нет, они выглядят как обычные потоки

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

В таком коде обычно безусловный бан на синхронные операции ввода-вывода.

Хорошо, включаем логику - сортировка массива, она - синхронная или нет? Это ведь НЕ операция ввода-вывода, верно? Усложняем задачу - в сортировке используется делегирование операции сравнения компаратору. Еще раз усложняем задачу - компаратор использует куеву тучу тригонометрических функций для расчета (мы сортируем поле из 150 тысяч геопозиций по их удаленности от геопозиции клиента). Результат - твой eventloop'ный код встревает на время сортировки. Будешь реализовывать сортировку как асинхронный вызов?

зеленые потоки - нет, они выглядят как обычные потоки

Green threads нужны чуть менее чем нигде. Они объективно не дают никакого профита - или они будут привязываться к полноценным потокам в пропорции один-ко-многим (и тогда ты получишь лютый трешак в диспетчеризации и при сколь-нибудь существенной нагрузке у тебя также будут переть контекст свитчи), либо они просто вообще не позволят тебе использовать ресурсы многопроцессорной системы изнутри приложения. Ну или как вариант ты можешь отдаться на откуп рантайму, в надежде что он сделает всё правильно (но этого не будет, да).

no-dashi ★★★★★
()

При каких нагрузках? При таких где много IO и мало всего остального: раздача файлов из фс, прокси серверы. Нужно понимать что 95% веба это прокси между браузером и бд.

redixin ★★★★
()
Ответ на: комментарий от no-dashi

Будешь реализовывать сортировку как асинхронный вызов?

Тут имеет смысл вставить вызов yeild() в функцию сортировки (когда обнаружится ее медленная работа, разумеется). А вообще, в этой ситуации нужно для начала определиться, что тебе важнее: задержки (latency) или общая производительность (throughput). Если второе - то event loop подождет :-)

chimera19
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.