LINUX.ORG.RU

История изменений

Исправление vertexua, (текущая версия) :

В треде выше уже были хорошие ответы. Попробую еще добавить кое-что. Например web/rpc серверы часто обрабатывают много IO запросов, в которых время которых мало тяжелой CPU-intensive работы, но зато одновременно происходит выполнение множества одновременных процессов для разных подключений. Процессов в широком смысле слова, а не процессов UNIX. Примером работы таких процессов есть много ожидания событий сети и парсинг форматов протоколов. Если говорить в числах, то например сервер мог бы обслуживать 10000 подключений и по сути только парсить сообщения и отправлять RPC дальше, больше ничего не делая. В таком случае количество состояния в каждом из таких «процессов» может быть например пару килобайт.

Можно использовать по потоку на процесс. Но системные потоки имеют большой системный стек и относительно высокую задержку планировщика когда процессов много.

Можно использовать пул потоков, но тогда сложно обойтись без множества блокировок для его реализации.

Можно распределять подключения между ядрами процессора на уровне дескрипторов входных сокетов, а после распределения обрабатывать каждый набор подключений одним потоком на одном ядре. В таком случае для каждого такого поток будет создан «селектор», в виде например epoll API для Linux. Это обьект, который в цикле выдает из ядра ОС сразу наборы сообщений напрмер о поступающих данных. Тогда в таком же цикле можно их обработать обычно без единой блокировки. Для удобства тут используют несколько техник, одна из них - зеленые потоки. Создается набор " контекстов". Контекст - стек (обычно меньше стандартного системного) и ссылка на функцию и ее аргументы. Потом они все «запускаются» в том смысле что каждой из нтх в цикле могут дать поработать пока они не запросят больше данных и тогда дают поработать следующей. Так они полностью в юзерспейсе переключаются путем сохранения и загрузки регистров процессора. Как только больше данных поступает из селектора - пробуждается зеленый поток и ему дают «поработать» до следующей зеленой блокировки - того момента как он запросит больше данных

Плюсы подхода - код зеленого потока выглядит как обычная функция с синхронным блокировками, стеки зеленых потоков минимальны, каждое ядро практически не блокируется под нагрузкой, используются все ядра из-за распределения дескрипторов подключений, каждый зеленый поток остается в том же самом системном потоке и часто ядре, улучшая утилизацию кешей. Кеши так же могут лучше работать из-за малых стеков

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

Исправление vertexua, :

В треде выше уже были хорошие ответы. Попробую еще добавить кое-что. Например web/rpc серверы часто обрабатывают много IO запросов, в которых время которых мало тяжелой CPU-intensive работы, но зато одновременно происходит выполнение множества одновременных процессов для разных подключений. Процессов в широком смысле слова, а не процессов UNIX. Примером работы таких процессов есть много ожидания событий сети и парсинг форматов протоколов. Если говорить в числах, то например сервер мог бы обслуживать 10000 подключений и по сути только парсить сообщения и отправлять RPC дальше, больше ничего не делая. В таком случае количество состояния в каждом из таких «процессов» может быть например пару килобайт.

Можно использовать по потоку на процесс. Но системные потоки имеют большой системный стек и относительно высокую задержку планировщика когда процессов много.

Можно использовать пул потоков, но тогда сложно обойтись без множества блокировок для его реализации.

Можно распределять подключения между ядрами процессора на уровне дескрипторов входных сокетов, а после распределения обрабатывать каждый набор подключений одним потоком на одном ядре. В таком случае для каждого такого поток будет создан «селектор», в виде например epoll API для Linux. Это обьект, который в цикле выдает из ядра ОС сразу наборы сообщений напрмер о поступающих данных. Тогда в таком же цикле можно их обработать обычно без единой блокировки. Для удобства тут используют несколько техник, одна из них - зеленые потоки. Создается набор " контекстов". Контекст - стек (обычно меньше стандартного системного) и ссылка на функцию и ее аргументы. Потом они все «запускаются» в том смысле что каждой из нтх в цикле могут дать поработать пока они не запросят больше данных и тогда дают поработать следующей. Так они полностью в юзерспейсе переключаются путем сохранения и загрузки регистров процессора. Как только больше данных поступает из селектора - пробуждается зеленый поток и ему дают «поработать» до следующей зеленой блокировки - того момента как он запросит больше данных

