Increase Java Serialization Performance
Object Serialization Performance
Let's measure the performance of sending Java Object
s over a network via Java IO and Object
serialization. We'll do this in steps to compare.
—Object Serialization, No Buffered IO:
The first test will be to run this application without buffered IO, so the Listener
code is modified slightly:
ObjectInputStream ois = new ObjectInputStream( client.getInputStream() ); Message msg = (Message)ois.readObject();
For the Sender
:
ObjectOutputStream oos = new ObjectOutputStream( sender.getOutputStream() ); Message msg = new Message(); ... oos.writeObject(msg); oos.flush();
The results on my computer, when sending and receiving 100,000 Object
s this way is ~43,000 objects / second.
—Object Serialization, With Buffered IO:
Adding buffered IO (as shown in the code snippets earlier) the results improved dramatically to more than 100,000 objects / second.
Further Improvement?
This test shows that buffered IO helps improve Object
serialization, but is that the best we can do? The answer is no: Object serialization over a network using this approach is not the most efficient method. There's a better way, which is to use a DataStream
. With this approach, most of the code in this example remains the same, except we won't send the actual Java Object
over the network. Instead, we'll send its data fields, as shown in the new Sender
class run()
method:
public void run() { try { Socket sender = new Socket("localhost", 8081); if ( sender != null && sender.isConnected() ) { DataOutputStream dos = new DataOutputStream( new BufferedOutputStream( sender.getOutputStream() )); Message msg = new Message(); msg.active = true; msg.userid = messages; msg.username = "User_" + messages; msg.data = this.toString(); msg.type = Message.MESSAGE_TYPE_USER; dos.writeInt(msg.type); dos.writeBoolean(msg.active); dos.writeUTF(msg.username); dos.writeInt(msg.userid); dos.writeUTF(msg.data); dos.flush(); } } catch ( Exception e ) { e.printStackTrace(); } }
The first change is to use a DataOutputStream
object in place of ObjectOutputStream
. Each field of the Object
is written to the stream via calls that match their type; i.e., writeInt()
for an Integer
, writeUTF()
for a String
, and so on.
Corresponding changes must be made on the other side, within the Listener
class:
public void run() { // Wait for messages from client try { DataInputStream dis = new DataInputStream( new BufferedInputStream(client.getInputStream())); Message msg = new Message(); msg.type = dis.readInt(); msg.active = dis.readBoolean(); msg.username = dis.readUTF(); msg.userid = dis.readInt(); msg.data = dis.readUTF(); } catch ( Exception e ) { e.printStackTrace(); } }
Here, a DataInputStream
is used, and the fields of the Message
object are filled via matching calls to read from the stream. Since the bytes are sent in the order they're written, they must be read in the same order. Therefore, the fields must be read from the stream in the same order they were written to the stream. Let's see how these results affect performance.
Data Stream Performance
When using the DataOutputStream
and DataInputStream
approach to send Object
data over a network, performance improves dramatically. The results on my computer, when sending and receiving 100,000 Object
s this way is almost 300,000 objects / second. This is about a 3x improvement over the buffered IO example of Object
serialization earlier! Although you lose a little flexibility, the trade-off with this approach is well worth it.
Conclusion
This example serves to illustrate the varying methods of sending data across Java Thread
s, and even Java applications over a network, using Java IO, along with the performance. It also shows how a little experimentation can lead to big performance gains.
Happy coding!
-EJB