C
Cyrille \cns\ Szymanski
Hello,
I'm benchmarking several server io strategies and for that purpose I've
built two simplistic Java ECHO servers.
One of the server implementation takes advantage of the java.nio API.
However it (my implementation) is slower than the classic 1 thread /
client server. I've managed to find out (thanks to the profiler) that the
accept() function call was slowing down the process. The strange thing is
that I'm calling accept() only when SelectionKey.isAcceptable() and thus
this operation should be fast, right ? Issues ?
To test this behaviour I used a program that sequentially creates N
connections to the server. The first server I wrote used an infinite loop
that accepts sockets from a ServerSocket. The second server I wrote uses
nio, selects on OP_ACCEPT and does a SelectionKey.accept(). This takes
about 10 times longer.
I'd also like to take advantage of multiprocessor architectures and spawn
as many "worker threads" (taken from the IOCP voc.) as there are CPUs
installed. Has anybody done this already ?
Is it good practice to have multiple threads waiting on select() on the
same Selector ?
How can I register a Channel with a selector while one thread is in
Selector.select() and have this thread process incoming events ? What
I've done so far is loop on selects() with a timeout but this surely
isn't good practice.
I'm saddened to see that as I wrote it the 1 thread/client outperforms
the nio one...
Here comes the code :
import java.io.*;
import java.lang.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
public class javaenh
{
public static void main(String args[]) throws Exception
{
// incoming connection channel
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
channel.socket().bind( new InetSocketAddress( 1234 ) );
// Register interest in when connection
Selector selector = Selector.open();
channel.register( selector, SelectionKey.OP_ACCEPT );
System.out.println( "Ready" );
// Wait for something of interest to happen
while( selector.select()>0 )
{
// Get set of ready objects
Iterator readyItor = selector.selectedKeys().iterator();
// Walk through set
while( readyItor.hasNext() )
{
// Get key from set
SelectionKey key = (SelectionKey)readyItor.next();
readyItor.remove();
if( key.isReadable() )
{
// Get channel and context
SocketChannel keyChannel = (SocketChannel)key.channel
();
ByteBuffer buffer = (ByteBuffer)key.attachment();
buffer.clear();
// Get the data
if( keyChannel.read( buffer )==-1 ) {
keyChannel.socket().close();
buffer = null;
} else {
// Send the data
buffer.flip();
keyChannel.write( buffer );
// wait for data to be sent
keyChannel.register( selector,
SelectionKey.OP_WRITE, buffer );
}
}
else if( key.isWritable() )
{
// Get channel and context
SocketChannel keyChannel = (SocketChannel)key.channel
();
ByteBuffer buffer = (ByteBuffer)key.attachment();
// data sent, read again
keyChannel.register( selector, SelectionKey.OP_READ,
buffer );
}
else if( key.isAcceptable() )
{
// Get channel
ServerSocketChannel keyChannel =
(ServerSocketChannel)key.channel();
// accept incoming connection
SocketChannel clientChannel = keyChannel.accept();
// create a client context
ByteBuffer buffer = ByteBuffer.allocateDirect( 1024
);
// register it in the selector
clientChannel.configureBlocking(false);
clientChannel.register( selector,
SelectionKey.OP_READ, buffer );
}
else
{
System.err.println("Ooops");
}
}
}
}
}
I'm benchmarking several server io strategies and for that purpose I've
built two simplistic Java ECHO servers.
One of the server implementation takes advantage of the java.nio API.
However it (my implementation) is slower than the classic 1 thread /
client server. I've managed to find out (thanks to the profiler) that the
accept() function call was slowing down the process. The strange thing is
that I'm calling accept() only when SelectionKey.isAcceptable() and thus
this operation should be fast, right ? Issues ?
To test this behaviour I used a program that sequentially creates N
connections to the server. The first server I wrote used an infinite loop
that accepts sockets from a ServerSocket. The second server I wrote uses
nio, selects on OP_ACCEPT and does a SelectionKey.accept(). This takes
about 10 times longer.
I'd also like to take advantage of multiprocessor architectures and spawn
as many "worker threads" (taken from the IOCP voc.) as there are CPUs
installed. Has anybody done this already ?
Is it good practice to have multiple threads waiting on select() on the
same Selector ?
How can I register a Channel with a selector while one thread is in
Selector.select() and have this thread process incoming events ? What
I've done so far is loop on selects() with a timeout but this surely
isn't good practice.
I'm saddened to see that as I wrote it the 1 thread/client outperforms
the nio one...
Here comes the code :
import java.io.*;
import java.lang.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
public class javaenh
{
public static void main(String args[]) throws Exception
{
// incoming connection channel
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
channel.socket().bind( new InetSocketAddress( 1234 ) );
// Register interest in when connection
Selector selector = Selector.open();
channel.register( selector, SelectionKey.OP_ACCEPT );
System.out.println( "Ready" );
// Wait for something of interest to happen
while( selector.select()>0 )
{
// Get set of ready objects
Iterator readyItor = selector.selectedKeys().iterator();
// Walk through set
while( readyItor.hasNext() )
{
// Get key from set
SelectionKey key = (SelectionKey)readyItor.next();
readyItor.remove();
if( key.isReadable() )
{
// Get channel and context
SocketChannel keyChannel = (SocketChannel)key.channel
();
ByteBuffer buffer = (ByteBuffer)key.attachment();
buffer.clear();
// Get the data
if( keyChannel.read( buffer )==-1 ) {
keyChannel.socket().close();
buffer = null;
} else {
// Send the data
buffer.flip();
keyChannel.write( buffer );
// wait for data to be sent
keyChannel.register( selector,
SelectionKey.OP_WRITE, buffer );
}
}
else if( key.isWritable() )
{
// Get channel and context
SocketChannel keyChannel = (SocketChannel)key.channel
();
ByteBuffer buffer = (ByteBuffer)key.attachment();
// data sent, read again
keyChannel.register( selector, SelectionKey.OP_READ,
buffer );
}
else if( key.isAcceptable() )
{
// Get channel
ServerSocketChannel keyChannel =
(ServerSocketChannel)key.channel();
// accept incoming connection
SocketChannel clientChannel = keyChannel.accept();
// create a client context
ByteBuffer buffer = ByteBuffer.allocateDirect( 1024
);
// register it in the selector
clientChannel.configureBlocking(false);
clientChannel.register( selector,
SelectionKey.OP_READ, buffer );
}
else
{
System.err.println("Ooops");
}
}
}
}
}