Сервер сокетов C ++ - Невозможно насыщать процессор - PullRequest
26 голосов
/ 05 августа 2009

Я разработал мини-HTTP-сервер на C ++, используя boost :: asio, и сейчас я тестирую его с несколькими клиентами, и я не смог приблизиться к насыщению процессора. Я тестирую на экземпляре Amazon EC2 и получаю около 50% использования одного процессора, 20% другого, а остальные два простаивают (согласно htop).

подробности:

  • Сервер запускает один поток на ядро ​​
  • Запросы получены, проанализированы, обработаны, а ответы выписаны
  • Запросы для данных, которые считываются из памяти (только для чтения для этого теста)
  • Я 'загружаю' сервер, используя две машины, на каждой из которых запущено Java-приложение, запущено 25 потоков, отправляются запросы
  • Я вижу около 230 запросов в секунду (это приложение запросов, которые состоят из множества HTTP-запросов)

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

У меня были идеи:

  • Запросы очень маленькие и часто выполняются за несколько мс, я мог бы изменить клиента для отправки / создания больших запросов (возможно, с использованием пакетной обработки)
  • Я мог бы изменить сервер HTTP, чтобы использовать шаблон проектирования Select, это уместно здесь?
  • Я мог бы провести профилирование, чтобы понять, что является узким местом / является

Ответы [ 6 ]

45 голосов
/ 06 августа 2009

boost :: asio не так ориентирован на потоки, как можно было бы надеяться - существует большая блокировка кода epoll в boost / asio / detail / epoll_reactor.hpp, что означает, что только один поток может вызывать системный вызов epoll ядра. вовремя. И для очень маленьких запросов это имеет все значение (то есть вы увидите только приблизительно однопоточную производительность).

Обратите внимание, что это ограничение того, как boost :: asio использует возможности ядра Linux, а не обязательно само ядро ​​Linux. Системный вызов epoll поддерживает множественные потоки при использовании событий, инициируемых фронтом, но получить его правильно (без чрезмерной блокировки) может быть довольно сложно.

Кстати, я проделал некоторую работу в этой области (комбинируя полностью многопоточный цикл событий epoll, инициируемый краем, с запланированными пользователем потоками / волокнами) и сделал некоторый код доступным в проекте nginetd .

10 голосов
/ 06 августа 2009

Поскольку вы используете EC2, все ставки выключены.

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

Я еще не выяснил, для чего нужен EC2, если кто-то узнает, пожалуйста, дайте мне знать.

3 голосов
/ 06 августа 2009

230 запросов / сек кажется очень низким для таких простых асинхронных запросов. Таким образом, использование нескольких потоков, вероятно, является преждевременной оптимизацией - настройте его должным образом и настройте в одном потоке и посмотрите, нужны ли они вам по-прежнему. Простое избавление от ненужной блокировки может ускорить процесс.

Эта статья содержит некоторые подробности и обсуждение стратегий ввода-вывода для производительности в стиле веб-сервера примерно в 2003 году.

3 голосов
/ 06 августа 2009

Из ваших комментариев по использованию сети,
У вас, кажется, нет большого сетевого движения.

3 + 2.5 MiB/sec - около 50Mbps шарового парка (по сравнению с вашим портом 1 Гбит / с).

Я бы сказал, что у вас возникла одна из следующих двух проблем,

  1. Недостаточная рабочая нагрузка (низкая частота запросов от ваших клиентов)
    • Блокировка на сервере (создание ответа с помехами)

Просмотр замечаний cmeerw и показателей использования вашего процессора
(холостой ход 50% + 20% + 0% + 0%)
Скорее всего, это ограничение вашего сервера.
Второй ответ cmeerw (+1).

1 голос
/ 24 февраля 2016

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

Для чего это стоит. использование необработанных вызовов сокетов в моем настраиваемом HTTP может обслуживать 800K динамических запросов в секунду с 4 ядром I7. Он обслуживает из ОЗУ, где вы должны быть для этого уровня производительности. На этом уровне производительности сетевой драйвер и ОС потребляют около 40% ЦП. Используя ASIO, я могу получать от 50 до 100 тыс. Запросов в секунду, его производительность варьируется и в основном связана с моим приложением. Пост @cmeerw объясняет, почему.

Одним из способов повышения производительности является использование прокси-сервера UDP. Перехватывая HTTP-запросы и затем направляя их по UDP на ваш внутренний UDP-HTTP-сервер, вы можете обойти многие издержки TCP в стеках операционной системы. У вас также могут быть внешние интерфейсы, которые проходят через UDP самостоятельно, что не должно быть слишком сложно для себя. Преимущество HTTP-UDP-прокси состоит в том, что он позволяет вам использовать любой хороший интерфейс без изменений, и вы можете менять их по своему желанию без какого-либо влияния. Вам просто нужно еще пару серверов для его реализации. Эта модификация в моем примере снизила загрузку ЦП ОС до 10%, что увеличило количество моих запросов в секунду до чуть более миллиона на этом единственном бэкэнде. И FWIW У вас всегда должна быть настройка внешнего интерфейса для любого работающего сайта, поскольку внешние интерфейсы могут кэшировать данные, не замедляя более важные динамические запросы внутреннего интерфейса.

В будущем, похоже, будет написан ваш собственный драйвер, который реализует свой собственный сетевой стек, чтобы вы могли максимально приблизиться к запросам и реализовать там свой собственный протокол. Что, вероятно, не то, что большинство программистов хотят услышать, поскольку это более сложно. В моем случае я мог бы использовать на 40% больше процессора и переходить к более чем 1 миллиону динамических запросов в секунду. Прокси-метод UDP может приблизить вас к оптимальной производительности без необходимости делать это, однако вам потребуется больше серверов - хотя, если вы делаете столько запросов в секунду, вам обычно потребуется несколько сетевых карт и несколько внешних интерфейсов для обработки полосы пропускания пара легких прокси-серверов UDP там не такая уж большая проблема.

Надеюсь, что это вам пригодится.

0 голосов
/ 19 июня 2016

Сколько у вас экземпляров io_service? Boost asio имеет пример , который создает io_service для каждого процессора и использует их в форме RoundRobin.

Вы по-прежнему можете создавать четыре потока и назначать по одному на каждый процессор, но каждый поток может опрашивать свой собственный io_service.

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