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

Reply via email to