Discussion:
[asio-users] io_service post handler copy performance
he keding
2012-04-20 07:16:27 UTC
Permalink
When I test io_service performance(windows),
I find the Handler copy 4 times by call stack.
Can I pass 1 and 4 handler by reference in stead of by value?
If no, any reason? thanks!

1 and 2 is post call

1.
template <typename Handler>
void win_iocp_io_service::post(Handler handler) // here handler copy

2.
completion_handler(Handler& h)
: operation(&completion_handler::do_complete),
handler_(BOOST_ASIO_MOVE_CAST(Handler)(h)) // here handler_ copy
{
}

3 and 4 is call when complete

3.
static void do_complete(io_service_impl* owner, operation* base,
const boost::system::error_code& /*ec*/,
std::size_t /*bytes_transferred*/)
{
// Take ownership of the handler object.
completion_handler* h(static_cast<completion_handler*>(base));
ptr p = { boost::addressof(h->handler_), h, h };

BOOST_ASIO_HANDLER_COMPLETION((h));

// Make a copy of the handler so that the memory can be deallocated
before
// the upcall is made. Even if we're not about to make an upcall, a
// sub-object of the handler may be the true owner of the memory
associated
// with the handler. Consequently, a local copy of the handler is
required
// to ensure that any owning sub-object remains valid until after we
have
// deallocated the memory here.
Handler handler(BOOST_ASIO_MOVE_CAST(Handler)(h->handler_)); // here
handler copy

4.
template <typename Function>
inline void asio_handler_invoke(Function function, ...) // here function
copy
{
function();
}
Marat Abrarov
2012-04-20 07:46:25 UTC
Permalink
When I test io_service performance(windows), 
I find the Handler copy 4 times by call stack.
Can I pass 1 and 4 handler by reference in stead of by value?
If no, any reason? thanks!
Has your Handler class move constructor? I guess you use MS Visual C++ 10.0 (2010). If so then you have to write move
constructor yourself because MSVC 10.0 doesn't support implicit move constructors.
1. 
template <typename Handler>
void win_iocp_io_service::post(Handler handler)  // here handler copy
I don't know why it is written so (it really can be rewritten) - may be because later the handler will be copied (or
even moved) and this can help compiler to do some optimizations.

At mine asio-samples I use:

template <typename Handler>
void some_class::async_do_something(const Handler& handler)

for compilers that doesn't support rvalue references
and:

template <typename Handler>
void some_class::async_do_something(Handler&& handler)
{
... std::forward<Handler>(handler) ...
}

