Win32 Overlapped I / O - Завершение подпрограмм или WaitForMultipleObjects? - PullRequest
19 голосов
/ 16 апреля 2009

Мне интересно, какой подход быстрее и почему?

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

Должен ли я использовать подпрограммы завершения или использовать API WaitForMultipleObjects и почему?

Ответы [ 6 ]

34 голосов
/ 16 апреля 2009

Вы предлагаете два способа выполнения перекрывающихся операций ввода-вывода и игнорируете третий (или я неправильно понимаю ваш вопрос).

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

В качестве альтернативы вы можете пройти процедуру завершения, которая вызывается, когда происходит завершение. Это известно как «тревожный ввод-вывод» и требует, чтобы поток, который выполнил вызов WSARecv (), находился в состоянии «оповещения» для вызова процедуры завершения. Потоки могут переводить себя в состояние тревоги несколькими способами (вызывая SleepEx () или различные EX-функции функций Wait и т. Д.). Книга Рихтера , которую я открываю передо мной, гласит: «Я немного поработал с оповещаемым вводом / выводом, и я буду первым, кто скажет вам, что оповещаемый ввод / вывод ужасен и должен избегать". Хватит говорить ИМХО.

Существует третий способ, перед тем как выполнить вызов, вы должны связать дескриптор, на котором вы хотите сделать перекрытый ввод-вывод, с портом завершения. Затем вы создаете пул потоков, которые обслуживают этот порт завершения, вызывая GetQueuedCompletionStatus () и цикл. Вы выдаете свой WSARecv () со структурой OVERLAPPED БЕЗ события в нем, и после завершения ввода-вывода завершение выскакивает из GetQueuedCompletionStatus () в одном из потоков пула ввода-вывода и может обрабатываться там.

Как уже упоминалось ранее, Vista / Server 2008 немного очистил работу IOCP и устранил проблему, благодаря которой вы должны были убедиться, что поток, который выдал перекрывающийся запрос, продолжал работать до тех пор, пока запрос не будет завершен. Ссылку на ссылку можно найти здесь . Но эту проблему легко обойти в любом случае; вы просто перенаправляете WSARecv в один из потоков вашего пула ввода-вывода, используя тот же IOCP, который вы используете для завершения ...

В любом случае, ИМХО использование IOCP - лучший способ сделать перекрывающийся ввод / вывод. Да, чтобы разобраться с перекрывающейся / асинхронной природой вызовов, вначале может потребоваться немного времени, но это того стоит, поскольку система очень хорошо масштабируется и предлагает простой метод «запускай и забывай» для работы с перекрывающимися операциями.

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

в сторону; ИМХО, вам действительно следует прочесть « Windows Via C / C ++ (PRO-Developer) » Джеффри Рихтера и Кристофа Насарре, так как там написано все, что вам нужно знать о перекрывающихся операциях ввода-вывода и большинстве других современных платформ Windows. методы и API.

7 голосов
/ 16 апреля 2009

WaitForMultipleObjects ограничено 64 ручками; в сильно параллельном приложении это может стать ограничением.

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

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

Я бы не ожидал какой-либо существенной разницы в производительности, но, в конце концов, вы можете делать только собственные измерения, отражающие ваше использование. Обратите внимание, что в Vista / Server2008 внесены изменения с портами завершения, поэтому исходящий поток теперь не нужен для выполнения операций ввода-вывода, это может иметь большее значение (см. Эту статью Марка Руссиновича).

5 голосов
/ 06 декабря 2009

Таблица 6-3 в книге Сетевое программирование для Microsoft Windows, 2-е издание сравнивает масштабируемость перекрывающегося ввода-вывода через порты завершения с другими методами. Порты завершения выдувают все другие модели ввода / вывода из воды, когда дело доходит до пропускной способности, используя гораздо меньшее количество потоков.

2 голосов
/ 16 апреля 2009

Разница между WaitForMultipleObjects () и портами завершения ввода / вывода заключается в том, что IOCP масштабируется до тысяч объектов, тогда как WFMO () не используется и не должен использоваться для чего-то более 64 объектов (даже если вы могли бы).

Вы не можете реально сравнить их по производительности, потому что в области <64 объектов они будут практически идентичны. </p>

WFMO (), однако, выполняет циклический перебор для своих объектов, поэтому занятые объекты с низкими индексными номерами могут истощать объекты с высокими индексными номерами. (Например, если объект 0 постоянно уходит, он будет голодать от объектов 1, 2, 3 и т. Д.). Это явно нежелательно.

Я написал библиотеку IOCP (для сокетов) для решения проблемы C10K и поместил ее в общественное достояние. Я смог на 512 Мб W2K-машине получить 4000 сокетов, одновременно передавая данные. (Вы можете получить гораздо больше сокетов, если они простаивают - занятый сокет потребляет больше пула без постраничной передачи, и это максимальное ограничение на количество сокетов, которое вы можете иметь).

http://www.45mercystreet.com/computing/libiocp/index.html

API должен дать вам именно то, что вам нужно.

0 голосов
/ 16 апреля 2009

Любая рутина работает, и я не думаю, что одно значительно быстрее другого.

Эти два подхода существуют для удовлетворения различных моделей программирования. WaitForMultipleObjects предназначен для упрощения асинхронного шаблона завершения (например, функции UNIX select ()), в то время как порты завершения больше соответствуют модели, управляемой событиями.

Лично я считаю, что подход WaitForMultipleObjects () приводит к более чистому коду и большей поточной безопасности.

0 голосов
/ 16 апреля 2009

Не уверен. Но я использую WaitForMultipleObjects и / или WaitFoSingleObjects. Это очень удобно.

...