Повысить реализацию ASIO IO_SERVICE? - PullRequest
8 голосов
/ 20 марта 2012

Я писал фреймворк асинхронной регистрации, в котором у меня было несколько потоков, сбрасывающих данныеЯ начал играть в Boost asio, потому что он предлагал несколько простых способов обеспечения сериализации и упорядочения.Так как я новичок, я начал свой проект с поточно-ориентированного (использовал boost::mutex и boost:condition_variable) циклического bounded_buffer (который на самом деле был вектором).

Я написал небольшой простой тест для измерения производительности.Тест является простым потоком, регистрирующим миллион сообщений (помещая его в буфер), и мой рабочий поток просто извлекал сообщения из очереди для записи в файл / консоль / список регистраторов.(PS Использование мьютекса и CV было правильным, и указатели на сообщения перемещались, поэтому с этой точки зрения все было хорошо / эффективно).

Когда я изменил свою реализацию на использование вместо boost::asio::io_service и ис одним потоком, выполняющим run() производительность действительно улучшилась (на самом деле она очень хорошо масштабировалась при увеличении количества регистрируемых сообщений, в отличие от снижения производительности в моей первоначальной простой модели)

Вот несколько вопросов, которые я хочуочистить.

  1. Почему улучшение производительности?(Я думал, что внутренняя реализация boost::asio::io_service имеет потокобезопасную очередь для обработчиков, что делает ее намного более эффективной, чем мой собственный первоначальный простой дизайн очереди с безопасным потоком).Пожалуйста, обратите внимание, что мой дизайн был хорошо рассмотрен и не имел ошибок как таковых (скелетный код был основан на проверенных примерах), может кто-то пролить свет на внутренние детали того, как io_service реализует это.

  2. Вторым интересным наблюдением было то, что при увеличении потоков моя начальная производительность реализации улучшилась, но за счет потери сериализации / упорядочения, но производительность снизилась (очень незначительно) с boost :: asio (я думаю, это потому, что мои обработчики быливыполнение очень упрощенной задачи и переключение контекста было унизительным, я постараюсь поставить более сложную задачу и позже опубликую свои наблюдения).

  3. Мне бы очень хотелось узнать, имеется ли в виду boost::asioдля ввода-вывода и сетевых операций, или я использую его для выполнения параллельной задачи (параллельной) через пул потоков - это хороший подход к проектированию.Является ли io_service объект просто предназначенным для использования для объектов ввода / вывода (как написано в документации), но я нашел это действительно интересным способом помочь мне решать параллельные задачи (не только связанные с вводом / выводом или сетью) последовательным способом (иногда обеспечение порядка с использованием нитей).Я новичок в повышении, и мне действительно любопытно, почему базовая модель не работала и не масштабировалась так же, как когда я использовал boost asio.

Результаты: (в обоих случаях у меня был только 1 рабочийнить)

  • 1000 задача: 10 микросекунд / задача в обоих случаях
  • 10000 задача: 80 микросекунд (ограниченный буфер), 10 микросекунд в ускорении asio
  • Задача 100000: 250 микросекунд (основной буфер), 10 микросекунд в boost asio

Было бы интересно узнать, как boost решает поточно-ориентированную проблему в io_service потоко-безопасной очереди для обработчиков (iвсегда думал, что на каком-то уровне реализации они также должны использовать блокировки и cv).

Ответы [ 3 ]

4 голосов
/ 21 марта 2012

Боюсь, я не могу помочь с (1), но в отношении двух других вопросов:

(2) Я обнаружил, что в архитектуре boost::asio есть некоторые издержки, которые не являются детерминированными, то есть, что задержки между поступлением данных (или отправкой на объект службы ввода-вывода) могут отличаться от практически мгновенного ответа до порядка сотен миллисекунд. Я попытался измерить это как часть другой проблемы, которую я пытался решить в отношении регистрации и метки времени данных RS232, но не получил каких-либо окончательных результатов или способов стабилизации задержки. Я не удивлюсь, если обнаружу, что подобные проблемы существуют с компонентом переключения контекста.

