Когда вам понадобятся «сотни тысяч» тем?

31

Erlang, Go и Rust так или иначе заявляют, что поддерживают параллельное программирование с дешевыми «потоками» / сопрограммами. В Go Справка гласит:

Целесообразно создавать сотни тысяч подпрограмм в одном и том же адресном пространстве.

Rust Учебник говорит:

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

Документация Эрланга гласит:

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

Мой вопрос: какое приложение требует так много параллельных потоков выполнения? Только самые загруженные из веб-серверов получают даже тысячи одновременных посетителей. Приложения типа Boss-worker / job-dispatching, которые я написал, уменьшают отдачу, когда число потоков / процессов намного больше, чем количество физических ядер. Я полагаю, что это может иметь смысл для числовых приложений, но в действительности большинство людей делегируют параллелизм сторонним библиотекам, написанным на Fortran / C / C ++, а не этим языкам нового поколения.

user39019
источник
5
Я думаю, что источник вашей путаницы заключается в следующем: эти микропотоки / задачи / и т. Д. В первую очередь не предназначены для замены потоков / процессов ОС, о которых вы говорите, и при этом они не предназначены для разделения легко распараллеливаемого большого фрагмента обработки чисел. между несколькими ядрами (как вы правильно заметили, нет смысла иметь 100 тыс. потоков на 4 ядрах для этой цели).
us2012
1
Тогда для чего они предназначены? Может быть, я наивный, но я никогда не сталкивался с ситуацией, когда введение сопрограмм и т. Д. Упростило бы программу с одним потоком выполнения. И я смог достичь «низкого» уровня параллелизма с процессами, которые в Linux я могу запускать сотни или тысячи, не потревожив.
user39019
Было бы бессмысленно иметь на самом деле так много задач. Это не значит, что у вас не может быть большого количества задач, которые в основном были просто заблокированы в ожидании чего-либо.
Лорен Печтел
5
Идея асинхронности на основе задач по сравнению с асинхронностью на основе потоков заключается в том, что пользовательский код должен концентрироваться на задачах, которые должны выполняться, а не на управлении работниками , выполняющими эти задачи. Думайте о нити как о работнике, которого вы нанимаете; найм работника стоит дорого, и если вы это сделаете, вы хотите, чтобы он усердно работал над как можно большим количеством задач в 100% случаев. Множество систем можно охарактеризовать как имеющие сотни или тысячи ожидающих выполнения задач, но вам не нужны сотни или тысячи рабочих.
Эрик Липперт
Продолжая комментарий @ EricLippert, есть несколько ситуаций, когда существуют сотни тысяч задач. Пример # 1: декомпозиция задачи, параллельной данным, такой как обработка изображения. Пример №2: сервер, поддерживающий сотни тысяч клиентов, каждый из которых может в любой момент выдать команду. Каждой задаче потребовался бы собственный «облегченный контекст выполнения» - способность запоминать, в каком состоянии она находится (протоколы связи), какая команда выполняется в данный момент и что-то еще. Легкий вес возможен при условии, что у каждого есть мелкий стек вызовов.
Rwong

Ответы:

19

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

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

kr1
источник
15

Это могло бы помочь подумать о том, для чего изначально был разработан Erlang, который должен был управлять телекоммуникациями. Такие действия, как маршрутизация, коммутация, сбор / объединение датчиков и т. Д.

Внесите это в мир Интернета - рассмотрите такую ​​систему, как Twitter . Система, вероятно, не будет использовать микропотоки при создании веб-страниц, но она может использовать их для сбора / кэширования / распространения твитов.

Эта статья может помочь в дальнейшем.

Дейв Клаузен
источник
11

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

Рассмотрим эту функцию Erlang, которая поддерживает счетчик:

counter(Value) ->
    receive                               % Sit idle until a message is received
        increment -> counter(Value + 1);  % Restart with incremented value
        decrement -> counter(Value - 1);  % Restart with decremented value
        speak     ->
            io:fwrite("~B~n", [Value]),
            counter(Value);               % Restart with unaltered value
        _         -> counter(Value)       % Anything else?  Do nothing.
    end.

В обычном ОО-языке, таком как C ++ или Java, вы бы достигли этого, имея класс с закрытым членом класса, открытые методы для получения или изменения его состояния и экземпляр объекта для каждого счетчика. Erlang заменяет понятие экземпляра объекта процессом, понятие методов сообщениями и поддержание состояния с помощью хвостовых вызовов, которые перезапускают функцию с любыми значениями, составляющими новое состояние. Скрытое преимущество этой модели - и большинство из Эрланга смыслом существования - это то , что язык автоматически упорядочивает доступ к значению счетчика за счет использования очереди сообщений, что делает параллельный код очень легко осуществить с высокой степенью безопасности ,

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

