пятница, 15 мая 2020 г.

[prog.c++] Любопытное про производительность разных диспетчеров в SObjectizer

Занимаюсь сейчас разработкой нового диспетчера для SObjectizer в составе so5extra. Это asio_one_thread диспетчер. Т.е. создается отдельный экземпляр asio::io_context и на отдельной нити для этого экземпляра запускается цикл обработки событий (т.е. дергается метод io_context::run()). Вся обработка событий агентов, привязанных к такому диспетчеру, выполняется только средствами Asio. По сути, собщения на агентов на этом диспетчере доставляются через asio::post.

Нужен такой диспетчер для того, чтобы можно было к io_context привязать одного или нескольких агентов, которые бы посредством Asio выполняли работу с сетью. И при этом вся работа выполнялась бы строго в рамках одной рабочей нити и никуда больше не уходила.

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

Когда pinger и ponger работают на контекте одной нити (т.е. привязаны к одному io_context-у):

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5_extra.disp.asio_one_thread.ping_pong -r 2700000
Configuration: separate dispatchers: no, requests: 2700000

real    0m1,056s
user    0m1,042s
sys     0m0,005s

Т.е. несколько меньше 3M ping-pong/sec, что весьма неплохо.

На штатном one_thread-диспетчере из SObjectizer-а выходит чуть больше 3M msg/sec:

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5.ping_pong -r 2700000
Configuration: active objects: no, requests: 2700000

real    0m0,768s
user    0m0,750s
sys     0m0,008s

Самая веселуха начинается когда агентов разносишь на разные нити. В случае штатного one_thread-диспетчера получается:

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5.ping_pong -r 2700000 -a
Configuration: active objects: yes, requests: 2700000

real    0m3,479s
user    0m5,092s
sys     0m1,795s

Т.е. порядка 776K ping-pong/sec.

А вот у нового asio_one_thread-диспетчера лучший результат вот такой:

$ time ./target/gcc_7_5_0__x86_64_linux_gnu/release/sample.so_5_extra.disp.asio_one_thread.ping_pong -r 2700000 -s
Configuration: separate dispatchers: yes, requests: 2700000

real    0m18,394s
user    0m11,465s
sys     0m14,112s

Порядка 146K ping-pong/sec или почти в пять раз хуже.

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