Почему потоки ОС считаются дорогими?

Существует множество решений, направленных на реализацию потоков в пользовательском пространстве. Будь то gorang.org goroutines, зеленые потоки python, асинхронность C #, процессы erlang и т. Д. Идея состоит в том, чтобы позволить параллельное программирование даже с одним или ограниченным числом потоков.

Я не понимаю, почему потоки ОС так дороги? На мой взгляд, в любом случае вам нужно сохранить стек задачи (поток ОС или поток пользовательского пространства), который составляет несколько десятков килобайт, и вам нужен планировщик для перемещения между двумя задачами.

ОС предоставляет обе эти функции бесплатно. Почему потоки ОС должны быть дороже, чем «зеленые» потоки? В чем причина предполагаемого снижения производительности, вызванного выделением потока ОС для каждой «задачи»?

 Chi-Lan01 апр. 2012 г., 16:02
@delnan Хорошо, я слышал это. Но я до сих пор не уверен, почему они должны быть дороже. Оба должны сохранить стек и сделать переключение контекста (игнорируйте GIL, есть много примеров, не относящихся к Python).
 user39576001 апр. 2012 г., 16:00
Они не просто считаются дорогими, они есть. Я считаю, что некоторые зеленые нити (у Хаскелла?) Весят всего пару килобайт каждая, то есть в сто раз меньше. Другая проблема: стандартные потоки Python не являются зелеными - у них есть некоторые проблемы с многопоточностью из-за GIL, но они, тем не менее, являются реальными потоками ОС (возможно, вы думаете оgreenlets? Это другая история, и действительно похожая на зеленые нити).

Ответы на вопрос(6)

направленных на реализацию потоков в пользовательском пространстве. Будь то gorang.org goroutines, зеленые потоки python, асинхронность C #, процессы erlang и т. Д. Идея состоит в том, чтобы позволить параллельное программирование даже с одним или ограниченным числом потоков.

Это слой абстракции. Многим людям легче понять эту концепцию и использовать ее более эффективно во многих сценариях. Это также проще для многих машин (при условии хорошей абстракции), так как во многих случаях модель перемещается от ширины к растяжению. С помощью pthreads (в качестве примера) у вас есть все управление. С другими моделями потоков идея состоит в том, чтобы повторно использовать потоки, чтобы процесс создания параллельной задачи был недорогим, и использовать совершенно другую модель потоков. Намного легче переварить эту модель; есть меньше, чтобы учиться и измерять, и результаты в целом хорошие.

Я не понимаю, почему потоки ОС так дороги? На мой взгляд, в любом случае вам нужно сохранить стек задачи (поток ОС или поток пользовательского пространства), который составляет несколько десятков килобайт, и вам нужен планировщик для перемещения между двумя задачами.

Создание потока стоит дорого, а стеку требуется память. Кроме того, если ваш процесс использует много потоков, переключение контекста может снизить производительность. Таким образом, легкие модели потоков стали полезными по ряду причин. Создание потока ОС стало хорошим решением для средних и больших задач, в идеале в небольших количествах. Это ограничительно и требует много времени для обслуживания.

Задаче / пулу потоков / потоку пользовательского пространства не нужно беспокоиться о переключении контекста или создании потока. Он часто «повторно использует ресурс, когда он становится доступным, если он еще не готов, а также определяет количество активных потоков для этой машины».

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

ОС предоставляет обе эти функции бесплатно.

Они доступны, но они не бесплатны. Они сложны и очень важны для хорошей производительности. Когда вы создаете поток ОС, ему дается время «скоро» - все время процесса делится между потоками. Это не частый случай с пользовательскими потоками. Задача часто ставится в очередь, когда ресурс недоступен. Это уменьшает переключение контекста, память и общее количество потоков, которые должны быть созданы. Когда задача завершается, поток получает другой.

Рассмотрим эту аналогию распределения времени:

Предположим, вы в казино. Есть ряд людей, которые хотят карты.У вас есть фиксированное количество дилеров. Дилеров меньше, чем людей, которым нужны карты.Не всегда достаточно карт для каждого человека в любой момент времени.Людям нужны все карты, чтобы завершить игру / раздачу. Они возвращают свои карты дилеру, когда их игра / рука завершена.

