/* * Copyright 2010 Martin Grotzke * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package de.javakaffee.web.msm.serializer; import java.util.Calendar; import java.util.concurrent.atomic.AtomicInteger; import org.apache.catalina.core.StandardContext; import org.apache.catalina.loader.WebappLoader; import de.javakaffee.web.msm.JavaSerializationTranscoder; import de.javakaffee.web.msm.MemcachedBackupSession; import de.javakaffee.web.msm.MemcachedBackupSessionManager; import de.javakaffee.web.msm.SessionAttributesTranscoder; import de.javakaffee.web.msm.TranscoderService; import de.javakaffee.web.msm.serializer.TestClasses.Address; import de.javakaffee.web.msm.serializer.TestClasses.Component; import de.javakaffee.web.msm.serializer.TestClasses.Person; import de.javakaffee.web.msm.serializer.TestClasses.Person.Gender; import de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoder; import de.javakaffee.web.msm.serializer.kryo.KryoTranscoder; /** * A simple benchmark for existing serialization strategies. * * @author <a href="mailto:martin.grotzke@javakaffee.de">Martin Grotzke</a> */ public class Benchmark { /* * 50000: * -- JavaSerializationTranscoder -- Serializing 1000 sessions took 156863 msec. serialized size is 59016 bytes. -- JavolutionTranscoder -- Serializing 1000 sessions took 251870 msec. serialized size is 138374 bytes. -- KryoTranscoder -- Serializing 1000 sessions took 154816 msec. serialized size is 70122 bytes. */ public static void main( final String[] args ) throws InterruptedException { //Thread.sleep( 1000 ); final MemcachedBackupSessionManager manager = createManager(); // some warmup // final int warmupCycles = 100000; final int warmupCycles = 100; warmup( manager, new KryoTranscoder(), warmupCycles, 100, 3 ); warmup( manager, new JavaSerializationTranscoder(), warmupCycles, 100, 3 ); warmup( manager, new JavolutionTranscoder( Thread.currentThread().getContextClassLoader(), false ), warmupCycles, 100, 3 ); recover(); benchmark( manager, 10, 500, 4 /* 4^4 = 256 */ ); benchmark( manager, 10, 100, 3 /* 3^3 = 27 */ ); benchmark( manager, 10, 10, 2 /* 2^2 = 4 */ ); // Thread.sleep( Integer.MAX_VALUE ); } private static void benchmark( final MemcachedBackupSessionManager manager, final int rounds, final int countPersons, final int nodesPerEdge ) throws InterruptedException { final Stats kryoSerStats = new Stats(); final Stats kryoDeSerStats = new Stats(); benchmark( manager, new KryoTranscoder(), kryoSerStats, kryoDeSerStats, rounds, countPersons, nodesPerEdge ); final Stats javaSerStats = new Stats(); final Stats javaDeSerStats = new Stats(); benchmark( manager, new JavaSerializationTranscoder(), javaSerStats, javaDeSerStats, rounds, countPersons, nodesPerEdge ); recover(); final Stats javolutionSerStats = new Stats(); final Stats javolutionDeSerStats = new Stats(); benchmark( manager, new JavolutionTranscoder( Thread.currentThread().getContextClassLoader(), false ), javolutionSerStats, javolutionDeSerStats, rounds, countPersons, nodesPerEdge ); recover(); System.out.println( "Serialization,Size,Ser-Min,Ser-Avg,Ser-Max,Deser-Min,Deser-Avg,Deser-Max"); System.out.println( toCSV( "Java", javaSerStats, javaDeSerStats ) ); System.out.println( toCSV( "Javolution", javolutionSerStats, javolutionDeSerStats ) ); System.out.println( toCSV( "Kryo", kryoSerStats, kryoDeSerStats ) ); } private static String toCSV( final String name, final Stats serStats, final Stats deSerStats ) { return name + "," + serStats.size +","+ minAvgMax( serStats ) + "," + minAvgMax( deSerStats ); } private static String minAvgMax( final Stats stats ) { return stats.min +","+ stats.avg +","+ stats.max; } private static void recover() throws InterruptedException { Thread.sleep( 200 ); System.gc(); Thread.sleep( 200 ); } private static void benchmark( final MemcachedBackupSessionManager manager, final SessionAttributesTranscoder transcoder, final Stats serializationStats, final Stats deserializationStats, final int rounds, final int countPersons, final int nodesPerEdge ) throws InterruptedException { System.out.println( "Running benchmark for " + transcoder.getClass().getSimpleName() + "..." + " (rounds: "+ rounds +", persons: "+ countPersons +", nodes: "+ ((int)Math.pow( nodesPerEdge, nodesPerEdge ) + nodesPerEdge + 1 ) +")" ); final TranscoderService transcoderService = new TranscoderService( transcoder ); final MemcachedBackupSession session = createSession( manager, "123456789abcdefghijk987654321", countPersons, nodesPerEdge ); final byte[] data = transcoderService.serialize( session ); final int size = data.length; for( int r = 0; r < rounds; r++ ) { final long start = System.currentTimeMillis(); for( int i = 0; i < 500; i++ ) { transcoderService.serialize( session ); } serializationStats.registerSince( start ); serializationStats.setSize( size ); } System.gc(); Thread.sleep( 100 ); // deserialization for( int r = 0; r < rounds; r++ ) { final long start = System.currentTimeMillis(); for( int i = 0; i < 500; i++ ) { transcoderService.deserialize( data, manager ); } deserializationStats.registerSince( start ); deserializationStats.setSize( size ); } } private static void warmup( final MemcachedBackupSessionManager manager, final SessionAttributesTranscoder transcoder, final int loops, final int countPersons, final int nodesPerEdge ) throws InterruptedException { final TranscoderService transcoderService = new TranscoderService( transcoder ); final MemcachedBackupSession session = createSession( manager, "123456789abcdefghijk987654321", countPersons, nodesPerEdge ); System.out.print("Performing warmup for serialization using "+ transcoder.getClass().getSimpleName() +"..."); final long serWarmupStart = System.currentTimeMillis(); for( int i = 0; i < loops; i++ ) transcoderService.serialize( session ); System.out.println(" (" + (System.currentTimeMillis() - serWarmupStart) + " ms)"); System.out.print("Performing warmup for deserialization..."); final byte[] data = transcoderService.serialize( session ); final long deserWarmupStart = System.currentTimeMillis(); for( int i = 0; i < loops; i++ ) transcoderService.deserialize( data, manager ); System.out.println(" (" + (System.currentTimeMillis() - deserWarmupStart) + " ms)"); } private static MemcachedBackupSession createSession( final MemcachedBackupSessionManager manager, final String id, final int countPersons, final int countNodesPerEdge ) { final MemcachedBackupSession session = manager.createEmptySession(); session.setId( id ); session.setValid( true ); session.setAttribute( "stringbuffer", new StringBuffer( "<string\n&buffer/>" ) ); session.setAttribute( "stringbuilder", new StringBuilder( "<string\n&buffer/>" ) ); session.setAttribute( "persons", createPersons( countPersons ) ); session.setAttribute( "mycontainer", new TestClasses.MyContainer() ); session.setAttribute( "component", createComponents( countNodesPerEdge ) ); return session; } private static Component createComponents( final int countNodesPerEdge ) { final Component root = new Component( "root" ); for ( int i = 0; i < countNodesPerEdge; i++ ) { final Component node = new Component( "child" + i ); addChildren( node, countNodesPerEdge ); root.addChild( node ); } return root; } private static void addChildren( final Component node, final int count ) { for ( int i = 0; i < count; i++ ) { node.addChild( new Component( node.getName() + "-" + i ) ); } } private static Person[] createPersons( final int countPersons ) { final Person[] persons = new Person[countPersons]; for( int i = 0; i < countPersons; i++ ) { final Calendar dateOfBirth = Calendar.getInstance(); dateOfBirth.set( Calendar.YEAR, dateOfBirth.get( Calendar.YEAR ) - 42 ); final Person person = TestClasses.createPerson( "Firstname" + i + " Lastname" + i, i % 2 == 0 ? Gender.FEMALE : Gender.MALE, dateOfBirth, "email" + i + "-1@example.org", "email" + i + "-2@example.org", "email" + i + "-3@example.org" ); person.addAddress( new Address( "route66", "123456", "sincity", "sincountry" ) ); if ( i > 0 ) { person.addFriend( persons[i - 1] ); } persons[i] = person; } return persons; } private static MemcachedBackupSessionManager createManager() { final MemcachedBackupSessionManager manager = new MemcachedBackupSessionManager(); final StandardContext container = new StandardContext(); manager.setContainer( container ); final WebappLoader webappLoader = new WebappLoader() { /** * {@inheritDoc} */ @Override public ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); } }; manager.getContainer().setLoader( webappLoader ); return manager; } static class Stats { long min; long max; double avg; int size; private boolean _first = true; private final AtomicInteger _count = new AtomicInteger(); /** * A utility method that calculates the difference of the time * between the given <code>startInMillis</code> and {@link System#currentTimeMillis()} * and registers the difference via {@link #register(long)}. * @param startInMillis the time in millis that shall be subtracted from {@link System#currentTimeMillis()}. */ public void registerSince( final long startInMillis ) { register( System.currentTimeMillis() - startInMillis ); } public void setSize( final int size ) { this.size = size; } /** * Register the given value. * @param value the value to register. */ public void register( final long value ) { if ( value < min || _first ) { min = value; } if ( value > max || _first ) { max = value; } avg = ( avg * _count.get() + value ) / _count.incrementAndGet(); _first = false; } /** * Returns a string array with labels and values of count, min, avg and max. * @return a String array. */ public String[] getInfo() { return new String[] { "Count = " + _count.get(), "Min = "+ min, "Avg = "+ avg, "Max = "+ max }; } } }