Сервер 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.