
Hi Rene, --- Rene Rivera <grafik.list@redshift-software.com> wrote:
I vote to *not* accept Asio as a Boost library. This is based on some specific deficiencies in design/implementation and documentation. Hence if those are remedied I would be willing to change my vote.
I love a challenge :) <snip>
What is your evaluation of the documentation?
Insufficient -- Even though I'm a proponent of Dox style automated documentation in this case the Dox reference documentation is insufficient in describing how the various operations work. The supporting design, tutorials, and examples I don't feel provide sufficient background to understand how the pieces connect and react with each other. Specifically the example code should at minimum explain why calls are done. In summary, either the reference docs need to improve significantly, or the non-reference docs need to take up the slack.
Fair enough. I totally agree that the documentation can be improved, although naturally everyone has a different opinion on what bit of the documentation has the highest priority. It's an ongoing task for me. What else can I say?
Now some details about my use case and my performance concern... <snip> Of course it would be awesome if Christopher would come out and tell me I'm stoned and point out how to achieve the optimum I need. Barring that for me to use Asio for my project I would end up creating patched versions of epoll_reactor, kqueue_reactor, reactor_op_queue, reactive_sokect_service, and win_iocp_socket_service (maybe more). Such hacking of implementation classes to me means a serious design failure.
Yep, custom allocation is something I want to address, and others here (notably Christopher Baus) are taking an active enough interest in it to explore what can be done. However, thanks to the description of your use case, I have had an idea of how to enable what I think it is you want reasonably easily. I'm sure you'll correct me if it's not :) The change requires only a very minor interface change -- the addition of a class template which I shall call handler_allocator. All new calls that are associated with a handler in some way will have to be changed to use it. Here is the default implementation, which as you can see simply forwards the calls to the supplied standard allocator. namespace boost { namespace asio { template <typename Handler> class handler_allocator { public: template <typename Allocator> static typename Allocator::pointer allocate( Handler& handler, Allocator& allocator, typename Allocator::size_type count) { return allocator.allocate(count); } template <typename Allocator> static void deallocate( Handler& handler, Allocator& allocator, typename Allocator::pointer pointer, typename Allocator::size_type count) { allocator.deallocate(pointer, count); } }; } } // namespaces This class can be specialised for specific handler types. Plus I will make the following guarantee: all memory associated with a handler will be deallocated before the handler is invoked. This means that you can reuse the same memory block for a chain of calls (such as the chain of async_receive calls in your case). This class will also have to be partially specialised for the handlers for composed operations inside asio (such as async_read and async_write) to ensure that the allocate() and deallocate() calls are forwarded to the correct specialisation for the user's own handler. I did a proof of concept of this for the demuxer::post() operation on Windows using MSVC 7.1. The test program is shown below. In this test avoiding dynamic memory allocation gives approximately 15% performance improvement. The program shows how the handler parameter to the allocate/deallocate functions can be used to get hold of per-handler memory, if you need that level of control. Cheers, Chris ------------------------------------------------------------- #include <asio.hpp> #include <iostream> class handler; namespace boost { namespace asio { template <> class handler_allocator<::handler>; } } // namespaces class handler { public: handler( boost::asio::demuxer& demuxer, int& count, void* buffer) : demuxer_(demuxer), count_(count), buffer_(buffer) { } void operator()() { if (count_ > 0) { --count_; demuxer_.post(*this); } } private: boost::asio::demuxer& demuxer_; int& count_; void* buffer_; friend class boost::asio::handler_allocator<handler>; }; template <> class boost::asio::handler_allocator<::handler> { public: template <typename Allocator> static typename Allocator::pointer allocate( handler& h, Allocator& allocator, typename Allocator::size_type count) { return static_cast<typename Allocator::pointer>(h.buffer_); } template <typename Allocator> static void deallocate( handler& h, Allocator& allocator, typename Allocator::pointer pointer, typename Allocator::size_type count) { } }; int main() { boost::asio::demuxer d; DWORD start = GetTickCount(); int count = 1000000; char buf[1024]; d.post(handler(d, count, buf)); d.run(); DWORD end = GetTickCount(); std::cout << (end - start) << " ticks\n"; return 0; }