Преимущества и недостатки порта завершения ввода-вывода

Почему многие считают порт завершения ввода-вывода быстрой и удобной моделью?
Каковы преимущества и недостатки порта завершения ввода-вывода?

Я хочу знать некоторые моменты, которые делают IOCP быстрее, чем другие модели.

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


person Benjamin    schedule 12.03.2011    source источник
comment
Для тех, кто все еще заинтересован: Многопоточный асинхронный ввод-вывод и ввод-вывод Порты завершения — Dr Dubb's   -  person Masood Khaari    schedule 26.06.2013


Ответы (3)


Порты завершения ввода-вывода потрясающие. Нет лучшего слова, чтобы описать их. Если что-то в Windows было сделано правильно, так это завершение портов.

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

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

Другими словами, порты завершения — это беспроблемный «опрос событий», а также решение «загружать ЦП настолько, насколько это возможно».

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

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

person Damon    schedule 12.03.2011
comment
Ожидание ввода-вывода в целом выполняется в Windows очень хорошо, будь то IOCP, перекрытие с событиями, перекрытие с подпрограммами завершения или ожидание всего вышеперечисленного одновременно с семафором, завершением дочернего процесса и сообщениями пользовательского интерфейса. - person Ben Voigt; 12.03.2011
comment
Я спросил, почему порты завершения такие быстрые. Пробуждение ордера LIFO — это то, что я хотел. Но других предложений нет. В любом случае +1 для LIFO - person Benjamin; 13.03.2011
comment
Полезно знать, что даже с ReadFile/WriteFile вы можете произвольно расширять структуру OVERLAPPED. Просто вставьте его в более крупную структуру и используйте CONTAINING_RECORD для получения дополнительных данных. - person Paolo Bonzini; 25.09.2012
comment
... до тех пор, пока не прибудет событие (либо то, которое вы отправляете вручную, либо событие от таймера, асинхронного ввода-вывода или чего-то еще). Вы уверены, что можете опросить таймер с помощью IOCP? Ответ на stackoverflow.com/questions/3239080 говорит об обратном. - person Joey Adams; 25.10.2012
comment
@Joey Adams: Я собирался сказать, конечно, и дать ссылку на рабочий пример, который вы можете попробовать сами, но, к сожалению, рабочий пример не работает. Что вдвойне смущает меня, потому что это означает не только то, что приведенное выше утверждение о таймерах неверно, но и то, что я должен объяснить своему боссу, что я встроил дерьмо в программное обеспечение, которое было выпущено 6 месяцев назад, и не заметил, и никто в QA тоже заметили (это должно было гарантировать крайний срок на случай, если никакие события не поступят иначе, должно быть, это сработало случайно во время QA, потому что всегда было достаточно других событий). - person Damon; 26.10.2012
comment
Что касается исправления ошибки, и в случае, если вам нужно объединить таймер и порт завершения, можно обойти это в Vista/7/8, используя функцию обратного вызова с таймером (который, опять же, может вручную отправить событие в порт завершения ). К сожалению, для оповещения требуется GetQueuedCompletionStatusEx, поэтому поддержка 2000/XP отсутствует. - person Damon; 26.10.2012
comment
@Damon: Спасибо за продолжение. Другим вариантом может быть передача тайм-аута GetQueuedCompletionStatus. Если вашему приложению необходимо время ожидания в нескольких местах, вы можете использовать приоритетную очередь для отслеживания истечения срока действия, а рабочий поток снова и снова вызывает GetQueuedCompletionStatus с самым последним временем. Разбудите рабочий поток с помощью PostQueuedCompletionStatus. - person Joey Adams; 02.11.2012
comment
@Damon: Есть ли способ ограничить размер очереди IOCP? У меня есть множество операций чтения, которые я хочу запустить, но я не хочу запускать их все сразу (это требует слишком много памяти) — вместо этого я хочу ограничить их только N элементами в полете за раз. - person user541686; 01.08.2014
comment
@Mehrdad: размер очереди бесконечен (или, по крайней мере, кажется?), я успешно отправил десятки миллионов элементов без блокировки или получения ошибки. Насколько мне известно, нет никакого способа настроить это. С другой стороны, иметь только N предметов в полете - это другое дело, и возможно (даже автоматически). IOCP разрешает пропускать только N (как указано при создании, по умолчанию количество ЦП) потоков, как семафор. Он пропустит еще один, если один из рабочих заблокируется, поэтому иногда у вас может быть запущено N + 1 или около того потоков, но в целом это довольно надежно. - person Damon; 01.08.2014
comment
@Mehrdad: если вы хотите ограничить количество фактических чтений (а не количество рабочих, обрабатывающих их завершение) из-за ограничений памяти, вы можете, тем не менее, отправить задачи, которые инициируют их, на IOCP (оберните OVERLAPPED в структуре с параметрами и указателем на функцию). Это сработает. Я злоупотреблял IOCP для чистой межпоточной связи/синхронизации (что, в принципе, и есть), которая отлично работает, также с фактическим вводом-выводом на одном и том же IOCP в то же время. - person Damon; 01.08.2014
comment
@Damon: Спасибо за ответ. Это интересный трюк (мне нужно подумать над этим, чтобы увидеть, будет ли он по-прежнему высокопроизводительным), но буквально пару минут назад я понял, что могу просто использовать семафор :) он идеально подходит для этого. Рабочие потоки просто вызывают ReleaseSemaphore, а главный поток, который ставит в очередь операции чтения, ждет семафора с WaitForSingleObject. Это ограничивает количество чтений в полете до всего, что я хочу. - person user541686; 01.08.2014
comment
@Mehrdad: Семафор наоборот, если я не правильно понимаю ваше намерение. Мастер освобождает семафор, а рабочий ждет его. Что касается высокой пропускной способности IOCP, я сравнивал ее примерно 5-6 лет назад и обнаружил, что IOCP легко обрабатывает 300 000 событий в секунду. Что, по крайней мере для меня, явно исключало его как ограничивающий фактор с точки зрения скорости (на самом деле, написанная вручную lockfree очередь едва ли работает более чем в 2-3 раза лучше при большой нагрузке, в нечитерском неискусственном бенчмарке , поэтому, увидев, что что-то уже работает, я был вполне доволен использованием для этого IOCP). - person Damon; 01.08.2014
comment
@Damon: Нет, я думаю, вы неправильно поняли, рабочие освобождают семафор, а мастер ждет его. Это гарантирует, что мастер никогда не помещает в очередь более N элементов, что гарантирует, что система выполняет не более N чтений одновременно. - person user541686; 01.08.2014
comment
@Damon: у меня есть еще один вопрос, если вы не возражаете: как только я запустил все чтения, как мне правильно сообщить рабочим потокам, что я закончил и что они должны прекратить извлекать из очереди один раз существующие чтения были обработаны? - person user541686; 02.08.2014
comment
Обычно у вас есть M задач и N+X воркеров, где N — количество ядер, X немного больше (например, от 1 до 3) на случай, если воркер блокируется, M≫N. Вы отправляете (освобождаете) семафор из управляющего потока, поэтому у вас постоянно работает N +/- 1 потоков, пока остаются задачи. IOCP делает это полуавтоматически. Чтобы потоки завершались, когда вся работа выполнена, для IOCP вы можете просто опубликовать сообщение о завершении с некоторым узнаваемым магическим числом (я использую все нули для ключа, длины и перекрытия*). С семафором проще всего проверять глобальный bool каждый раз, когда ожидание возвращается. - person Damon; 02.08.2014
comment
Жаль, что IOCP терпит неудачу для stdin, stdout и анонимных каналов. - person alexchandel; 02.02.2016

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

