I was writing an asynchronous logging framework, where I had multiple threads dumping data. I started playing around Boost asio because it offered some easy ways to enforce serialization and ordering. Since I am a beginner, I started my design with a thread safe (used boost::mutex
and boost:condition_variable
) circular bounded_buffer (which was actually vector).
I wrote a small simple benchmark to measure the performance. The benchmark is just a single thread logging a million messages (pushing it into the buffer) , and my worker thread would just grab the messages from the queue to log to file/console/list of loggers. (P.S. The usage of mutex and C.V were correct, and pointers to messages were being moved around, so from that perspective everything was fine/efficient ).
When I changed my implementation to instead using boost::asio::io_service
and and having a single thread executing run()
the performance really improved (actually it scaled really well on increasing the number of messages being logged as opposed to degrading performance in my initial simple model)
Here are few questions that I want to clear.
Why performance improvement? (I thought
boost::asio::io_service
internal implementation has thread safe queue for handlers , what makes it so much more efficient than my own initial simple thread safe queue design ). Please take note that my design was well reviewed and had no faults as such (the skeleton code was based on proved examples), could someone shed more light on internal details of howio_service
implements this.The second interesting observation was that on increasing threads, my initial implementation performance improved but at the cost of losing serialization/ordering, but performance degraded (very slightly) with boost::asio (i think that is because my handlers were doing very simplistic task and context switching overhead was degrading, i will try putting more complex task and post my observations later).
I would really like to know if
boost::asio
is just meant for i/o and network operations or is my usage of using it for doing concurrent task (parallel) through a thread pool is a good design approach. Isio_service
object just meant to be used for i/o objects (as written in documentation) , but I found it a really interesting way of helping me solving concurrent tasks (not just i/o or networking related) in serialized way ( sometimes enforcing ordering using strands). I am new to boost, and really curious why the basic model didn't perform/scale as well as when i used boost asio.
Results: (in both i just had 1 worker thread )
- 1000 task : 10 micro sec/task in both cases
- 10000 task : 80 micro sec (bounded buffer) , 10 micro sec in boost asio
- 100000 task : 250 micro sec (bounde buffer) , 10 micro sec in boost asio
It would be interesting to know how boost solves thread safe problem in io_service
thread safe queue for handlers (i always thought at some level of implementation they also have to use locks and c.v ).
boost::asio
for managing other types of asynchronous tasks (not just network IO) to good success. – Partnership