Hi guys,
those last days, I spent some time on the UDP server, and I found many
places where we can improve the bare MINA performances.
I have already applied a few patches :
- The boundHandles field was a Map<String, H>, storing the channel
associated with a socketAddress using a String. This String was created
using the getAddressAsString method. It has been removed, and we now use
the SocketAddress as a key in the map (note that we already do that for TCP)
- We were creating a IoBuffer to read a datagram from the channel, with
a fixed size (the buffer were to be large enough to store the biggest
possible Datagram). That's ok, but we then created another IoBuffer to
store just the Datagram bytes, discarding the bytes after the first
buffer limit. This was a totally spurious operation : I removed the
creation of this second buffer.
Those fixes led to a visible speedup : for a test I'm using, I was able
to go down from 29 secs to 17 secs.
But there is more.
The IoSessionRecycler class is used to avoid creating new sessions for
UDP clients when they connect. It makes sense, as this is a disconnected
protocol, to keep a session associated with a remote host. The problem
is that we store those sessions using an Object containing the remote
and local address :
...
session = sessionRecycler.recycle(localAddress, remoteAddress);
...
public IoSession recycle(SocketAddress localAddress,
SocketAddress remoteAddress) {
return sessionMap.get(generateKey(localAddress, remoteAddress));
}
and
private Object generateKey(SocketAddress localAddress,
SocketAddress remoteAddress) {
List<SocketAddress> key = new ArrayList<SocketAddress>(2);
key.add(remoteAddress);
key.add(localAddress);
return key;
}
This is extremely costly. It represents 17% of the whole time.
My perception is that it makes no sense to use the localAddress as a
part of the session key, we should only use the remoteKey. Doing so save
11% (down from 17% to 6%).
Last, not least, we have some potential gain in some other areas. For
instance, we have a hierarchy where the NioDatagramAcceptor inherit from
AbstractPollingConnectionlessIoAcceptor, and the processReadySessions()
method which is in the abtstract class calls many methods in the
NioDatagramAcceptor. That's not bad per se, except that the
processReadySessions() method iterates on the channels :
private void processReadySessions(Iterator<H> handles) {
while (handles.hasNext()) {
H h = handles.next();
handles.remove();
try {
if (isReadable(h)) {
readHandle(h);
}
if (isWritable(h)) {
for (IoSession session :
getManagedSessions().values()) {
scheduleFlush((S) session);
}
}
} catch (Throwable t) {
ExceptionMonitor.getInstance().exceptionCaught(t);
}
}
}
where the Iterator is an instance of DatagramChannelIterator :
private static class DatagramChannelIterator implements
Iterator<DatagramChannel> {
private final Iterator<SelectionKey> i;
private DatagramChannelIterator(Collection<SelectionKey> keys) {
this.i = keys.iterator();
}
public boolean hasNext() {
return i.hasNext();
}
public DatagramChannel next() {
return (DatagramChannel) i.next().channel();
}
public void remove() {
i.remove();
}
}
The problem is that the isReadable() and isWritable() methods need to
get the SelectionKey associated to the channel :
protected boolean isReadable(DatagramChannel handle) {
SelectionKey key = handle.keyFor(selector);
if ((key == null) || (!key.isValid())) {
return false;
}
return key.isReadable();
}
So we do a SelectionKey -> channel conversion, followed by a channel ->
SelectionKey conversion (called twice, one in isReadable, on in isWritable)/
We can most certainly move all this code in the NioDatagramChannel
class, and save some extra CPU.
Note that I have already removed the DatagramChannelIterator class, to
use an iterator over the SeelctionKeys :
private void processReadySessions(Set<SelectionKey> handles) {
Iterator<SelectionKey> iterator = handles.iterator();
while (iterator.hasNext()) {
H handle = (H) iterator.next().channel();
iterator.remove();
...
and it also saves a few %...
Ok, that's pretty much it for today, feel free to comment.
--
Regards,
Cordialement,
Emmanuel Lécharny
www.iktek.com