TCP UDP and RMI Performance Evaluation

22 Mar 2011 by

TCP UDP and RMI Throughput

TCP UDP and RMI Throughput

It is interesting to see the performance evaluation of TCP, UDP and RMI. This article shows the simple scenario to test their performance by looking at latency and throughput. The test result shows that RMI has poor performance compares to TCP and UDP. TCP and UDP has comparable result though UDP gains slightly higher than TCP.

From this work, I can draw conclusion that RMI is easy to implement the function based program but it is not good for intensive exchange packets. UDP and TCP are good for packet transmissions but they are hard to implement protocol. Though UDP has less overhead, it is not guarantee the packet delivery. At last, TCP is good for almost scenario with good administration.

Introduction

TCP and UDP are the modest transport layer protocols that has dominant today inter-network. RMI is middleware which relies on unicast TCP protocol. Please read Java RMI Overview and How to Implement Java RMI? for more detail.

Methodology

We develop a simple client server program in Java. Client sends 100000 messages to server with the size of 10, 15, 20, 25, 30, 35 kilobytes respectively. It is to be sure that the network is under saturated condition. We run 5 times for each case. TCP and RMI is very reliable protocol. There is no message lost in these 2 protocol. However, UDP does not responsible for message delivery, we see some failure messages that need to be re-sent. In LAN, UDP transmission is very fast which causes a lot of error in receiver side. So to reduce some error, we ask the sender to wait for 1 nano-second for every sending. One nano-second is very small amount that can be negligible. We also consider about re-sending mechanism in UDP for any packet that does not receive or receive with error.

Experimental Environment

Client

Parameter Value
Operating Systems Ubuntu Linux 10.10
Java OpenJDK 1.6
CPU Intel Core 2 Duo 2.66GHz
RAM 2GB

Server

Parameter Value
Operating Systems Mac OS X v10.6
Java Sun JDK 1.6
CPU Intel Core 2 Duo 2.53GHz
RAM 4GB
 

Result

TCP UDP and RMI Latency

TCP UDP and RMI Latency

The result reveals that RMI takes much longer than TCP and UDP. It is probably because of the stub, skeleton and RMI registry service. In order to call remote methods on the server, RMI requires almost 10 steps as shown in Remote Procedure Call and Java RMI Overview article.

UDP is slightly better than TCP in term of latency. Even though UDP does not guarantee the delivery of the packets, in LAN we investigate that not so many packets have been lost. Moreover, UDP has less overhead since there is no Acknowledgement mechanism.

TCP UDP and RMI Throughput

TCP UDP and RMI Throughput

We also shows the throughput comparison between the 3 protocols. We calculate throughput by dividing total received packet size by duration.

Because RMI takes longer to complete, the throughput also lower compare to TCP and UDP.

 

Conclusion

RMI is easy to implement large scale distributed systems but if the system requires a lot of exchanging messages, then it is not recommended. Even though UDP slightly performs better than TCP, it is not recommended for accurately demand application. It is better for live conference, multimedia, discovery services etc. TCP, on the other hand, is good if we deal with a lot of exchanging messages.

Appendix – Code

UDP Program

Client

import java.io.IOException;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;

/**
 *
 * @author http://lycog.com
 */
public class UDPPerformanceClient {
  public static void main(String[] args){
    try{
      int n = 5;
      for(int num=0;num<5;num++){
        System.out.println("----------------------");
        n = n + 5;
        for(int loop=0;loop<5;loop++){
          DatagramSocket socket = new DatagramSocket();
          InetAddress serverIp = InetAddress.getByName("192.168.40.190");
          int PORT = 8888;
          int transmittedTime = 100000;
          int count = 0;
          byte[] byteMsg = new byte[1024*n];
          byte[] command;

          System.out.println("n=" + n + " loop=" + loop);
          command = ("" + n).getBytes();
          sendPacket(socket, command, serverIp, PORT);

          long startTime = System.nanoTime();
          while(true){
            for(int i=count; i<transmittedTime; i++){
              sendPacket(socket, byteMsg, serverIp, PORT);
              sleep(0);
            }
            //Send notification whether it is complete
            System.out.println("Send check count command");
            command = "count".getBytes();
            sendPacket(socket, command, serverIp, PORT);
            sleep(0);

            System.out.println("Receiving count...");
            String strCount = receivePacket(socket);
            count = Integer.parseInt(strCount);
            System.out.println("Count = " + count);
            if(count==transmittedTime) {
              //Send quit command to terminate
              System.out.println("Send quits command");
              byte[] quit = "quit".getBytes();
              sendPacket(socket, quit, serverIp, PORT);
              sleep(0);
              break;
            }
          }

          long stopTime = System.nanoTime();

          System.out.println("Duration = " + (stopTime-startTime));
        }
      }
    }catch(IOException ioe){
      ioe.printStackTrace();
    }
  }