Плюсы подхода - код зеленого потока выглядит как обычная функция с синхронным блокировками, стеки зеленых потоков минимальны, каждое ядро практически не блокируется под нагрузкой, используются все ядра из-за распределения дескрипторов подключений, каждый зеленый поток остается в том же самом системном потоке и часто ядре, улучшая утилизацию кешей. Кеши так же могут лучше работать из-за малых стеков

Исправление vertexua, :

В треде выше уже были хорошие ответы. Попробую еще добавить кое-что. Например web/rpc серверы часто обрабатывают много IO запросов, в которых время которых мало тяжелой CPU-intensive работы, но зато одновременно происходит выполнение множества одновременных процессов для разных подключений. Процессов в широком смысле слова, а не процессов UNIX. Примером работы таких процессов есть много ожидания событий сети и парсинг форматов протоколов. Если говорить в числах, то например сервер мог бы обслуживать 10000 подключений и по сути только парсить сообщения и отправлять RPC дальше, больше ничего не делая. В таком случае количество состояния в каждом из таких «процессов» может быть например пару килобайт.

Можно использовать по потоку на процесс. Но системные потоки имеют большой системный стек и относительно высокую задержку планировщика когда процессов много.

Можно использовать пул потоков, но тогда сложно обойтись без множества блокировок для его реализации.

Можно распределять подключения между ядрами процессора на уровне дескрипторов входных сокетов, а после распределения обрабатывать каждый набор подключений одним потоком на одном ядре. В таком случае для каждого такого поток будет создан «селектор», в виде например epoll API для Linux. Это обьект, который в цикле выдает из ядра ОС сразу наборы сообщений напрмер о поступающих данных. Тогда в таком же цикле можно их обработать обычно без единой блокировки. Для удобства тут используют несколько техник, одна из них - зеленые потоки. Создается набор " контекстов". Контекст - стек (обычно меньше стандартного системного) и ссылка на функцию и ее аргументы. Потом они все «запускаются» в том смысле что каждой из нтх в цикле могут дать поработать пока они не запросят больше данных и тогда дают поработать следующей. Так они полностью в юзерспейсе переключаются путем сохранения и загрузки регистров процессора. Как только больше данных поступает из селектора - пробуждается зеленый поток и ему дают «поработать» до следующей зеленой блокировки - того момента как он запросит больше данных

Плюсы подхода - код зеленого потока выглядит как обычная функция с синхронным блокировками, стеки зеленых потоков минимальны, каждое ядро практически не блокируется под нагрузкой, используются все ядра из-за распределения дескрипторов подключений

Исходная версия vertexua, :

В треде выше уже были хорошие ответы. Попробую еще добавить кое-что. Например web/rpc серверы часто обрабатывают много IO запросов, в которых время которых мало тяжелой CPU-intensive работы, но зато одновременно происходит выполнение множества одновременных процессов для разных подключений. Процессов в широком смысле слова, а не процессов UNIX. Примером работы таких процессов есть много ожидания событий сети и парсинг форматов протоколов. Если говорить в числах, то например сервер мог бы обслуживать 10000 подключений и по сути только парсить сообщения и отправлять RPC дальше, больше ничего не делая. В таком случае количество состояния в каждом из таких «процессов» может быть например пару килобайт.

Можно использовать по потоку на процесс. Но системные потоки имеют большой системный стек и относительно высокую задержку планировщика когда процессов много.

Можно использовать пул потоков, но тогда сложно обойтись без множества блокировок для его реализации.

Можно распределять подключения между ядрами процессора на уровне дескрипторов входных сокетов, а после распределения обрабатывать каждый набор подключений одним потоком на одном ядре. В таком случае для каждого такого поток будет создан «селектор», в виде например epoll API для Linux. Это обьект, который в цикле выдает из ядра ОС сразу наборы сообщений напрмер о поступающих данных. Тогда в таком же цикле можно их обработать обычно без единой блокировки. Для удобства тут используют несколько техник, одна из них - зеленые потоки. Создается набор " контекстов". Контекст - стек (обычно меньше стандартного системного) и ссылка на функцию и ее аргументы. Потом они все «запускаются» в том смысле что каждой из нтх в цикле могут дать поработать пока они не запросят больше данных и тогда дают поработать следующей. Так они полностью в юзерспейсе переключаются путем сохранения и загрузки регистров процессора. Как только больше данных поступает из селектора - пробуждается зеленый поток и ему дают «поработать» до следующей зеленой блокировки - того момента как он запросит больше данных