Blrfl
источник
1
Ваше последнее приложение counter/1должно использовать строчную букву c;) Я пытался это исправить, но StackExchange не любит 1-символьные правки.
d11wtq
4

Мой вопрос: какое приложение требует так много параллельных потоков выполнения?

1) Тот факт, что язык «масштабируется», означает, что у вас будет меньше шансов отказаться от него, когда все станет сложнее в будущем. (Это называется концепцией «Целого продукта».) Именно поэтому многие люди отказываются от Apache для Nginx. Если вы приблизитесь к «жесткому пределу», накладываемому накладными расходами потоков, вы испугаетесь и начнете думать о том, как его преодолеть. Веб-сайты никогда не могут предсказать, какой объем трафика они получат, поэтому разумно тратить немного времени на масштабируемость.

2) Одна процедура на запрос только начало. Есть много причин для использования внутренних программ.

  • Рассмотрим веб-приложение с 100 одновременными запросами, но каждый запрос генерирует 100 внутренних запросов. Очевидным примером является агрегатор поисковой системы. Но практически любое приложение может создавать процедуры для каждой «области» на экране, а затем генерировать их независимо, а не последовательно. Например, каждая страница на Amazon.com состоит из более чем 150 внутренних запросов, собранных специально для вас. Вы не замечаете, потому что они параллельны, а не последовательны, и каждая «область» - это собственный веб-сервис.
  • Рассмотрим любое приложение, где надежность и задержка имеют первостепенное значение. Вы, вероятно, хотите, чтобы каждый входящий запрос запускал несколько внутренних запросов и возвращал те данные, которые были получены первыми .
  • Рассмотрим любое «соединение клиента», выполненное в вашем приложении. Вместо того, чтобы говорить «для каждого элемента, получить данные», вы можете выделить кучу программ. Если у вас есть куча подчиненных БД для запроса, вы будете волшебным образом работать в N раз быстрее. Если вы этого не сделаете, это не будет медленнее.

Возвращает уменьшение попадания, когда число потоков / процессов намного больше, чем количество физических ядер

Производительность - не единственная причина разбить программу на CSP . Это на самом деле может сделать программу проще для понимания, и некоторые проблемы могут быть решены с помощью гораздо меньшего количества кода.

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

Раньше это означало многопотоковое программирование. Но это было сложно и опасно - до сих пор не так много инструментов, чтобы убедиться, что вы не создаете гонки. И как вы препятствуете будущему сопровождающему совершить ошибку? Если вы посмотрите на большие / сложные программы, вы увидите, что они тратят МНОГО ресурсов в этом направлении.

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

BraveNewCurrency
источник
2

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

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

Захари К
источник
1

Удобство. Когда я начал заниматься многопоточным программированием, я занимался симуляцией и разработкой игр для развлечения. Я обнаружил, что было бы очень удобно просто раскрутить поток для каждого отдельного объекта и позволить ему делать свое дело, а не обрабатывать каждый через цикл. Если ваш код не нарушен недетерминированным поведением и у вас нет коллизий, это может упростить кодирование. Теперь, когда у нас есть все возможности, если я вернусь к этому, я легко могу представить себе, что нужно выделить пару тысяч потоков из-за достаточной вычислительной мощности и памяти для обработки такого количества отдельных объектов!

Брайан Кноблаух
источник
1

Простой пример для Erlang, который был разработан для связи: передача сетевых пакетов. Когда вы делаете один http-запрос, вы можете иметь тысячи пакетов TCP / IP. Добавьте к этому, что все подключаются одновременно, и у вас есть свой вариант использования.

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

Флориан Маргейн
источник
-2

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

Максимус Минимус
источник
2
Несколько лет назад я потратил несколько лет на обработку изображений FLIR в реальном времени, обрабатывая изображения 256x256 со скоростью 30 кадров в секунду. Если у вас нет МНОГО АППАРАТНЫХ процессоров и БЕСШОВНОГО способа разделения ваших данных между ними, ПОСЛЕДНЯЯ вещь, которую вы хотите сделать, - это добавить переключение контекста, нехватку памяти и перегрузку кэша к фактическим вычислительным затратам.
Джон Р. Штром
Это зависит от выполняемой работы. Если все, что вы делаете, это передаете работу аппаратному ядру / исполнительному устройству, после чего вы можете фактически забыть об этом (и заметьте, что именно так работают графические процессоры, так что это не гипотетический сценарий), тогда этот подход действительный.
Максимус Минимус