[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