Предостережения выбора / опроса по сравнению с реакторами epoll в Twisted

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

Чтение пары десятков сравнений между epoll и альтернативами показывает, что epoll явно является чемпионом по скорости и масштабируемости, особенно в том, что он масштабируется линейно, что является фантастическим. Тем не менее, как насчет использования процессора и памяти, остается ли epoll чемпионом?


person David    schedule 09.01.2010    source источник


Ответы (2)


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

Хотя одно уточнение. И select, и epoll линейно масштабируются. Однако большая разница в том, что API-интерфейсы, ориентированные на пользовательское пространство, имеют сложности, основанные на разных вещах. Стоимость вызова select примерно соответствует значению переданного ему файлового дескриптора с самым большим номером. Если вы выберете на одном fd, 100, то это примерно в два раза дороже, чем на одном fd, 50. Добавление большего количества fd ниже самого высокого не совсем бесплатно, поэтому на практике это немного сложнее, но это - хорошее первое приближение для большинства реализаций.

Стоимость epoll ближе к количеству файловых дескрипторов, на которых действительно есть события. Если вы отслеживаете 200 файловых дескрипторов, но только 100 из них имеют события, то вы (очень грубо) платите только за эти 100 активных файловых дескрипторов. Именно здесь epoll предлагает одно из своих основных преимуществ перед select. Если у вас есть тысяча клиентов, которые в основном простаивают, то, используя select, вы все равно платите за всю тысячу из них. Однако с epoll это похоже на то, что у вас их всего несколько - вы платите только за те, которые активны в любой момент времени.

Все это означает, что epoll приведет к меньшей загрузке ЦП для большинства рабочих нагрузок. Что касается использования памяти, это немного не так. select действительно удается представить всю необходимую информацию в очень компактном виде (один бит на дескриптор файла). А ограничение FD_SETSIZE (обычно 1024) на количество файловых дескрипторов, которые вы можете использовать с select, означает, что вы никогда не потратите более 128 байт на каждый из трех наборов fd, которые вы можете использовать с select (чтение, запись, исключение). По сравнению с этими 384 байтами, epoll - своего рода свинья. Каждый файловый дескриптор представлен многобайтовой структурой. Однако в абсолютном выражении он по-прежнему не будет использовать много памяти. Вы можете представить огромное количество файловых дескрипторов в несколько десятков килобайт (я думаю, примерно 20 КБ на 1000 файловых дескрипторов). И вы также можете добавить тот факт, что вам нужно потратить все 384 из этих байтов с select, если вы хотите отслеживать только один дескриптор файла, но его значение оказывается 1024, тогда как с epoll вы потратите только 20 байтов. Тем не менее, все эти цифры довольно малы, поэтому особой разницы нет.

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

Случайно я также недавно обнаружил один небольшой недостаток epoll по сравнению с select или poll. Хотя ни один из этих трех API не поддерживает обычные файлы (т. Е. Файлы в файловой системе), select и poll представляют этот недостаток поддержки, сообщая, что такие дескрипторы всегда доступны для чтения и всегда доступны для записи. Это делает их непригодными для любого значимого вида неблокирующего ввода-вывода файловой системы, программа, которая использует select или poll и случайно обнаруживает файловый дескриптор из файловой системы, по крайней мере, продолжит работу (или, если она выйдет из строя, она не будет быть из-за select или poll), хотя, возможно, и не с лучшей производительностью.

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

На практике я видел это только при взаимодействии с stdio. Пользователь может перенаправить stdin или stdout из / в обычный файл. В то время как раньше stdin и stdout были каналом - отлично поддерживаемым epoll - затем он становится обычным файлом, и epoll громко дает сбой, нарушая работу приложения.

