Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package org.shaman.database; import java.beans.XMLDecoder; import java.beans.XMLEncoder; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import org.apache.commons.io.output.CountingOutputStream; import org.apache.commons.io.output.NullOutputStream; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.*; import org.shaman.database.error.FailOnErrorHandler; import org.shaman.database.io.ArrayOutput; import org.shaman.database.io.Output; import org.shaman.database.io.StreamInput; import org.shaman.database.records.EmptyEntity; import org.shaman.database.records.HugeEntity; import org.shaman.database.records.SerializableContainer; import org.shaman.database.records.SerializableStringMap; import org.shaman.database.records.SmallEntity; /** * Benchmark comparing the file size and save/load times. * It tests three data sets: few huge records, many empty records, a complex scene * with different records. It compares the file size and load/save times * when saved with the database, * with the standard java serialization algorithm (compressed and uncompressed) * and the xml-Exporter of java-beans. * @author Sebastian Wei */ @Ignore //because it is too slow public class Benchmark extends DatabaseTests { public Benchmark() { } /** * runs the benchmark. * @param args not used */ public static void main(String[] args) throws IOException, ClassNotFoundException { DatabaseTests.registerRecords(); new Benchmark().benchmark(); } @Test public void benchmark() throws IOException, ClassNotFoundException { //disable logger Logger logger = Logger.getLogger("org.shaman.database"); Level oldLevel = logger.getLevel(); logger.setLevel(Level.OFF); System.out.println("Benchmark"); long seed = System.currentTimeMillis(); Random rand = new Random(seed); //to reproduce the same results, set seed to another value System.out.println("seed: " + seed); //description System.out.println("This benchmark generates 9 different test record graphs,\n" + "3 with few but huge entities, 2 with many empty entites and 4 with a complex scene"); System.out.println("It tests these cases:"); System.out.println("1. The file size when saved with the databse, the default java serialization,\n" + " the java serialization gzip-compressed and java-beans xml output"); System.out.println("2. The time to save and load the scene using the database, the java serialization,\n" + " the java serialization gzip-compressed and java-beans xml output"); System.out.println( "in the last case, the output streams are buffered using BufferedInputStream / BufferedOutputStream\n" + "and the database uses the default io as used when not excplizit specified"); System.out.println(); //generate scenes Record[] scenes = new Record[] { generateScene1(rand), generateScene1(rand), generateScene1(rand), generateScene2(rand), generateScene2(rand), generateScene3(rand), generateScene3(rand), generateScene3(rand), generateScene3(rand) }; try { //file size System.out.println("file size:"); printFileSizes(scenes); //save/load times printSaveLoadTimes(scenes); } finally { logger.setLevel(oldLevel); } } /** * generates a scene with a few huge entities. * @param rand the random generator * @return the root of this scene */ private Record generateScene1(Random rand) { int size = 10; HugeEntity[] entities = new HugeEntity[size]; for (int i = 0; i < size; i++) { entities[i] = HugeEntity.createRandom(rand); } return new SerializableContainer(entities); } /** * generates a scene with many empty entities * @param rand the random generator * @return the root of this scene. */ private Record generateScene2(Random rand) { int size = rand.nextInt(10000) + 10000; EmptyEntity[] entities = new EmptyEntity[size]; for (int i = 0; i < size; i++) { entities[i] = new EmptyEntity(); } return new SerializableContainer(entities); } /** * generates a complex scenes with many different entites and record links. * @param rand the random generator * @return the root of this scene. */ private Record generateScene3(Random rand) { int size1 = rand.nextInt(50) + 100; //count of HugeEntity int size2 = rand.nextInt(500) + 1000; //count of EmptyEntity int size3 = rand.nextInt(200) + 100; //count of SmallEntity SerializableStringMap<Record> map = new SerializableStringMap<Record>(); HugeEntity[] e1 = new HugeEntity[size1]; for (int i = 0; i < size1; i++) { e1[i] = HugeEntity.createRandom(rand); map.put("huge" + i, e1[i]); } EmptyEntity[] e2 = new EmptyEntity[size2]; for (int i = 0; i < size2; i++) { e2[i] = new EmptyEntity(); map.put("empty" + i, e2[i]); } SmallEntity[] e3 = new SmallEntity[size3]; for (int i = 0; i < size3; i++) { e3[i] = SmallEntity.createRandom(rand); map.put("small" + i, e3[i]); } //connect for (int i = 0; i < size1; i++) { e1[i].setLink(e1[rand.nextInt(size1)]); } map.preSave(); return map; } private final long getTime() { return System.currentTimeMillis(); } private final long getTimeMultiplicator() { return 1; //to multiple to get ms } private void printSaveLoadTimes(Record... roots) throws IOException, ClassNotFoundException { //result array final int subpassCount = 5; long[][] saveTimes = new long[4][roots.length * subpassCount]; long[][] loadTimes = new long[4][roots.length * subpassCount]; DecimalFormat format = new DecimalFormat("0000"); System.out.println("\ntime to save / load (ms):"); System.out.println("passes database serial compressed xml"); System.out.println(" save\\load save\\load save\\load save\\load"); //for every data graph for (int i = 0; i < roots.length; i++) { File f1 = File.createTempFile("benchmark1", ".db"); File f2 = File.createTempFile("benchmark2", ".serial"); File f3 = File.createTempFile("benchmark3", ".serial"); File f4 = File.createTempFile("benchmark4", ".xml"); Record root = roots[i]; //save it multiple times for (int j = 0; j < subpassCount; j++) { int index = i * subpassCount + j; //delete files from previous pass f1.delete(); f2.delete(); f3.delete(); f4.delete(); long time1, time2; //database Database db = new Database(root); time1 = getTime(); db.save(f1, FailOnErrorHandler.INSTANCE); time2 = getTime(); saveTimes[0][index] = time2 - time1; //memory database // time1 = getTime(); // ArrayOutput outp = new ArrayOutput(); // db.save(outp, FailOnErrorHandler.INSTANCE); // outp.writeTo(f1b); // time2 = getTime(); // saveTimes[0][index] = time2-time1; //uncompressed serialization time1 = getTime(); OutputStream out = new BufferedOutputStream(new FileOutputStream(f2)); ObjectOutputStream dos = new ObjectOutputStream(out); dos.writeObject(root); dos.close(); time2 = getTime(); saveTimes[1][index] = time2 - time1; //compressed serialization time1 = getTime(); out = new GZIPOutputStream(new BufferedOutputStream(new FileOutputStream(f3))); dos = new ObjectOutputStream(out); dos.writeObject(root); dos.close(); time2 = getTime(); saveTimes[2][index] = time2 - time1; //xml time1 = getTime(); out = new BufferedOutputStream(new FileOutputStream(f4)); XMLEncoder xml = new XMLEncoder(out); xml.writeObject(root); xml.close(); time2 = getTime(); saveTimes[3][index] = time2 - time1; } //load it multiple times for (int j = 0; j < subpassCount; j++) { int index = i * subpassCount + j; long time1, time2; //database time1 = getTime(); Database db = new Database(f1, FailOnErrorHandler.INSTANCE); time2 = getTime(); if (j == 0) { assertEquals(root, db.getRoot()); } loadTimes[0][index] = time2 - time1; //memory database // time1 = getTime(); // db = new Database(new StreamInput(f1b), FailOnErrorHandler.INSTANCE); // time2 = getTime(); // if (j==0) { // assertEquals(root, db.getRoot()); // } // loadTimes[1][index] = time2-time1; //uncompressed serialization time1 = getTime(); InputStream in = new BufferedInputStream(new FileInputStream(f2)); ObjectInputStream dis = new ObjectInputStream(in); Object o = dis.readObject(); dis.close(); time2 = getTime(); if (j == 0) { if (o instanceof SerializableStringMap) { ((SerializableStringMap) o).postLoad(); } assertEquals(root, o); } loadTimes[1][index] = time2 - time1; //compressed serialization time1 = getTime(); in = new GZIPInputStream(new BufferedInputStream(new FileInputStream(f3))); dis = new ObjectInputStream(in); o = dis.readObject(); dis.close(); time2 = getTime(); if (j == 0) { if (o instanceof SerializableStringMap) { ((SerializableStringMap) o).postLoad(); } assertEquals(root, o); } loadTimes[2][index] = time2 - time1; //xml time1 = getTime(); in = new BufferedInputStream(new FileInputStream(f4)); XMLDecoder xml = new XMLDecoder(in); o = xml.readObject(); in.close(); time2 = getTime(); if (j == 0) { if (o instanceof SerializableStringMap) { ((SerializableStringMap) o).postLoad(); } assertEquals(root, o); } loadTimes[3][index] = time2 - time1; } //delete files f1.delete(); f2.delete(); f3.delete(); f4.delete(); //print this pass for (int j = 0; j < subpassCount; j++) { int index = i * subpassCount + j; System.out.print("pass " + (i + 1) + '\\' + (j + 1) + " "); for (int h = 0; h < 4; h++) { //System.out.printf(format4, saveTimes[h][index]*getTimeMultiplicator()); System.out.print(format.format(saveTimes[h][index] * getTimeMultiplicator())); System.out.print('\\'); //System.out.printf(format4, loadTimes[h][index]*getTimeMultiplicator()); System.out.print(format.format(loadTimes[h][index] * getTimeMultiplicator())); System.out.print(" "); } System.out.println(); } } } /** * prints the file size * @param roots the roots from the different passes */ private void printFileSizes(Record... roots) throws IOException { //result array int size; String format = "%1$,11d"; System.out.println("passes database serial compressed xml"); //save it for (int i = 0; i < roots.length; i++) { Record root = roots[i]; System.out.print("pass " + (i + 1) + " "); //database ArrayOutput ao = new ArrayOutput(); Database db = new Database(root); db.save(ao, FailOnErrorHandler.INSTANCE); size = ao.getTotalSize(); System.out.printf(format, size); System.out.print("B "); //uncompressed serialization CountingOutputStream out = new CountingOutputStream(new NullOutputStream()); ObjectOutputStream dos = new ObjectOutputStream(out); dos.writeObject(root); dos.flush(); size = out.getCount(); System.out.printf(format, size); System.out.print("B "); //compressed serialization out.resetCount(); dos = new ObjectOutputStream(new GZIPOutputStream(out)); dos.writeObject(root); dos.flush(); size = out.getCount(); System.out.printf(format, size); System.out.print("B "); //xml out.resetCount(); XMLEncoder xml = new XMLEncoder(out); xml.writeObject(root); xml.flush(); size = out.getCount(); System.out.printf(format, size); System.out.println("B"); } } }