Boost logo

Boost :

From: Christopher Kohlhoff (chris_at_[hidden])
Date: 2005-12-20 07:45:37


Hi Rene,

--- Rene Rivera <grafik.list_at_[hidden]> 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;
}


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk