Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

Я хочу изменить ответ Тюдоров, который является хорошей отправной точкой. Существует два основных потока потоков:

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 3

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

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

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

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

Ответ 6

Я думаю, что две вещи находятся на разных уровнях.

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

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

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

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