Для очень небольшого количества сокетов (разумеется, зависит от вашего оборудования, но мы говорим о чем-то порядка 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
случайно обнаруживает файловый дескриптор из файловой системы, будет, по крайней мере, продолжать работать (или, если это произойдет, это не будет, потому что of select
или poll
), хотя, возможно, и не с лучшей производительностью.
С другой стороны, epoll
он быстро завершится с ошибкой ( EPERM
очевидно), когда его попросят отслеживать такой файловый дескриптор. Строго говоря, это вряд ли неправильно. Это просто недвусмысленно сигнализирует об отсутствии поддержки. Обычно я бы приветствовал явные условия отказа, но это недокументировано (насколько я могу судить) и приводит к полностью неработающему приложению, а не к тому, которое просто работает с потенциально сниженной производительностью.
На практике я видел это только при взаимодействии с stdio. Пользователь может перенаправить stdin или stdout из / в обычный файл. В то время как раньше stdin и stdout были каналом - прекрасно поддерживаемым epoll - затем он становится обычным файлом, и epoll громко дает сбой, нарушая работу приложения.
poll
для полноты?man select
)? Ядро Linux не налагает фиксированного ограничения, но реализация glibc делает fd_set типом фиксированного размера, при этом FD_SETSIZE определен как 1024, а макросы FD _ * () работают в соответствии с этот предел. Для отслеживания файловых дескрипторов больше 1023 используйте вместо этого poll (2). В CentOS 7 я уже сталкивался с проблемами, когда мой собственный код не выполнял select (), потому что ядро вернуло дескриптор файла> 1023, и в настоящее время я рассматриваю проблему, которая пахнет так, как будто она может быть Twisted с той же проблемой.При тестировании в моей компании возникла одна проблема с epoll (), таким образом, единственная стоимость по сравнению с select.
При попытке чтения из сети с тайм-аутом создание epoll_fd (вместо FD_SET) и добавление fd к epoll_fd намного дороже, чем создание FD_SET (который является простым malloc).
Согласно предыдущему ответу, по мере того, как количество FD в процессе становится большим, стоимость select () становится выше, но в нашем тестировании, даже со значениями fd в 10 000, select все еще был победителем. Это случаи, когда поток ожидает только один fd и просто пытается преодолеть тот факт, что сетевое чтение и запись по сети не завершаются по таймауту при использовании модели блокирующего потока. Конечно, модели с блокирующими потоками имеют низкую производительность по сравнению с системами с неблокирующими реакторами, но бывают случаи, когда для интеграции с определенной устаревшей базой кода это необходимо.
Такой вариант использования редко встречается в высокопроизводительных приложениях, потому что модели реактора не нужно каждый раз создавать новый epoll_fd. Для модели, в которой epoll_fd является долгоживущим - что явно предпочтительнее для любой конструкции высокопроизводительного сервера - epoll является явным победителем во всех отношениях.
источник
select()
если у вас есть значения дескриптора файла в диапазоне 10k + - если вы не перекомпилируете половину своей системы, чтобы изменить FD_SETSIZE - поэтому мне интересно, как эта стратегия вообще работала. Для описанного вами сценария я бы, вероятно, посмотрел наpoll()
который больше похож,select()
чем на него есть,epoll()
но снимает ограничение FD_SETSIZE.FD_SETSIZE
- это константа времени компиляции, установленная при компиляции вашей библиотеки C. Если вы определите другое значение при сборке приложения, ваше приложение и библиотека C не согласятся, и все пойдет плохо. Если у вас есть ссылки, в которых утверждается, что переопределение безопасно,FD_SETSIZE
мне было бы интересно их увидеть.