Как наиболее эффективно обрабатывать большое количество файловых дескрипторов? - PullRequest
7 голосов
/ 27 сентября 2008

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

  1. Создает отдельный поток для обработки ввода / вывода для каждого сокета.
  2. Используйте системный вызов select для мультиплексирования ввода / вывода в один поток.
  3. Используйте системный вызов poll для мультиплексирования ввода / вывода (вместо выбора).
  4. Используйте системные вызовы epoll , чтобы избежать необходимости повторной отправки файлов сокетов через границы пользователя / системы.
  5. Создает несколько потоков ввода-вывода, каждый из которых мультиплексирует относительно небольшой набор общего числа соединений с использованием API опроса.
  6. Согласно # 5, за исключением использования API epoll для создания отдельного объекта epoll для каждого независимого потока ввода-вывода.

На многоядерных процессорах я ожидал бы, что # 5 или # 6 будут иметь лучшую производительность, но у меня нет никаких твердых данных, подтверждающих это. При поиске в Интернете появилась эта страница, описывающая опыт авторов, тестировавших подходы № 2, № 3 и № 4 выше. К сожалению, этой веб-странице, по-видимому, около 7 лет, и в последнее время не найдено никаких явных обновлений.

Итак, мой вопрос: какой из этих подходов люди считают наиболее эффективным и / или есть ли другой подход, который работает лучше, чем любой из перечисленных выше? Будем благодарны за ссылки на реальные графики, технические документы и / или доступные в Интернете рецензии.

Ответы [ 4 ]

3 голосов
/ 30 декабря 2008

Если говорить о моем опыте работы с большими IRC-серверами, мы использовали select () и poll () (потому что epoll () / kqueue () не были доступны). При примерно 700 одновременных клиентах сервер будет использовать 100% ЦП (сервер irc не был многопоточным). Однако, что интересно, сервер все равно будет работать хорошо. При количестве клиентов около 4000 сервер начинает отставать.

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

Переходя к epoll () / kqueue (), аналогичные специализированные машины будут тривиально работать с 10 000 клиентов, а некоторые (по общему мнению, более мощные машины, но все же машины, которые по нынешним стандартам считаются крошечными), содержат 30 000 клиентов без пота.

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

Я бы рекомендовал использовать epoll () / kqueue () вместо select () / poll () практически в любой ситуации. Я не экспериментировал с разделением клиентов между потоками. Честно говоря, я никогда не находил сервис, который требовал бы дополнительной работы по оптимизации для обработки клиентского интерфейса, чтобы оправдать эксперименты с потоками.

2 голосов
/ 22 июня 2011

Последние два года я работал над этой конкретной проблемой (для веб-сервера G-WAN, который поставляется с МНОГИМИ тестами и диаграммами, раскрывающими все это).

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

Если у вас мало обработки (низкая задержка обработки), то использование одного потока будет быстрее, если использовать несколько потоков.

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

Я не смотрел серьезно на код epoll в ядре (пока я сосредоточился только на пользовательском режиме), но я предполагаю, что реализация epoll в ядре ограничена блокировками.

Вот почему использование нескольких нитей быстро ударило о стену.

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

2 голосов
/ 01 октября 2008

По моему опыту, вы получите лучший результат с # 6.

Я также рекомендую вам взглянуть на libevent, чтобы разобраться с некоторыми из этих деталей. По крайней мере, вы сможете увидеть некоторые из их тестов benchmark results.

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

0 голосов
/ 29 сентября 2008

Я широко использую epoll (), и он хорошо работает. Обычно у меня активны тысячи розеток, и я тестирую до 131 072 розеток. И epoll () всегда может с этим справиться.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...