org.shaman.database.Benchmark.java Source code

Java tutorial

Introduction

Here is the source code for org.shaman.database.Benchmark.java

Source

/*
 * 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");
        }

    }
}