Surprise, it's the Proactor Pattern
So today we've finished the deployment of a very important piece of software where I work. The development of this piece of software has taught me a lot not only about the real power of C++ when you do it "the right way" in terms of expressiveness and sound software design: but also of the power of doing the right things that cause the most impact.
I cannot talk about what that project is and what is about, but I can talk about a surprising little thing that contributed most to the success of the project: the Proactor Pattern. From the original paper:
The Proactor pattern presented in this paper describes how to structure applications and systems that effectively utilize asynchronous mechanisms supported by operating systems. When an application invokes an asynchronous operation, the OS performs the operation on behalf of the application. This allows the application to have multiple operations running simultaneously without requiring the application to have a corresponding number of threads. Therefore, the Proactor pattern simplifies concurrent programming and improves performance by requiring fewer threads and leveraging OS support for asynchronous operations.
This pattern has allowed me to write a very stable highly scalable server. The good thing really is not just that it's a cool pattern, but more because of how you can leverage it to do a lot of other things. The pattern where you "handle the completion of an action" or "do something, then do this" allows you to distribute the processing of what the 'action' is (or what 'this' is) in the particular context to a group of processing threads. Allowing your processing threads to be as oblivious to what it's executing as possible compared to having to tie in the actual processing to specific thread life-times means you're decoupling basically the event queuing mechanism from the actual event handling.
This decoupled design allows users of a framework/library to leverage it in ways that are otherwise impossible with more coupled/rigid frameworks. For instance, I have been (ab)using the Boost.Asio 'io_service' object as a task queue for processing by a thread pool -- by packaging tasks as opaque Boost.Function objects or results of Boost.Bind operations. Boost.Asio uses a proactor framework internally to handle the IO related scheduling and dispatching of asynchronous events. And if C++ developers were lucky, it would be part of the next C++ standard library release (along with C++0x).
The surprise is, thanks to Boost.Asio we're now able to handle thousands of transactions per second with this new piece of software with very little strain on the CPU. Yet again, modern C++ development, a sprinkling of a little template metaprogramming voodoo here and there, and well-designed solutions saves the day -- and provisions for growth to scale in the future.
Time permitting, I may be able to post a simple example of the types of things Parallel Computing, High Performance Computing, Functional Programming, Modern C++, and Template Metaprogramming all culminating together allows. For now, this is an installment in the series of articles "On Parallel Computing" to let the world know of "that little pattern that could".
This is part of an on-going series of articles pertaining to parallel computing and programming techniques in C++. If you have specific questions particular to your application and your situation, please feel free to email me through dean.berris@cplusplus-soup.com and I'll get to your questions as soon as I can.



3 comments:
Great post. Looking forward to reading more articles on these themes.
I like your approach of using the io_service object as task queue and feeding it with functors as tasks. Do you unleash multiple threads on the same io_service object to distribute the work?
In a current project of mine, I am currently leveraging Boost.Asio as well to implement an asynchronous event model (publish/subscribe) that utilizes queues to exchanged events between different contexts.
Keep up the good articles!
Hi Matthias,
Yes, I unleash multiple threads on the same io_service to distribute the work. You may also want to look into the io_service::strand which "serializes" operations associated with an object. More about that from the HTTP example 3 in the Asio distribution.
At any rate, Boost.Asio is really very nice to work with especially if you're interested in performance and efficiency.
Thanks to you and 'jonm' for dropping a line! :)
Post a Comment