|
Boost : |
From: Christopher Kohlhoff (chris_at_[hidden])
Date: 2005-12-17 19:32:36
Hi Ion,
Just some quick comments about the memory allocations:
--- Ion Gaztañaga <igaztanaga_at_[hidden]> wrote:
<snip>
> Good overall. I've following the "Daytime.3 - An asynchronous
> TCP daytime server" example with the Visual 7.1 debugger and
> specially looking for mechanisms that hurt embedded systems.
> I've found some interesting things about memory allocations.
> I've commented the code with the number of calls to "operator
> new":
<snip annotated code>.
I made a few quick changes and re-ran the daytime3 server to
observe the memory allocations. What I changed was:
- Created a class static member to be returned by the null()
function. Returning a new object each time was a bit of a
pessimisation, I admit :)
- Explicitly initialised the services socket_acceptor_service
and stream_socket_service. Normally these are initialised on
the first use of a socket_acceptor or stream_socket
respectively. I made this change to the example to make it
clearer what the per-object cost is.
- Only initialise the reactor implementation on the first use of
asynchronous connect emulation (it's not used in this
program).
------------------------------
int main()
{
try
{
//--> 4 calls to new.
asio::demuxer demuxer;
//--> 4 calls to new.
demuxer.get_service(asio::service_factory<
asio::socket_acceptor_service<> >());
//--> 2 calls to new.
demuxer.get_service(asio::service_factory<
asio::stream_socket_service<> >());
//--> 2 calls to new associated with socket in shared_ptr.
asio::socket_acceptor acceptor(demuxer,
asio::ipv4::tcp::endpoint(13));
//--> 1 call to new (the explicit new shown here).
asio::stream_socket* socket
= new asio::stream_socket(demuxer);
//--> 1 call to new for OVERLAPPED-derived object.
acceptor.async_accept(*socket,
boost::bind(handle_accept, &acceptor, socket,
asio::placeholders::error));
//--> 2 calls to new when accepting new connection and
// underlying socket in shared_ptr is created.
demuxer.run();
}
catch (asio::error& e)
{
std::cerr << e << std::endl;
}
return 0;
}
void handle_accept(asio::socket_acceptor* acceptor,
asio::stream_socket* socket, const asio::error& error)
{
if (!error)
{
using namespace std; // For time_t, time, ctime, strdup and strlen.
time_t now = time(0);
char* write_buf = strdup(ctime(&now));
size_t write_length = strlen(write_buf);
//--> 1 call to new for OVERLAPPED-derived object.
asio::async_write(*socket,
asio::buffer(write_buf, write_length),
boost::bind(handle_write, socket, write_buf,
asio::placeholders::error,
asio::placeholders::bytes_transferred));
//--> 1 call to new (the explicit new shown here).
socket = new asio::stream_socket(acceptor->demuxer());
//--> 1 call to new for OVERLAPPED-derived object.
acceptor->async_accept(*socket,
boost::bind(handle_accept, acceptor, socket,
asio::placeholders::error));
}
else
{
delete socket;
}
}
void handle_write(asio::stream_socket* socket, char* write_buf,
const asio::error& /*error*/, size_t /*bytes_transferred*/)
{
using namespace std; // For free.
free(write_buf);
//--> 0 calls to new.
delete socket;
}
------------------------------
The shared_ptr wrapping the socket is a workaround for Windows
not behaving as MSDN says it should with respect to operation
cancellation. I'd be *very* happy to find another efficient way
to correctly indicate operation cancellation without requiring
the allocation.
The intention was that the Allocator template parameter be used
for custom memory allocation, much as it is in the standard
library. However the current implementation doesn't use it.
Christopher Baus has said that he is looking at another way to
allow customisable allocation, and I look forward to seeing his
proposal.
Cheers,
Chris
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk