1. Probably anyone who has ever worked with serialization of objects, be that in Java or any other language, knows that it should be avoided whenever possible. Just like the first rule of distribution is "Do not distribute!", the first rule of serialization should be "Do not serialize!". However, in many cases, especially in distributed environments, serialization cannot be avoided and therefore must be significantly optimized to achieve any kind of reasonable throughput.

    At GridGain, given the distributed nature of our product, we have always been working on optimizing of our serialization routines, but starting with version 4.3.0 we have achieved the fastest results so far. Our GridOptimizedMarshaller in our tests achieved up to 20x performance optimization on standard Java serialization with java.io.Serializable. If you switch to java.io.Externalizable, then GridGain marshaller is up to 10x faster. We have even compared our marshaller to Kryo serialization, and turns out that our marshaller is up to 5x faster than Kryo. On top of that, the footprint of GridGain serialized objects is significantly smaller than Java.

    The coolest thing here is that we do not require any custom interfaces or API s - GridGain optimized serialization works directly with standard Java POJOs, regardless if they implement java.io.Serializable interface or not. If your POJOs implement java.io.Externalizable, then our marshaling works even faster.

    How do we do it? The main culprit of Java serialization is java.io.ObjectOutputStream which is extremely expensive to initialize and performs poorly. The first thing we did is replaced it with our own implementation, based on direct memory copying by invoking native C and Java so-called "unsafe" routines.  We also serialize fields in predefined order by doing lots of object introspection which allows us to pass only values and not their type names or other metadata.

    Here are the results from the test on my MacBookPro 2.7 GHz Intel i7:
    >>> Java serialization via Externalizable (average): 22,551 ms
    >>> Kryo serialization (average): 17,300 ms
    >>> GridGain serialization (average): 2,937 ms
    Here is the test itself. This test is included with our product, so feel free to download it and try for yourself. 
    public class SerializationBenchmark {
        /** Number of runs. */
        private static final int RUN_CNT = 3;
    
        /** Number of iterations. */
        private static final int ITER_CNT = 200000;
    
        public static void main(String[] args) throws Exception {
            // Create sample object.
            SampleObject obj = createObject();
    
            // Run Java serialization test.
            javaSerialization(obj);
    
            // Run Kryo serialization test.
            kryoSerialization(obj);
    
            // Run GridGain serialization test.
            gridGainSerialization(obj);
        }
    
        private static long javaSerialization(SampleObject obj) throws Exception {
            long avgDur = 0;
    
            for (int i = 0; i < RUN_CNT; i++) {
                SampleObject newObj = null;
    
                long start = System.currentTimeMillis();
    
                for (int j = 0; j < ITER_CNT; j++) {
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
    
                    ObjectOutputStream objOut = null;
    
                    try {
                        objOut = new ObjectOutputStream(out);
    
                        objOut.writeObject(obj);
                    }
                    finally {
                        U.close(objOut, null);
                    }
    
                    ObjectInputStream objIn = null;
    
                    try {
                        objIn = new ObjectInputStream(
                            new ByteArrayInputStream(out.toByteArray()));
    
                        newObj = (SampleObject)objIn.readObject();
                    }
                    finally {
                        U.close(objIn, null);
                    }
                }
    
                long dur = System.currentTimeMillis() - start;
    
                avgDur += dur;
            }
    
            avgDur /= RUN_CNT;
    
            System.out.format("\n>>> Java serialization via Externalizable (average): %,d ms\n\n", avgDur);
    
            return avgDur;
        }
    
        private static long kryoSerialization(SampleObject obj) throws Exception {
            Kryo marsh = new Kryo();
    
            long avgDur = 0;
    
            for (int i = 0; i < RUN_CNT; i++) {
                SampleObject newObj = null;
    
                long start = System.currentTimeMillis();
    
                for (int j = 0; j < ITER_CNT; j++) {
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
    
                    Output kryoOut = null;
    
                    try {
                        kryoOut = new Output(out);
    
                        marsh.writeObject(kryoOut, obj);
                    }
                    finally {
                        U.close(kryoOut, null);
                    }
    
                    Input kryoIn = null;
    
                    try {
                        kryoIn = new Input(new ByteArrayInputStream(out.toByteArray()));
    
                        newObj = marsh.readObject(kryoIn, SampleObject.class);
                    }
                    finally {
                        U.close(kryoIn, null);
                    }
                }
    
                long dur = System.currentTimeMillis() - start;
    
                avgDur += dur;
            }
    
            avgDur /= RUN_CNT;
    
            System.out.format("\n>>> Kryo serialization (average): %,d ms\n\n", avgDur);
    
            return avgDur;
        }
    
        private static long gridGainSerialization(SampleObject obj) throws Exception {
            GridMarshaller marsh = new GridOptimizedMarshaller(false, 
                Arrays.asList(SampleObject.class.getName()), null);
    
            long avgDur = 0;
    
            for (int i = 0; i < RUN_CNT; i++) {
                SampleObject newObj = null;
    
                long start = System.currentTimeMillis();
    
                for (int j = 0; j < ITER_CNT; j++)
                    newObj = marsh.unmarshal(marsh.marshal(obj), null);
    
                long dur = System.currentTimeMillis() - start;
    
                avgDur += dur;
            }
    
            avgDur /= RUN_CNT;
    
            System.out.format("\n>>> GridGain serialization (average): %,d ms\n\n", avgDur);
    
            return avgDur;
        }
    
        private static SampleObject createObject() {
            long[] longArr = new long[3000];
    
            for (int i = 0; i < longArr.length; i++)
                longArr[i] = i;
    
            double[] dblArr = new double[3000];
    
            for (int i = 0; i < dblArr.length; i++)
                dblArr[i] = 0.1 * i;
    
            return new SampleObject(123, 123.456f, (short)321, longArr, dblArr);
        }
    
        private static class SampleObject 
            implements Externalizable, KryoSerializable {
            private int intVal;
            private float floatVal;
            private Short shortVal;
            private long[] longArr;
            private double[] dblArr;
            private SampleObject selfRef;
    
            public SampleObject() {}
    
            SampleObject(int intVal, float floatVal, Short shortVal, 
                long[] longArr, double[] dblArr) {
                this.intVal = intVal;
                this.floatVal = floatVal;
                this.shortVal = shortVal;
                this.longArr = longArr;
                this.dblArr = dblArr;
    
                selfRef = this;
            }
    
            // Required by Java Externalizable.
            @Override public void writeExternal(ObjectOutput out) 
                throws IOException {
                out.writeInt(intVal);
                out.writeFloat(floatVal);
                out.writeShort(shortVal);
                out.writeObject(longArr);
                out.writeObject(dblArr);
                out.writeObject(selfRef);
            }
    
            // Required by Java Externalizable.
            @Override public void readExternal(ObjectInput in) 
             throws IOException, ClassNotFoundException {
                intVal = in.readInt();
                floatVal = in.readFloat();
                shortVal = in.readShort();
                longArr = (long[])in.readObject();
                dblArr = (double[])in.readObject();
                selfRef = (SampleObject)in.readObject();
            }
    
            // Required by Kryo serialization.
            @Override public void write(Kryo kryo, Output out) {
                kryo.writeObject(out, intVal);
                kryo.writeObject(out, floatVal);
                kryo.writeObject(out, shortVal);
                kryo.writeObject(out, longArr);
                kryo.writeObject(out, dblArr);
                kryo.writeObject(out, selfRef);
            }
    
            // Required by Kryo serialization.
            @Override public void read(Kryo kryo, Input in) {
                intVal = kryo.readObject(in, Integer.class);
                floatVal = kryo.readObject(in, Float.class);
                shortVal = kryo.readObject(in, Short.class);
                longArr = kryo.readObject(in, long[].class);
                dblArr = kryo.readObject(in, double[].class);
                selfRef = kryo.readObject(in, SampleObject.class);
            }
        }
    }
    
    31

    View comments

About me
About me
- Antoine de Saint-Exupery -
- Antoine de Saint-Exupery -
"A designer knows he has achieved perfection not when there is nothing left to add, but when there is nothing left to take away."
Blog Archive
Blogs I frequent
Loading
Dynamic Views theme. Powered by Blogger.