[Undertow-dev] Minimising data copies
Tom Anderson
twic at urchin.earth.li
Sun Feb 21 12:40:27 UTC 2016
Hello,
I'm interested in building a server that serves templated HTML responses,
and in doing so does as little allocation and data movement as possible.
I'm doing the templating with Mustache, which writes its output to a
Writer:
https://github.com/spullara/mustache.java/blob/master/compiler/src/main/java/com/github/mustachejava/Mustache.java#L57
First question: am i right in thinking that in Undertow, there is no way
for a handler to write directly to the response stream, or at least, that
this is not a sensible thing to do? It would involve blocking the handler
thread, so it seems like it would be completely missing the point.
So, the thing to do then is to get data from a Writer to a Sender.
Currently, i'm doing this:
StringWriter buffer = new StringWriter();
mustacheTemplate.execute(buffer, scope);
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/html");
exchange.getResponseSender().send(buffer.toString());
Which works. However, it involves accumulating the response data in a
StringWriter, then converting it to a String, then handing it to Undertow,
which AIUI will later (in AsyncSenderImpl::send(String, Charset,
IoCallback)) convert it to a byte array, then copy the contents of the
byte array into however many pooled ByteBuffers are necessary, before
finally sending the pooled buffers off to the network.
That involves three complete copies of the data!
Second question: is that correct, or have i missed something?
Now, i see that Sender also has a couple of methods that operate directly
on ByteBuffers:
Sender::send(ByteBuffer, IoCallback)
Sender::send(ByteBuffer[], IoCallback)
And, AIUI, if i call either of these, then there will be no more copies on
the way out to the network.
Third question: are these methods the ones to use if i want to minimise
copies?
If so, i have a pile more questions:
Fourth: does it matter if i call the method which takes a single buffer,
and pass a buffer bigger than 16 kB? Will that break things, or lead to
seriously degraded performance?
Fifth: should the buffers be wrapped or direct?
Sixth: should i create my own buffers, or obtain them from pool that i can
reach with exchange.getConnection().getByteBufferPool()?
Seventh: if i use buffers from the pool, whose responsibility is it to
return them to the pool, mine or Undertow's?
If the answers to these questions are, respectively, yes, yes, yes, yes,
direct, from the pool, mine, then it looks to me like it would be really
useful to have a small shim which sits on the exchange and makes it easy
to do all this starting from an OutputStream. Something like:
ResponseSlicer slicer = new ResponseSlicer(exchange);
OutputStream out = slicer.getOutputStream();
// use out
slicer.send();
Or even just:
OutputStream out = new ResponseSlicingOutputStream(exchange);
// use out
out.close();
If that seems sensible, i'm happy to write it and raise a pull request to
add it.
Thanks,
tom
--
10 PARTY : GOTO 10
More information about the Undertow-dev
mailing list