I'm trying to do local IPC using Sockets and Object streams in Java however I'm seeing poor performance.
I am testing the ping time of sending an object via an ObjectOutputStream to receiving a reply via an ObjectInputStream over a Socket.
Here's the Requestor:
public SocketTest(){
int iterations = 100;
try {
Socket socket = new Socket("localhost", 1212);
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
double start = System.currentTimeMillis();
for (int i = 0; i < iterations; ++i) {
Request request = new Request();
objectOutputStream.writeObject(request);
Response response = (Response)objectInputStream.readObject();
}
double finish = System.currentTimeMillis();
System.out.println("Per ping: " + (finish - start) / iterations );
} catch (Exception e) {
e.printStackTrace();
}
}
Here's the responder:
public ServerSocketTest(){
try {
ServerSocket serverSocket = new ServerSocket(1212);
Socket socket = serverSocket.accept();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
Request request = (Request)objectInputStream.readObject();
while (request != null) {
Response response = new Response();
objectOutputStream.writeObject(response);
request = (Request)objectInputStream.readObject();
}
} catch (Exception e) {
e.printStackTrace();
}
}
The result I'm getting is:
Per ping: 80.35
80 msec is far to slow for local traffic.
Request and Response classes are very small and their serialisation is fast.
I have tried naively adding:
socket.setKeepAlive(true);
socket.setTcpNoDelay(true);
with little effect.
performing a ping localhost:
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=0 ttl=64 time=0.035 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=2 ttl=64 time=0.049 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=3 ttl=64 time=0.039 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=4 ttl=64 time=0.056 ms
is also fast.
Java version 1.6.0_05l Running on RedHat 2.4
I would expect that you have to call
objectOutputStream.flush()
on both sides to ensure that the data is immediately sent to the network. Otherwise, the TCP stack may wait a while for more data to fill a partial IP packet.Have you tried embedding both reques ts and responses in BufferedInputStream/BufferedOutputStream ? It should widely improve performances.
So construct the BufferedOutputStream & flush it before constructing the BufferedInputStream. To avoid hanging.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4788782
it says as per documentation
In addition to using Buffered streams and calling flush() before every read, you should also create the ObjectOutputStream first at both ends. Also, testing for readObject() returning null is pointless unless you're planning to call writeObject(null). The test for EOS with readObject() is catch (EOFException exc).
This seems like something of a micro-benchmark. They're always hard to get right, but I think if you start by sending say 2000 messages before starting your latency measurements.
Also, see this detailed answer on how to do micro-benchmarks right.