Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > boost::asio::io_service.run() и много блокировок


Автор: phprus 17.11.2012, 16:56
Доброго времени суток!

Написал я некоторый сервер с использованием boost.asio по модели, когда есть один экземпляр io_service и пул потоков по числу cpu, каждый из которых выполняет run().

Профилирование сервера показало просто дикое число блокировок мутексов и условных переменных внутри ASIO при использовании epoll. Сейчас на маленьких тестах это миллионы блокировок, на больших тестах и при нормальной работе будут миллиарды, что душу явно не греет и не факт, что способствует использованию всех 4-х ядер существующей железки и всех 20 планируемой.

Почитав документацию я увидел следующее http://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/overview/implementation.html :
Цитата

Demultiplexing using epoll is performed in one of the threads that calls io_service::run(), io_service::run_one(), io_service::poll() or io_service::poll_one().

и похоже именно отсюда растут уши такого количества блокировок.

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

Автор: mabrarov 17.11.2012, 18:30
Вот http://asio-samples.blogspot.ru/2012/07/asio-performance-test.html, http://www.blogger.com/comment.g?blogID=3767932718256497924&postID=8263472374514844 и https://github.com/virtan/mrps есть некоторые размышления http://comments.gmane.org/gmane.comp.lib.boost.asio.user/5326.

Вкратце, вы правы - придется применять "модель вида пул потоков в каждом из которых свой io_service и жестким распихиванием клиентов по этим io_service при подключении". Этот выбор можно и отложить на run time - см. проект echo_server (qt_echo_server) из https://asio-samples.svn.sourceforge.net/svnroot/asio-samples/trunk.

Если же углубиться в детали, то "виновата" не библиотека Boost.Asio а сам epoll (точнее его реактивная идеология). В Windows, с его проактором на уровне ОС, я не смог повторить эту проблему.

Автор: phprus 17.11.2012, 19:07
mabrarov,
Спасибо за ссылки, почитаю.

Цитата(mabrarov @  17.11.2012,  21:30 Найти цитируемый пост)
Вкратце, вы правы - придется применять "модель вида пул потоков в каждом из которых свой io_service и жестким распихиванием клиентов по этим io_service при подключении".

Значит придется применить...

Цитата(mabrarov @  17.11.2012,  21:30 Найти цитируемый пост)
Этот выбор можно и отложить на run time - см. проект echo_server (qt_echo_server) из

Немного не понял, что имеется ввиду?
Нечто отличное от выбора io_service в момент создания объекта-клиента? А выбор например циклический.


Цитата(mabrarov @  17.11.2012,  21:30 Найти цитируемый пост)
В Windows, с его проактором на уровне ОС, я не смог повторить эту проблему. 

А про Windows мануал говорит:
Цитата

Demultiplexing using I/O completion ports is performed in all threads that call io_service::run(), io_service::run_one(), io_service::poll() or io_service::poll_one().

Там такой проблемы и не должно быть судя по всему.

Автор: mabrarov 19.11.2012, 00:22
Цитата(phprus @ 17.11.2012,  19:07)
Немного не понял, что имеется ввиду?
Нечто отличное от выбора io_service в момент создания объекта-клиента? А выбор например циклический.

Модель (одну из) 
  •  один экземпляр asio::io_service и пул рабочих потоков, работающих с этим единственным экземпляром
  •  по одному экземпляру asio::io_service на каждый рабочий поток (аля пул из asio::io_service)
можно выбирать в run time.

Автор: phprus 24.11.2012, 15:36
Цитата(mabrarov @  19.11.2012,  03:22 Найти цитируемый пост)
можно выбирать в run time.

Да, действительно.


В итоге я остановился на следующей структуре приложения:
1) main_io_service - не занимается вообще ничем кроме как отслеживания сигналов операционной системы (SIGINT и т.д.), выполняется в главном потоке отвечает за управление модулями, корректное завершения приложения и т.д.
2) background_io_service и пул потоков, выполняющих background_io_service.run() - предназначен для выполнения тяжелых фоновых операций. Вспомогательные таймеры, загрузка/выгрузка на диск различных данных, с которыми можно оперировать в фоновом режиме.
3) пул потоков, в каждом свой io_service - для обслуживания сетевой активности. Сокет в момент создания прикрепляется к одному из потоков. Так как большинство моих действий при работе с сетью очень легкие, то это работает нормально даже не смотря на наличие некоторого количества mutex'ов для защиты общих структур данных. Вначале я беспокоился, что один глобальный mutex будет тормозить, но тестирование показало что до момента, когда я упрусь в него еще очень и очень долго (глобальный mutex защищает map объектов, которые нужно доставать по ключу, а так как map редко, но все-же меняется, то пришлось использовать mutex).

Автор: borisbn 24.11.2012, 16:07
Цитата(phprus @  24.11.2012,  15:36 Найти цитируемый пост)
глобальный mutex защищает map объектов, которые нужно доставать по ключу, а так как map редко, но все-же меняется, то пришлось использовать mutex

посмотри http://www.boost.org/doc/libs/1_51_0/doc/html/thread/synchronization.html#thread.synchronization.mutex_types.shared_mutex
Цитата
The class boost::shared_mutex provides an implementation of a multiple-reader / single-writer mutex.

ИМХО как раз твой случай

Автор: phprus 25.11.2012, 10:01
borisbn
Спасибо за совет про Boost.shared_mutex, я про него правда знаю.
В ближайшее время посмотрю на сколько shared_mutex будет лучше.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)