person Jean-Paul Calderone    schedule 09.01.2010
comment
Очень хороший ответ. Рассмотрите возможность явного описания поведения poll для полноты? - person quark; 16.06.2010
comment
Мои два цента за поведение при чтении из обычных файлов: я обычно предпочитаю полный отказ падению производительности. Причина в том, что он с большей вероятностью будет обнаружен во время разработки и, таким образом, будет работать должным образом (например, имея альтернативный метод выполнения ввода-вывода для реальных файлов). YMMV, конечно: заметного замедления может не быть, и в этом случае неудача не лучше. Но резкое замедление, которое случается только в особых случаях, может быть очень трудно уловить во время разработки, оставив его как бомбу замедленного действия при фактическом развертывании. - person quark; 16.06.2010
comment
Просто нужно полностью прочитать вашу правку. В некотором смысле я согласен с тем, что, вероятно, неправильно, что epoll не имитирует своих предшественников, но опять же, я могу представить, что разработчик, реализовавший ошибку EPERM, думал, Просто потому, что он всегда был сломан, не делает правильным сломать и мой . И еще один контраргумент: я защитный программист, все, что выше 1 + 1, является подозрительным, и я кодирую таким образом, чтобы допускать изящные сбои. Когда ядро ​​запускает неожиданную ошибку, это нехорошо и неразумно. - person David; 07.11.2010
comment
@ Жан-Поль, не могли бы вы добавить еще несколько пояснений по поводу kqueue? - person Good Person; 15.02.2013
comment
Не говоря уже о производительности, есть ли проблема, связанная с этим (из man select). Ядро Linux не налагает фиксированных ограничений, но реализация glibc делает fd_set типом фиксированного размера с FD_SETSIZE, определенным как 1024, и макросами FD _ * (), работающими в соответствии с до этого предела. Для отслеживания файловых дескрипторов больше 1023 используйте вместо этого poll (2). В CentOS 7 я уже сталкивался с проблемами, когда мой собственный код не выполнял select (), потому что ядро ​​вернуло дескриптор файла ›1023, и в настоящее время я рассматриваю проблему, которая пахнет так, как будто это Twisted, вызывающая ту же проблему. - person Paul D Smith; 06.11.2018
comment
Мааайбе? Я не уверен, что полностью понимаю вопрос. Это правда, что select () не работает с файловыми дескрипторами с высокими номерами. Это может быть одной из причин использовать вместо этого один из других механизмов. Если этого недостаточно, можно задать новый вопрос. - person Jean-Paul Calderone; 06.11.2018

При тестировании в моей компании возникла одна проблема с epoll (), таким образом, единственная стоимость по сравнению с select.

При попытке чтения из сети с тайм-аутом создание epoll_fd (вместо FD_SET) и добавление fd к epoll_fd намного дороже, чем создание FD_SET (который является простым malloc).

Согласно предыдущему ответу, по мере того, как количество FD в процессе становится большим, стоимость select () становится выше, но в нашем тестировании, даже со значениями fd в 10 000, select все равно был победителем. Это случаи, когда поток ожидает только один fd и просто пытается преодолеть тот факт, что сетевое чтение и запись по сети не завершаются по тайм-ауту при использовании модели блокирующего потока. Конечно, модели с блокирующими потоками имеют низкую производительность по сравнению с системами с неблокирующими реакторами, но в некоторых случаях требуется интеграция с конкретной устаревшей базой кода.

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

person Brian Bulkowski    schedule 30.04.2014
comment
Но вы даже не можете использовать select(), если у вас есть значения дескриптора файла в диапазоне 10k + - если вы не перекомпилируете половину вашей системы, чтобы изменить FD_SETSIZE - так что мне интересно, как эта стратегия вообще работала. Для описанного вами сценария я бы, вероятно, посмотрел на poll(), который больше похож на select(), чем на epoll(), но снимает ограничение FD_SETSIZE. - person Jean-Paul Calderone; 30.04.2014
comment
Вы можете использовать select (), если у вас есть значения дескриптора файла в диапазоне 10 КБ, потому что вы можете malloc () использовать FD_SET. Фактически, поскольку FD_SETSIZE - это время компиляции, а фактический предел fd - во время выполнения, ТОЛЬКО безопасное использование FD_SET проверяет номер файлового дескриптора на размер FD_SET и выполняет malloc (или моральный эквивалент), если FD_SET является слишком маленький. Я был шокирован, когда увидел это в производстве вместе с заказчиком. После 20 лет программирования сокетов весь код, который я когда-либо писал, и большинство учебных пособий в сети стали небезопасными. - person Brian Bulkowski; 07.05.2014
comment
Насколько я знаю, это не так ни на каких популярных платформах. FD_SETSIZE - это константа времени компиляции, установленная при компиляции вашей библиотеки C. Если вы определите другое значение при создании приложения, ваше приложение и библиотека C не согласятся, и все пойдет плохо. Если у вас есть ссылки, в которых утверждается, что FD_SETSIZE можно переопределить безопасно, мне было бы интересно их увидеть. - person Jean-Paul Calderone; 08.05.2014