Performance problem on Android with OioWorker
chudak
meadandale at gmail.com
Thu Jul 21 12:09:48 EDT 2011
We are building an application on Android that is using Netty for our com
layer. We recently switched from using NIO to OIO because of some major
Android/Dalvik problems in the NIO layer and SSLEngine that we've
encountered.
After switching to OIO we noticed major performance problems sending large
messages across our com channel.
One of our developers noticed that the OioWorker is using
InputStream.available() to size the input buffer when reading bytes:
int bytesToRead = in.available();
if (bytesToRead > 0)
{
buf = new byte[bytesToRead];
readBytes = in.read(buf);
}
The problem is that this method is unreliable. The android api documentation
provides a more 'interesting' description of this method than the Oracle
javadocs (emphasis mine):
http://developer.android.com/reference/java/io/InputStream.html#available%28%29
"Returns an estimated number of bytes that can be read or skipped without
blocking for more input.
*Note that this method provides such a weak guarantee that it is not very
useful in practice.*
Firstly, the guarantee is "without blocking for more input" rather than
"without blocking": a read may still block waiting for I/O to complete — the
guarantee is merely that it won't have to wait indefinitely for data to be
written. The result of this method should not be used as a license to do I/O
on a thread that shouldn't be blocked.
*Secondly, the result is a conservative estimate and may be significantly
smaller than the actual number of bytes available. In particular, an
implementation that always returns 0 would be correct. In general, callers
should only use this method if they'd be satisfied with treating the result
as a boolean yes or no answer to the question "is there definitely data
ready?".*
Thirdly, the fact that a given number of bytes is "available" does not
guarantee that a read or skip will actually read or skip that many bytes:
they may read or skip fewer.
*It is particularly important to realize that you must not use this method
to size a container and assume that you can read the entirety of the stream
without needing to resize the container.* Such callers should probably write
everything they read to a ByteArrayOutputStream and convert that to a byte
array. Alternatively, if you're reading from a file, length() returns the
current length of the file (though assuming the file's length can't change
may be incorrect, reading a file is inherently racy)."
On Android, the InputStream.available() results in a recv(MSG_PEEK) system
call, to which the kernel only guarantees that 1 byte can be read without
blocking. This results in the worker continually reading a single byte and
then notifying the decoder that more data is available to read--leading to
an unreasonable number of attempts to decode the messages.
We've modified the OioWorker locally to do:
buf = new byte[bytesToRead > 10240 ? bytesToRead : 10240]; // use 10k buffer
unless more bytes are available
which has solved the performance problem.
--
View this message in context: http://netty-forums-and-mailing-lists.685743.n2.nabble.com/Performance-problem-on-Android-with-OioWorker-tp6607433p6607433.html
Sent from the Netty User Group mailing list archive at Nabble.com.
More information about the netty-users
mailing list