Как бы вы попросили дилеров раздавать карты?

В планировщике ОС это будет основано на (потоке) приоритете. Каждому человеку будет даваться одна карта за раз (время процессора), и приоритет будет оцениваться постоянно.

Люди представляют задачу или работу потока. Карты представляют время и ресурсы. Дилеры представляют темы и ресурсы.

Как бы вы справились быстрее, если бы было 2 дилера и 3 человека? а если было 5 дилеров и 500 человек? Как вы могли бы минимизировать нехватку карт для раздачи? С помощью потоков добавление карт и добавление дилеров - это не решение, которое вы можете предложить «по требованию». Добавление процессоров эквивалентно добавлению дилеров. Добавление потоков эквивалентно тому, что дилеры раздают карты одновременно большему количеству людей (увеличивает переключение контекста). Существует ряд стратегий для более быстрого раздачи карт, особенно после того, как вы избавитесь от потребности людей в картах за определенное время. Разве не было бы быстрее подойти к столу и заключить сделку с человеком или людьми, пока их игра не будет завершена, если соотношение дилера и людей будет 1/50? Сравните это с посещением каждой таблицы в зависимости от приоритета и координацией посещения всех дилеров (подход ОС). Это не означает, что ОС глупа - это означает, что создание потока ОС - это инженер, добавляющий больше людей и больше таблиц, потенциально больше, чем разумно могут обработать дилеры. К счастью, ограничения могут быть сняты во многих случаях с помощью других моделей многопоточности и более высоких абстракций.

Почему потоки ОС должны быть дороже, чем «зеленые» потоки? В чем причина предполагаемого снижения производительности, вызванного выделением потока ОС для каждой «задачи»?

Если вы разработали критически важную для производительности низкоуровневую библиотеку потоков (например, для pthreads), вы бы осознали важность повторного использования (и внедрили ее в свою библиотеку в качестве модели, доступной для пользователей). С этой точки зрения важность моделей многопоточности более высокого уровня - это простое и очевидное решение / оптимизация, основанная на использовании в реальном мире, а также идеал, что полоса входа для принятия и эффективного использования многопоточности может быть снижена.

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

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

Это первый важный момент: существуют пулы потоков, так что вы можете перерабатывать потоки, чтобы не тратить время на их запуск, а также не тратить память на их стеки.

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

Одна вещь, которая делает «зеленые» потоки быстрее потоков ядра, это то, что они являются объектами пользовательского пространства, управляемыми виртуальной машиной. Запуск их - это вызов пространства пользователя, а запуск потока - вызов пространства ядра, который намного медленнее.

 Chi-Lan01 апр. 2012 г., 16:13
@ Тюдор, Хммм .. Я понимаю это, но ты должен объяснить почему. Что нужно сохранить потоку ОС, чего нет в обычном потоке. Оба должны хранить стек пользовательского потока, оба должны планировать их. Чем нить ОС стоит дорого?
 Tudor01 апр. 2012 г., 16:14
Почему отрицательные голоса?
 L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳01 апр. 2012 г., 16:09
@ Chi-Lan: "зеленые" / "легкие" темы сделаны, чтобы избежать этой проблемы. Примеры этого есть в Haskell, Erlang и Python.
 Tudor01 апр. 2012 г., 16:01
@ Chi-Lan: «зеленая» нить может быть не реальной, а абстракцией нити. Несколько «зеленых» потоков могут быть разумно спланированы в одном и том же потоке ядра для эффективного использования, например, с использованием волокон Windows для совместного планирования.
 Chi-Lan01 апр. 2012 г., 16:00
Я не понимаю, почему у него больше накладных расходов. Чем он отличается от "зеленых" тем. Вы должны сохранить их стек, так что вы тратите столько же памяти.

Человек в Google показывает интересный подход.

По его словам, переключение режимов ядра само по себе не является узким местом, а затраты на ядро происходят на SMP-планировщике. И он утверждает, что расписание M: N с помощью ядра не будет дорогим, и это заставляет меня ожидать, что общие потоки M: N будут доступны на всех языках.