  private static String receivePacket(DatagramSocket socket){
    byte[] byteRecv = new byte[1024];
    String msg = "";
    try{
      DatagramPacket inPacket = new DatagramPacket(byteRecv,
              0, byteRecv.length);
      socket.receive(inPacket);
      msg = new String(byteRecv, 0, inPacket.getLength());
    }catch(IOException ioe){
      ioe.printStackTrace();
    }
    return msg;
  }

  private static void sendPacket(DatagramSocket socket,
          byte[] msg, InetAddress ip, int port){

    try{
      DatagramPacket outPacket = new DatagramPacket(msg,
              0, msg.length, ip, port);
      socket.send(outPacket);
    }catch(IOException ioe){
      ioe.printStackTrace();
    }
  }

  private static void sleep(int millisecond){
    try {
      Thread.sleep(millisecond, 1);
    } catch (InterruptedException ie) {
      ie.printStackTrace();
    }
  }
}

Server

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 *
 * @author http://lycog.com
 */
public class UDPPerformanceServer {
  public static void main(String[] args){
    try{
      DatagramSocket server = new DatagramSocket(8888);
      DatagramPacket inPacket = null;
      DatagramPacket outPacket = null;
      int count = 0;
      int n = 0;
      byte[] command;
      String strCommand;
      int clientPort;
      InetAddress clientIp;
      while(true){
        System.out.println("Waiting for client...");
        command = new byte[256];
        inPacket = new DatagramPacket(command, 0, command.length);
        server.receive(inPacket);
        strCommand = new String(command, 0, inPacket.getLength());
        n = Integer.parseInt(strCommand);
        //get client port
        clientPort = inPacket.getPort();
        //get client IP address
        clientIp = inPacket.getAddress();
        System.out.println("Receiving byte = " + n + "*1024");

        while(true){
          byte[] inBuf = new byte[1024*n];
          inPacket = new DatagramPacket(inBuf, inBuf.length);
          server.receive(inPacket);

          //Check if it is correct size
          if(inPacket.getLength()==(n*1024)){
            count++;
          }
          String msg = new String(inPacket.getData(), 0, inPacket.getLength());

          //If client requests for received packets
          if(msg.equals("count")){
            command = ("" + count).getBytes();
            sendPacket(server, command, clientIp, clientPort);
            System.out.println("Number of receiving packet = " + count);
            sleep(0);
          }

          //If client requests termination
          if(msg.equals("quit")) {
            count = 0;
            System.out.println("client terminated");
            System.out.println("-------------------------");
            break;
          }
        }
      }
    }catch(IOException ioe){
      ioe.printStackTrace();
    }
  }

  private static void sendPacket(DatagramSocket socket,
          byte[] msg, InetAddress ip, int port){
    try{
      DatagramPacket outPacket = new DatagramPacket(msg,
              0, msg.length, ip, port);
      socket.send(outPacket);
    }catch(IOException ioe){
      ioe.printStackTrace();
    }
  }

  private static void sleep(int millisecond){
    try {
      Thread.sleep(millisecond, 1);
    } catch (InterruptedException ie) {
      ie.printStackTrace();
    }
  }
}

—————————————————————————————————————-

TCP Program

Client

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;

/**
 *
 * @author http://lycog.com
 */
public class TCPTransmissionPerformanceClient {
  public static void main(String[] args){
    try{
      int transmittedTime = 100000;
      int n = 5;
      //Vary the message size
      for(int num=0;num<5;num++){
        n = n + 5;

        //Repeat the same size for 5 times
        for(int loop=0;loop<5;loop++){
          //Connect to server
          Socket socket = new Socket("192.168.40.190", 8888);

          PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);

          //Prepare message size to be transmitted
          byte[] byteMsg = new byte[1024*n];
          String strMsg = new String(byteMsg);

          System.out.println("Start sending messages");
          System.out.println("n=" + n + " loop=" + loop);
          long startTime = System.nanoTime();
          for(int i=0; i<transmittedTime; i++){
            pw.println(strMsg);
          }
          pw.println("quit");
          long endTime = System.nanoTime();

          System.out.println("Duration = " + (endTime-startTime));
        }
      }
    }catch(IOException ioe){
      ioe.printStackTrace();
    }
  }
}

Server

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 *
 * @author http://lycog.com
 */