for compilers that support rvalue references (MSVC 10.0 is).
2.
completion_handler(Handler& h)
    : operation(&completion_handler::do_complete),
      handler_(BOOST_ASIO_MOVE_CAST(Handler)(h))  // here handler_ copy
{
}
Here the copy has to be created because of the logic of completion. The only thing that can help here is move
constructor (that is often "lighter" than the copy constructor because of the resource stealing nature).
3.
static void do_complete(io_service_impl* owner, operation* base,
      const boost::system::error_code& /*ec*/,
      std::size_t /*bytes_transferred*/)
  {
    // Take ownership of the handler object.
    completion_handler* h(static_cast<completion_handler*>(base));
    ptr p = { boost::addressof(h->handler_), h, h };
    BOOST_ASIO_HANDLER_COMPLETION((h));
    // Make a copy of the handler so that the memory can be deallocated before
    // the upcall is made. Even if we're not about to make an upcall, a
    // sub-object of the handler may be the true owner of the memory associated
    // with the handler. Consequently, a local copy of the handler is required
    // to ensure that any owning sub-object remains valid until after we have
    // deallocated the memory here.
    Handler handler(BOOST_ASIO_MOVE_CAST(Handler)(h->handler_)); // here handler copy
Here the copy has to be created because of the logic of Asio custom memory allocation. The only thing that can help here
is move constructor. Also the handler var in this place may be moved later. I do it in this way
(ma::handler_storage_service class):
static void do_post(base_type* base, const Arg& arg)
{
this_type* this_ptr = static_cast<this_type*>(base);
// Take ownership of the wrapper object
// The deallocation of wrapper object will be done
// throw the handler stored in wrapper
typedef detail::handler_alloc_traits<Handler, this_type> alloc_traits;
detail::handler_ptr<alloc_traits> ptr(this_ptr->handler_, this_ptr);
// Make a local copy of handler stored at wrapper object
// This local copy will be used for wrapper's memory deallocation later
#if defined(MA_HAS_RVALUE_REFS)
Handler handler(std::move(this_ptr->handler_));
#else
Handler handler(this_ptr->handler_);
#endif
// Change the handler which will be used for wrapper's memory deallocation
ptr.set_alloc_context(handler);
// Make copies of other data placed at wrapper object
// These copies will be used after the wrapper object destruction
// and deallocation of its memory
boost::asio::io_service& io_service(this_ptr->io_service_);
boost::asio::io_service::work work(this_ptr->work_);
(void) work;
// Destroy wrapper object and deallocate its memory
// through the local copy of handler
ptr.reset();
// Post the copy of handler's local copy to io_service
#if defined(MA_HAS_RVALUE_REFS)
io_service.post(detail::bind_handler(std::move(handler), arg));
#else
io_service.post(detail::bind_handler(handler, arg));
#endif
}
4.
template <typename Function>
inline void asio_handler_invoke(Function function, ...) // here function copy
{
  function();
}
This place can be rewritten, I guess. In asio-samples I write it so:

namespace ma_asio_handler_invoke_helpers {

#if defined(MA_HAS_RVALUE_REFS)

template <typename Function, typename Context>
inline void invoke(Function&& function, Context& context)
{
using namespace boost::asio;
asio_handler_invoke(std::forward<Function>(function),
boost::addressof(context));
}

#else // defined(MA_HAS_RVALUE_REFS)

template <typename Function, typename Context>
inline void invoke(const Function& function, Context& context)
{
using namespace boost::asio;
asio_handler_invoke(function, boost::addressof(context));
}

#endif // defined(MA_HAS_RVALUE_REFS)

} // namespace ma_asio_handler_invoke_helpers

Regards,
Marat Abrarov.
he keding
2012-04-21 15:44:03 UTC
Permalink
ok,thanks your replay, i understand more clearly :)
Post by Marat Abrarov
Post by he keding
When I test io_service performance(windows),
I find the Handler copy 4 times by call stack.
Can I pass 1 and 4 handler by reference in stead of by value?
If no, any reason? thanks!
Has your Handler class move constructor? I guess you use MS Visual C++
10.0 (2010). If so then you have to write move
constructor yourself because MSVC 10.0 doesn't support implicit move constructors.
Post by he keding
1.
template <typename Handler>
void win_iocp_io_service::post(Handler handler) // here handler copy
I don't know why it is written so (it really can be rewritten) - may be
because later the handler will be copied (or
even moved) and this can help compiler to do some optimizations.
template <typename Handler>
void some_class::async_do_something(const Handler& handler)
for compilers that doesn't support rvalue references
template <typename Handler>
void some_class::async_do_something(Handler&& handler)
{
... std::forward<Handler>(handler) ...
}
for compilers that support rvalue references (MSVC 10.0 is).
Post by he keding
2.
completion_handler(Handler& h)
: operation(&completion_handler::do_complete),
handler_(BOOST_ASIO_MOVE_CAST(Handler)(h)) // here handler_ copy
{
}
Here the copy has to be created because of the logic of completion. The
only thing that can help here is move
constructor (that is often "lighter" than the copy constructor because of
the resource stealing nature).
Post by he keding
3.
static void do_complete(io_service_impl* owner, operation* base,
const boost::system::error_code& /*ec*/,
std::size_t /*bytes_transferred*/)
{
// Take ownership of the handler object.
completion_handler* h(static_cast<completion_handler*>(base));
ptr p = { boost::addressof(h->handler_), h, h };
BOOST_ASIO_HANDLER_COMPLETION((h));
// Make a copy of the handler so that the memory can be deallocated
before
Post by he keding
// the upcall is made. Even if we're not about to make an upcall, a
// sub-object of the handler may be the true owner of the memory
associated
Post by he keding
// with the handler. Consequently, a local copy of the handler is
required
Post by he keding
// to ensure that any owning sub-object remains valid until after we
have
Post by he keding
// deallocated the memory here.
Handler handler(BOOST_ASIO_MOVE_CAST(Handler)(h->handler_)); // here
handler copy
Here the copy has to be created because of the logic of Asio custom memory
allocation. The only thing that can help here
is move constructor. Also the handler var in this place may be moved
later. I do it in this way
static void do_post(base_type* base, const Arg& arg)
{
this_type* this_ptr = static_cast<this_type*>(base);
// Take ownership of the wrapper object
// The deallocation of wrapper object will be done
// throw the handler stored in wrapper
typedef detail::handler_alloc_traits<Handler, this_type> alloc_traits;
detail::handler_ptr<alloc_traits> ptr(this_ptr->handler_, this_ptr);
// Make a local copy of handler stored at wrapper object
// This local copy will be used for wrapper's memory deallocation later
#if defined(MA_HAS_RVALUE_REFS)
Handler handler(std::move(this_ptr->handler_));
#else
Handler handler(this_ptr->handler_);
#endif
// Change the handler which will be used for wrapper's memory deallocation
ptr.set_alloc_context(handler);
// Make copies of other data placed at wrapper object
// These copies will be used after the wrapper object destruction
// and deallocation of its memory
boost::asio::io_service& io_service(this_ptr->io_service_);
boost::asio::io_service::work work(this_ptr->work_);
(void) work;
// Destroy wrapper object and deallocate its memory
// through the local copy of handler
ptr.reset();
// Post the copy of handler's local copy to io_service
#if defined(MA_HAS_RVALUE_REFS)
io_service.post(detail::bind_handler(std::move(handler), arg));
#else
io_service.post(detail::bind_handler(handler, arg));
#endif
}
Post by he keding
4.
template <typename Function>
inline void asio_handler_invoke(Function function, ...) // here function
copy
Post by he keding
{
function();
}
namespace ma_asio_handler_invoke_helpers {
#if defined(MA_HAS_RVALUE_REFS)
template <typename Function, typename Context>
inline void invoke(Function&& function, Context& context)
{
using namespace boost::asio;
asio_handler_invoke(std::forward<Function>(function),
boost::addressof(context));
}
#else // defined(MA_HAS_RVALUE_REFS)
template <typename Function, typename Context>
inline void invoke(const Function& function, Context& context)
{
using namespace boost::asio;
asio_handler_invoke(function, boost::addressof(context));
}
#endif // defined(MA_HAS_RVALUE_REFS)
} // namespace ma_asio_handler_invoke_helpers
Regards,
Marat Abrarov.
------------------------------------------------------------------------------
For Developers, A Lot Can Happen In A Second.
Boundary is the first to Know...and Tell You.
Monitor Your Applications in Ultra-Fine Resolution. Try it FREE!
http://p.sf.net/sfu/Boundary-d2dvs2
_______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Loading...