Thread или жеProcess это экземпляр программы, которая выполняется В процессе / потоке гораздо больше вещей. Стек выполнения, открытие файлов, сигналы, состояние процессоров и многое другое.

Greentlet отличается, это работает в VM. Это поставляет легкую нить. Многие из них предоставляют псевдо-одновременно (обычно в одном или нескольких потоках уровня ОС). И часто они предоставляют метод без блокировки путем передачи данных вместо обмена данными.

Итак, две вещи фокусируются по-разному, поэтому вес различен.

И, на мой взгляд, гринлет должен быть закончен в ВМ, а не в ОС.

 Chi-Lan01 апр. 2012 г., 16:43
Гринлет возможен без вм, см. golang.org
Решение Вопроса

который является хорошей отправной точкой. Существуют две основные накладные расходы потоков:

Запуск и остановка их. Включает создание стека и объектов ядра. Включает в себя переходы ядра и глобальные блокировки ядра.Хранить их стека вокруг.

(1) проблема только в том случае, если вы все время создаете и останавливаете их. Обычно это решается с помощью пулов потоков. Я считаю эту проблему практически решенной. Планирование задачи в пуле потоков обычно не требует обращения к ядру, что делает его очень быстрым. Накладные расходы составляют порядка нескольких операций с блокированной памятью и нескольких выделений.

(2) Это становится важным, только если у вас естьмного темы (> 100 или около того). В этом случае асинхронный ввод-вывод является средством избавления от потоков. Я обнаружил, что если у вас нет безумного количества потоков, синхронный ввод-вывод, включая блокировку, немного быстрее, чем асинхронный ввод-вывод (вы правильно поняли: синхронизация ввода-вывода происходит быстрее).

 Chi-Lan01 апр. 2012 г., 16:36
(1) Я не уверен, почему объекты ядра стоят дороже, чем объекты пользовательского пространства, в любом случае вам нужны блокировки, и все блокировки сводятся к OS = kernle lock. Я не понимаю (2) вам все равно нужно сохранить их стек.
 usr01 апр. 2012 г., 16:39
Не все альтернативы потока поддерживают стек, например, в случае, когда будущее / задача еще не начала выполняться. Кроме того, стеки потоков ОС могут быть более тяжелыми. Стек .NET всегда выделяет 1 МБ памяти (что очень печально).
 usr01 апр. 2012 г., 16:41
Что касается (1): блокировки не сводятся к блокировкам ядра. Многие оптимизации возможны для несвязанных и / или кратковременных блокировок. Объекты ядра имеют больше накладных расходов по многим причинам (например, они могут совместно использоваться процессами, могут иметь ACL, ...). Они также требуют перехода в режим ядра.
 Chi-Lan01 апр. 2012 г., 16:41
ОК, я рассмотрел 1, спасибо, и я предполагаю, что ответ для (2) «некоторые системы потоков реализованы с дефектом, поэтому им нужна новая модель для исправления проблемы стека, которую они вызвали ;-)».
 Matt Joiner18 июн. 2012 г., 09:01
Отличный ответ, вы пригвоздили основные проблемы, связанные с параллелизмом.

независимо от его размера - указатель стека необходимо сохранить в блоке Thread Info в ядре (поэтому обычно сохраняется большинство регистров, так как они будут вытеснены любым программным / жестким прерыванием вызвал ввод ОС).

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

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

«Зеленые нити» или «волокна» (обычно) планируются из кода пользователя. Изменение контекста намного проще и дешевле, чем прерывание ОС и т. Д., Поскольку при каждом изменении контекста не требуется цикл Вагнера по кольцу, контекст процесса не изменяется, а поток ОС, выполняющий зеленую группу потоков, не изменяется.

Так как ничего не существует, существуют проблемы с зелеными нитями. Они управляются «настоящими» потоками ОС. Это означает, что если один «зеленый» поток в группе, запущенной одним потоком ОС, выполняет вызов ОС, который блокирует, все зеленые потоки в группе блокируются. Это означает, что простые вызовы, такие как sleep (), должны быть «эмулированы» конечным автоматом, который уступает другим зеленым потокам (да, точно так же, как повторная реализация ОС). Точно так же любая межпотоковая сигнализация.

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

Ваш ответ на вопрос