Сервер Socket Socket - не удается насытить процессор
Я разработал мини-HTTP-сервер на С++, используя boost:: asio, и теперь я загружаю его несколькими клиентами, и мне не удалось приблизиться к насыщению процессора. Я тестирую экземпляр Amazon EC2 и получаю около 50% использования одного процессора, 20% другого, а остальные два неактивны (согласно htop).
Подробнее:
- Сервер запускает один поток на ядро
- Выполняются запросы, обрабатываются, обрабатываются и ответы
- Запросы относятся к данным, которые считываются из памяти (только для чтения для этого теста).
- Я загружаю сервер, используя две машины, каждая из которых запускает Java-приложение, запускает 25 потоков, отправляет запросы
- Я вижу около 230 запросов/сек (это запросы приложений, которые состоят из множества HTTP-запросов)
Итак, на что я должен смотреть, чтобы улучшить этот результат? Учитывая, что процессор в основном простаивает, я бы хотел использовать эту дополнительную емкость для получения более высокой пропускной способности, скажем, 800 запросов/сек или что-то еще.
Идеи, которые у меня были:
- Запросы очень маленькие и часто выполняются за несколько мс, я мог бы модифицировать клиента для отправки/компоновки больших запросов (возможно, с использованием пакетной обработки)
- Я мог бы изменить HTTP-сервер, чтобы использовать шаблон выбора Select, здесь это подходит?
- Я мог бы сделать некоторые профилирования, чтобы попытаться понять, что узкое место /
Ответы
Ответ 1
boost:: asio не такой дружелюбный к потоковому, как вы могли бы надеяться - существует большой замок вокруг epoll-кода в boost/asio/detail/epoll_reactor.hpp, что означает, что только один поток может вызывать в ядро epoll syscall вовремя. И для очень маленьких запросов это имеет значение (это означает, что вы увидите только однопоточную производительность).
Обратите внимание, что это ограничение того, как boost:: asio использует возможности ядра Linux, а не только ядро Linux. Сценарий epoll поддерживает несколько потоков при использовании событий, связанных с краем, но правильное (без чрезмерной блокировки) может быть довольно сложным.
Кстати, я занимался какой-то работой в этой области (объединив полностью-многопотоковый цикл событий epoll с запрошенными пользователем потоками/волокнами) и сделал некоторый код доступным под nginetd.
Ответ 2
Как вы используете EC2, все ставки отключены.
Попробуйте использовать реальное оборудование, и тогда вы сможете увидеть, что происходит. Попытка выполнить тестирование производительности в виртуальных машинах практически невозможна.
Я еще не разработал, для чего EC2 полезен, если кто-то узнает, пожалуйста, дайте мне знать.
Ответ 3
230 запросов/сек кажутся очень низкими для таких простых асинхронных запросов. Таким образом, использование нескольких потоков - вероятно, преждевременная оптимизация - заставить ее работать исправно и настроиться в одном потоке и посмотреть, нужны ли вам все еще. Просто избавление от ненужной блокировки может ускориться.
В этой статье есть некоторые подробности и обсуждения стратегий ввода-вывода для производительности веб-сервера примерно 2003 года. Кто-нибудь получил что-то более новое?
Ответ 4
Из ваших комментариев по использованию сети,
Кажется, у вас мало движения сети.
3 + 2.5 MiB/sec
находится вокруг шарового парка 50Mbps
(по сравнению с вашим портом 1 Гбит/с).
Я бы сказал, что у вас есть одна из следующих двух проблем:
- Недостаточная рабочая нагрузка (низкая скорость запроса от ваших клиентов)
- Блокировка сервера (создание помех отклика)
Глядя на отметки cmeerw
и ваши показатели использования процессора
(холостой ход на 50% + 20% + 0% + 0%
)
скорее всего, это ограничение в реализации вашего сервера.
I второй cmeerw
ответ (+1).
Ответ 5
ASIO отлично подходит для небольших и средних задач, но он не очень хорош в использовании мощности базовой системы. Ни вызовы сокетов, ни даже IOCP в Windows, но если вы опытны, вы всегда будете лучше, чем ASIO. В любом случае, есть много накладных расходов со всеми этими методами, только больше с ASIO.
Для чего это стоит. используя вызовы raw сокета на моем настраиваемом HTTP, может обслуживать 800K динамических запросов в секунду с 4-ядерным I7. Он служит из ОЗУ, где вам нужно быть на таком уровне производительности. На этом уровне производительности сетевой драйвер и ОС потребляют около 40% процессора. Используя ASIO, я могу получить от 50 до 100 тыс. Запросов в секунду, его производительность довольно изменчива и в основном связана в моем приложении. Сообщение @cmeerw в основном объясняет, почему.
Одним из способов повышения производительности является внедрение прокси UDP. Перехватывая HTTP-запросы, а затем маршрутизируя их по UDP на ваш сервер UDP-HTTP, вы можете обойти много накладных расходов TCP в операционных системах. Вы также можете иметь передние концы, которые проходят через UDP самостоятельно, что не должно быть слишком сложным для себя. Преимущество прокси-сервера HTTP-UDP заключается в том, что он позволяет вам использовать любой хороший интерфейс без изменений, и вы можете поменять их по своему усмотрению без какого-либо воздействия. Вам просто нужно еще несколько серверов для его реализации. Эта модификация в моем примере снизила загрузку ОС до 10%, что увеличило мои запросы в секунду до чуть более миллиона на этом одном бэкэнд. И FWIW. У вас всегда должна быть встроенная настройка для любого сайта-исполнителя, поскольку интерфейсы могут кэшировать данные без замедления более важных динамических запросов.
Будущее, похоже, создает собственный драйвер, который реализует собственный сетевой стек, чтобы вы могли как можно ближе подойти к запросам и реализовать свой собственный протокол там. Скорее всего, это не то, что большинство программистов хотят услышать, поскольку это сложнее. В моем случае я мог бы использовать на 40% больше ЦП и переходить на более 1 млн динамических запросов в секунду. Прокси-метод UDP может приблизить вас к оптимальной производительности без необходимости делать это, однако вам понадобится больше серверов, хотя, если вы делаете это много запросов в секунду, вам обычно потребуется несколько сетевых карт и несколько интерфейсов для обработки полосы пропускания, поэтому пара простых прокси UDP в этом не является большой сделкой.
Надеюсь, что это может быть полезно вам.
Ответ 6
Сколько экземпляров io_service у вас есть? Boost asio имеет example, который создает io_service для каждого процессора и использует их в виде RoundRobin.
Вы все равно можете создать четыре потока и назначить один на каждый процессор, но каждый поток может опросить собственный io_service.