(3) Что касается использования boost::asio для задач, отличных от асинхронного ввода-вывода, то теперь это мой стандартный инструмент для большинства асинхронных операций. Я все время использую таймеры boost::asio для асинхронных процессов и для генерации таймаутов для других задач. Возможность добавления нескольких рабочих потоков в пул означает, что вы можете хорошо масштабировать решение и для других асинхронных задач с высокой нагрузкой. Мой самый простой и любимый класс, который я написал в прошлом году, - это крошечный класс рабочих потоков для boost::asio служб ввода-вывода (извините, если есть какие-либо опечатки, это транскрипция из памяти, а не вырезка и вставка):

class AsioWorker
{
public:
  AsioWorker(boost::asio::io_service * service):
  m_ioService(service), m_terminate(false), m_serviceThread(NULL)
  {
    m_serviceThread = new boost::thread( boost::bind( &AsioWorker::Run, this ) )
  }
  void Run( void )
  {
    while(!m_terminate)
      m_ioService->poll_one();
      mySleep(5); // My own macro for cross-platform millisecond sleep
  }
  ~AsioWorker( void )
  {
    m_terminate = true;
    m_serviceThread->join();
  }
private:
  bool m_terminate;
  boost::asio::io_service *m_ioService;
  boost::thread *m_serviceThread;
}

Этот класс - отличная маленькая игрушка, просто добавьте new по мере необходимости и delete, когда закончите с ними. Вставьте std::vector<AsioWorker*> m_workerPool в класс устройств, который использует boost::asio, и вы сможете еще больше обернуть элементы управления пулом потоков. Я всегда испытывал желание написать интеллектуальный авто-менеджер пула, основанный на времени, чтобы увеличить пул потоков соответствующим образом, но у меня еще не было проекта, где это было необходимо.

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

0 голосов
/ 15 сентября 2018

@ user179156. Очень интересный вопрос Мне также было интересно узнать о том же вопросе, который указан в пункте (1) в вашем сообщении. Вот что я нашел

Ниже приводится выдержка из форсированной документации.

Потокобезопасность Как правило, можно одновременно использовать отдельные объекты, но небезопасно одновременно использовать один объект. Тем не менее, такие типы, как io_service обеспечивают более надежную гарантию что одновременно можно использовать один объект

Вопрос в том, как тогда Boost ASIO настолько популярен и как он реализован внутри компании для обеспечения эффективности.

Вот выдержка из документации, которая дает представление о том, как это реализовано внутри.

Механизм демультиплексирования Linux Kernel 2.6: • Использует epoll для демультиплексирования. Темы: • Демультиплексирование с использованием epoll выполняется в один из потоков, который вызывает io_service :: run (), io_service :: run_one (), io_service :: poll () или io_service :: poll_one (). • Дополнительный поток на io_service используется для эмуляции асинхронного разрешение хоста

Теперь давайте посмотрим, как мы можем сделать связь между внутренним потоком boost и потоком демультиплексора более эффективной. Если бы я должен был написать boost asio с нуля. Вот как бы я это сделал

Используйте epoll с трубами

Таким образом, чтобы передавать сообщения между потоками, я буду просто передавать указатель на объект, который я передал в boost :: post. Вот интересная ссылка о том, как эффективно передавать сообщения между потоками

0 голосов
/ 24 марта 2013

Я также нашел boost::asio отличной инфраструктурой для общего многоядерного процессора.Я измерил его производительность в мелкозернистой задаче с большим количеством синхронизации и обнаружил, что она превосходит «классическую» реализацию, которую я написал, используя потоки C ++ 11 и условные переменные.

Он также превосходил TBB, но не так сильно.Я копался в их коде, чтобы попытаться найти «секрет».Единственное, что я вижу, это то, что их очередь представляет собой классический связанный список, а не контейнер stl.

Несмотря на это, я не уверен, насколько хорошо asio будет масштабироваться в многопоточной архитектуре, такой какXeon Phi.Кажется, что отсутствуют две вещи:

  1. приоритетная очередь и
  2. очередь для кражи работы.

Я подозреваю, что добавление этих функций приведет кдовести его до уровня производительности TBB.

...