IOCP позволяет нескольким потокам (рабочим операциям ввода/вывода) обрабатывать ввод/вывод нескольких клиентов «справедливо». Потоки приостанавливаются и не используют циклы ЦП, пока не будет чем заняться.

Также вы можете прочитать некоторую информацию в этой замечательной книге https://rads.stackoverflow.com/amzn/click/com/0321256190

person Sanja Melnichuk    schedule 12.03.2011
comment
OVERLAPPED Ввод-вывод очень хорошо преодолевает один поток на клиента, спасибо. Что IOCP приносит в таблицу, так это (как вы правильно упомянули) распределение нагрузки между несколькими потоками. Для большинства приложений использование OVERLAPPED I/O из одного потока проще и эффективнее. Только серверы приложений большого объема должны учитывать IOCP. - person Ben Voigt; 12.03.2011
comment
@Ben: Честно говоря, я считаю, что программирование IOCP приводит к более понятному стилю программирования, чем при использовании перекрывающегося ввода-вывода. Это особенно верно, если у вас одновременно выполняется несколько операций (например, при копировании файла). - person ReinstateMonica Larry Osterman; 12.03.2011
comment
@Larry: я не знаю, как вы справляетесь с перекрывающимся вводом-выводом, но я использую MsgWaitForMultipleObjectsEx и процедуры завершения. Тот же стиль программирования конечного автомата, что и IOCP, и нет необходимости в синхронизации потоков. - person Ben Voigt; 12.03.2011
comment
@Ben Voigt +1 Полностью согласен с вами, это нужно только для серверов приложений большого объема. - person Sanja Melnichuk; 12.03.2011