public class TCPTransmissionPerformanceServer {
  public static void main(String[] args){
    try{
      ServerSocket server = new ServerSocket(8888);

      while(true){
        System.out.println("Server starts listening...");
        Socket socket = server.accept();
        BufferedReader br = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));

        String msg = "";
        while(true){
          msg = br.readLine();
          if(msg.equals("quit")) break;
        }
      }
    }catch(IOException ioe){
      ioe.printStackTrace();
    }
  }
}

—————————————————————————————————————-

RMI Program

Step 1: Interface

import java.rmi.Remote;
import java.rmi.RemoteException;
/**
 *
 * @author http://lycog.com
 */
public interface IMessage extends Remote{
  void captureMessage(byte[] message) throws RemoteException;
}

Step 2: Implement interface

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
 *
 * @author http://lycog.com
 */
public class MessageImplementation extends UnicastRemoteObject
                implements IMessage
{
  public MessageImplementation() throws RemoteException{

  }

  public void captureMessage(byte[] message){
    //Hold receiving message
    byte[] msg = message;
  }
}

Step 3: Create server and registry object

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

/**
 *
 * @author http://lycog.com
 */
public class MessageServer {
  public static void main(String[] args){
    try{
      //Declare message object
      MessageImplementation msgObject =
              new MessageImplementation();

      //Create and get reference from registry
      Registry registry = LocateRegistry.createRegistry(1099);

      //Register message object
      registry.rebind("Message", msgObject);

      System.out.println("Server starts....");
    }catch(RemoteException re){
      re.printStackTrace();
    }
  }
}

Step 4: Client Program

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

/**
 *
 * @author http://lycog.com
 */
public class MessageClient {
  public static void main(String[] args){
    try{
      int n = 0;
      int transmittedTime = 100000;
      for(int num=0;num<5;num++){
        n = n + 2;
        for(int loop=0;loop<5;loop++){
          //Get reference from registry
          Registry registry = LocateRegistry.getRegistry("192.168.40.190", 1099);
          //Lookup message object from server
          IMessage msgObject = (IMessage)registry.lookup("Message");

          System.out.println("n=" + n + " loop=" + loop);
          byte[] message = new byte[1024*n];
          System.out.println("Start sending....");
          long startTime = System.nanoTime();
          for(int i=0;i<transmittedTime;i++){
            msgObject.captureMessage(message);
          }
          msgObject.captureMessage("quit".getBytes());
          long stopTime = System.nanoTime();

          System.out.println("Duration = " + (stopTime - startTime));
        }
      }
    }catch(NotBoundException nbe){
      nbe.printStackTrace();
    }catch(RemoteException re){
      re.printStackTrace();
    }
  }
}

 

6 responses so far

  • Mackeeper says:

    The issue of my own Mac throughout the time period of continuous and effective use was that the quantity of application is increasing inside the totally unacceptable percentage. The easiest way using this scenario would be installing a brand new as well as efficient solution which could integrate the whole levels of essential program intone and therefore would allow to end issues fast sufficient reason for no additional difficulties.

  • atul shukla says:

    good work keep it up

  • midiman says:

    It should be mentioned that that comparison with RMI is a little unfair – RMI is an application protocol running on top of TCP (can be SSL TCP).
    While it’s true rmi isn’t really meant for high traffic exchange – but if you tried to do this, with, say, HTTPS (or, if you’re a real glutton for punishment, DCOM), you’d run into the same problems.
    Comparing RMI with TCP/UDP is like comparing apples and fruit bowls.

  • anhdt84 says:

    I have a scenario like this:
    – 2 java applications deployed on 2 JVM( 2 seperate machines on network)
    – I want to call remote method from JVM1 to JVM2( including passing serializion data)
    RMI make me feel easy to do that but low performace make me worry( about < 800 TPS in my case)
    Could you please give me some advices to resolve my problem??
    I find http://dev.root1.de/projects/simon is same with RMI but I'm not sure
    Thanks and best regards.My scenario is

  • anhdt84 says:

    I have a scenario like this:
    – 2 java applications deployed on 2 JVM( 2 seperate machines on network)
    – I want to call remote method from JVM1 to JVM2( including passing serializion data)
    RMI make me feel easy to do that but low performace make me worry( about < 800 TPS in my case)
    Could you please give me some advices to resolve my problem??
    I find http://dev.root1.de/projects/simon is same with RMI but I'm not sure
    Thanks and best regards.

  • You are not testing the same thing!

    RMI waits for a response from the server side before next call while UDP and TCP is just pushing data as fast as it can to the local buffer.

    See http://werner.yellowcouch.org/Lectures/javarmi/javarmi-logical.png

    You should update the TCP and UDP tests to wait for a response from the server to get comparable results.

Leave a Reply