Является ли неблокирующий ввод-вывод действительно быстрее, чем многопоточный блокирующий ввод-вывод?Как? - PullRequest
106 голосов
/ 17 декабря 2011

Я искал в Интернете некоторые технические подробности о блокировке ввода-вывода и неблокирующем вводе-выводе, и я обнаружил, что несколько человек заявили, что неблокирующий ввод-вывод будет быстрее, чем блокировка ввода-вывода.Например, в этом документе .

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

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

  1. Чтобы загрузить содержимое файла, приложение делегирует эту задачу в основанную на событиях структуру ввода-вывода, передавая функцию обратного вызова вместе симя файла
  2. Структура событий делегируется операционной системе, которая программирует контроллер DMA жесткого диска для записи файла непосредственно в память
  3. Структура событий позволяет запускать дополнительный код.
  4. После завершения копирования с диска в память контроллер DMA вызывает прерывание.
  5. Обработчик прерываний операционной системы уведомляет основанную на событиях структуру ввода-вывода о том, что файл полностью загружен в память,Как оно это делает?Использование сигнала ??
  6. Код, который в настоящий момент выполняется в рамках инфраструктуры ввода / вывода событий, завершается.
  7. Основанная на событиях структура ввода / вывода проверяет свою очередь и видит сообщение операционной системы отшаг 5 и выполняет обратный вызов, полученный на шаге 1.

Так ли это работает?Если это не так, как это работает?Это означает, что система событий может работать без необходимости явного касания стека (например, реального планировщика, который должен был бы делать резервную копию стека и копировать стек другого потока в память при переключении потоков)?Сколько времени это на самом деле экономит?Есть что-то еще?

Ответы [ 9 ]

42 голосов
/ 17 декабря 2011

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

Давайте рассмотрим возможные реализации программы сетевого сервера, которыедолжен обрабатывать 1000 клиентов, подключенных параллельно:

  1. Один поток на соединение (может блокировать ввод / вывод, но может быть и неблокирующий ввод / вывод).
    Каждый поток требует ресурсов памяти (также памяти ядра!), Что является недостатком.И каждый дополнительный поток означает больше работы для планировщика.
  2. Один поток для всех соединений.
    Это берет нагрузку из системы, потому что у нас меньше потоков.Но это также не позволяет вам использовать полную производительность вашей машины, потому что вы можете в конечном итоге довести один процессор до 100% и позволить всем остальным процессорам бездействовать.
  3. Несколько потоков, каждый из которых обрабатывает некоторые потоки.соединений.
    Это берет нагрузку от системы, потому что там меньше потоков.И он может использовать все доступные процессоры.В Windows этот подход поддерживается API пула потоков .

Конечно, наличие большего количества потоков само по себе не является проблемой.Как вы могли заметить, я выбрал довольно большое количество соединений / потоков.Я сомневаюсь, что вы увидите разницу между тремя возможными реализациями, если мы говорим только о дюжине потоков (это также то, что Раймонд Чен предлагает в блоге MSDN Есть ли в Windows ограничение в 2000 потоков на процесс?).

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

Описанные шаги 1-7 дают хорошее представление о том, как это работает.В Windows операционная система сообщит вам о завершении асинхронного ввода-вывода (WriteFile со структурой OVERLAPPED) с использованием события или обратного вызова.Функции обратного вызова будут вызываться, например, только когда ваш код вызывает WaitForMultipleObjectsEx с bAlertable, установленным на true.

Еще некоторые чтения в Интернете:

28 голосов
/ 13 мая 2013

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

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

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

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

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

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

4 голосов
/ 01 июня 2015

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

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

Об этом очень легко говорить с помощьюмолчаливые предположения.Например, некоторые быстро заявляют о себе, например: «Асинхронный ввод-вывод будет быстрее, чем многопоточный ввод-вывод».Это требование сомнительно по нескольким причинам.Во-первых, это может быть случай, когда некоторая заданная инфраструктура асинхронного ввода-вывода реализована именно с многопоточностью, и в этом случае они являются одним и тем же, и нет смысла говорить, что одно понятие «быстрее, чем» другое,

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

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

«Асинхронный» не обязательно должен быть параллельным, например, как указано выше: вы «асинхронно» выполняете две задачи с привязкой к ЦП на машине с ровно одним ядром процессора.

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

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

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

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

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

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

Еще один способ выразить это: позволить операционной системе чередовать использование одного ресурса для двух задач не может быть быстрее, чем просто позволить одной задаче использовать ресурс, пока другая ждет, а затем позволить Второе задание закончить поочередно. Кроме того, стоимость планировщика чередования означает, что в любой реальной ситуации это фактически создает замедление. Не имеет значения, происходит ли чередующееся использование ЦП, сетевого ресурса, ресурса памяти, периферийного устройства или любого другого системного ресурса.

4 голосов
/ 04 января 2013

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

2 голосов
/ 20 февраля 2015

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

Мартин

2 голосов
/ 17 декабря 2011

Одной из возможных реализаций неблокирующего ввода-вывода является именно то, что вы сказали, с пулом фоновых потоков, которые блокируют ввод-вывод и уведомляют поток инициатора ввода-вывода через некоторый механизм обратного вызова.Фактически, именно так работает модуль AIO в glibc. Здесь - некоторые расплывчатые подробности о реализации.

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

0 голосов
/ 11 февраля 2019

Позвольте привести контрпример, что асинхронный ввод-вывод не работает.Я пишу прокси, похожий на ниже с использованием boost :: asio.https://github.com/ArashPartow/proxy/blob/master/tcpproxy_server.cpp

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

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

0 голосов
/ 27 января 2019

В Node запускается несколько потоков, но это уровень ниже во время выполнения C ++.

"Так что да, NodeJS является однопоточным, но это полуправда, фактически он управляется событиями и однопоточен с фоновыми работниками. Основной цикл событий однопоточный, но большинство операций ввода-вывода выполняется отдельные потоки, потому что API-интерфейсы ввода-вывода в Node.js по своей конструкции асинхронные / неблокирующие для размещения цикла событий. "

https://codeburst.io/how-node-js-single-thread-mechanism-work-understanding-event-loop-in-nodejs-230f7440b0ea

«Node.js не является блокирующим, что означает, что все функции (обратные вызовы) делегированы в цикл обработки событий и (или могут быть) выполнены разными потоками. Это обрабатывается во время выполнения Node.js»

https://itnext.io/multi-threading-and-multi-process-in-node-js-ffa5bb5cde98

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

0 голосов
/ 17 декабря 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...