Порты завершения ввода-вывода предоставляются операционной системой как асинхронная операция ввода-вывода, что означает, что она выполняется в фоновом режиме (обычно аппаратно). Система не тратит ресурсы (например, потоки) в ожидании завершения ввода-вывода. Когда ввод-вывод завершен, аппаратное обеспечение отправляет прерывание в ОС, которая затем пробуждает соответствующий процесс/поток для обработки результата. НЕПРАВИЛЬНО: IOCP НЕ требует аппаратной поддержки (см. комментарии ниже)

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

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

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

person Stephen Chung    schedule 12.03.2011
comment
Какая аппаратная поддержка? Я не думаю, что механизм IOCP связан с оборудованием. - person Benjamin; 12.03.2011
comment
Асинхронный ввод-вывод никогда не использует пул потоков. Некоторые широко используемые фреймворки используют оболочку пула потоков для вызовов синхронного ввода-вывода, но это не асинхронный ввод-вывод. - person Ben Voigt; 12.03.2011
comment
IOCP не требует аппаратной поддержки. - person Ana Betts; 13.03.2011
comment
@Бенджамин, @Пол Беттс, вы оба правы. +1 к обоим комментариям. Аппаратная поддержка не необходима для IOCP. У меня ошибочно сложилось впечатление, что IOCP используется в основном для ввода-вывода с диска или устройства, что требует поддержки аппаратных прерываний для уведомления о завершении ввода-вывода. - person Stephen Chung; 13.03.2011
comment
Формулировка здесь была плохой, но возражения также вводят в заблуждение. Дело в том, что аппаратное обеспечение может выполнять операции (например, размещение сетевых буферов и т. д.) без участия пользовательского потока. Поток программы должен просыпаться только после того, как аппаратное обеспечение сигнализирует о том, что произошло интересующее событие. - person EricLaw; 22.08.2013
comment
@BenVoigt, что мне не хватает? Конечно, если вы имеете в виду пулы потоков, представленные в Vista, формально вы правы. Но порты завершения ввода-вывода обычно используют что-то похожее на пул потоков, хотя вопрос о том, совместно ли используют пулы потоков Vista общие детали реализации, мне не известен. - person 0xC0000022L; 25.10.2018
comment
@ 0xC0000022L: я имею в виду, что работа IOCP выполняется в потоке, в котором вы хотите использовать порт завершения. Асинхронный ввод-вывод с использованием обратных вызовов запускает APC в потоке, который запустил ввод-вывод (когда он входит в режим ожидания с возможностью предупреждения). Ни один из них не отправляет работу в пул потоков. Но это еще хуже, потому что вы можете подумать, что даже если ввод-вывод не использует автоматически пул потоков, вы можете сами поместить работу ввода-вывода в потоки пула потоков. Не так... когда поток завершается, Ввод-вывод, начатый из этого потока, отменяется - person Ben Voigt; 25.10.2018
comment
Это означает, что если вы попытаетесь объединить асинхронный ввод-вывод и пул потоков, а диспетчер пула потоков решит, что в пуле есть ненужные потоки, ввод-вывод будет отменен. По этой причине потоки пула потоков могут безопасно использовать только синхронный ввод-вывод. (Это то, что описывает ответ, используйте пул потоков и заставьте потоки ждать завершения ввода-вывода... но это классический пример синхронизации) - person Ben Voigt; 25.10.2018
comment
Ясно, значит, вы имели в виду не концепцию объединения потоков, а возможности, предоставляемые начиная с Vista. Понял. - person 0xC0000022L; 25.10.2018
comment
@ 0xC0000022L: Если под словом «предоставлено с Vista» вы подразумеваете, что оно все еще доступно с Vista (но появилось задолго до этого). Но нет, это не ограничивается пулом потоков ОС. Любой пул потоков, который завершает потоки по запросу, нарушит оперативный ввод-вывод. - person Ben Voigt; 25.10.2018