В табл. 59.9 представлены результаты мониторинга набора из
Перед тестированием мы изменили значение константы FD_SETSIZE в заголовочных файлах glibc на 16 384, чтобы позволить программе отслеживать большое количество файловых дескрипторов, задействуя вызов select().
Таблица 59.9. Время, которое уходит на 100 000 операций мониторинга с помощью poll(), select() и интерфейса epoll
Количество отслеживаемых дескрипторов (N) — Процессорное время poll() (секунды) — Процессорное время select() (секунды) — Процессорное время epoll (секунды)
10 — 0,61 — 0,73 — 0,41
100 — 2,90 — 3,00 — 0,42
1000 — 35,00–35,00 — 0,53
10 000–990,00 — 930,00 — 0,66
В подразделе 59.2.5 мы узнали, что вызовы select() и poll() демонстрируют низкую производительность при наблюдении за большим количеством файловых дескрипторов. Теперь посмотрим, почему интерфейс epoll не имеет такой проблемы.
• При каждом вызове select() или poll() ядро вынуждено проверять все указанные файловые дескрипторы. Для сравнения: интерфейс epoll позволяет пометить интересующий нас дескриптор с помощью вызова epoll_ctl(). Ядро записывает эту информацию в список, связанный с исходным описанием открытого файла, и затем при каждой операции ввода/вывода, которая делает файловый дескриптор готовым, добавляет элемент в соответствующий список готовности epoll. (Событие ввода/вывода, связанное с одним описанием открытого файла, может сделать готовыми сразу несколько файловых дескрипторов.) Последующий вызов epoll_wait() просто извлекает элементы из списка готовности.
• При каждом вызове select() или poll() передает ядру структуру данных со сведениями обо всех файловых дескрипторах, за которыми нужно наблюдать; затем ядро возвращает эту структуру обратно, предварительно записав в нее информацию обо всех готовых дескрипторах. Для сравнения: в интерфейсе epoll предусмотрен вызов epoll_ctl(), создающий структуру данных с перечнем отслеживаемых файловых дескрипторов прямо в
Помимо фактов, приведенных выше, стоит отметить еще несколько моментов. Каждый вызов select() требует предварительной инициализации входящей структуры данных, а каждую структуру, возвращаемую вызовами select() и poll() и состоящую из N элементов, следует проверять на наличие готовых файловых дескрипторов. Однако, если верить результатам тестирования, время, которое уходит на эти операции, является несущественным по сравнению с тем, сколько времени тратится на выполнение системных вызовов для мониторинга N дескрипторов. Операции проверки не учитываются в табл. 59.9.
Грубо говоря, производительность вызовов select() и poll() падает линейно по мере увеличивается значения
Интерфейс epoll линейно масштабируется в зависимости от количества происходящих событий ввода/вывода, что делает его особенно эффективным в сценариях, характерных для серверов, которые одновременно обслуживают множество клиентов: большинство отслеживаемых файловых дескрипторов не проявляют никакой активности, и лишь немногие из них являются готовыми.
59.4.6. Уведомления, срабатывающие по фронту
Уведомления, предоставляемые интерфейсом epoll, изначально срабатывают по уровню. Это значит, что epoll сообщает нам, будет ли заблокирована операция ввода/вывода для заданного файлового дескриптора. Извещения такого вида предоставляются также вызовами poll() и select().