io.usethesource.criterion.CalculateFootprintsHeterogeneous.java Source code

Java tutorial

Introduction

Here is the source code for io.usethesource.criterion.CalculateFootprintsHeterogeneous.java

Source

/**
 * Copyright (c) Michael Steindorfer <Centrum Wiskunde & Informatica> and Contributors.
 * All rights reserved.
 *
 * This file is licensed under the BSD 2-Clause License, which accompanies this project
 * and is available under https://opensource.org/licenses/BSD-2-Clause.
 */
package io.usethesource.criterion;

import java.lang.annotation.Annotation;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.math.BigInteger;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.gs.collections.impl.map.mutable.primitive.IntIntHashMap;
import com.pholser.junit.quickcheck.generator.ComponentizedGenerator;
import com.pholser.junit.quickcheck.generator.GenerationStatus;
import com.pholser.junit.quickcheck.generator.Generator;
import com.pholser.junit.quickcheck.generator.Size;
import com.pholser.junit.quickcheck.generator.java.lang.IntegerGenerator;
import com.pholser.junit.quickcheck.internal.GeometricDistribution;
import com.pholser.junit.quickcheck.internal.generator.SimpleGenerationStatus;
import com.pholser.junit.quickcheck.random.SourceOfRandomness;
import gnu.trove.map.hash.TIntIntHashMap;
import io.usethesource.capsule.SetMultimap;
import io.usethesource.capsule.api.TernaryRelation;
import io.usethesource.capsule.api.Triple;
import io.usethesource.capsule.core.PersistentTrieMap;
import io.usethesource.capsule.experimental.heterogeneous.TrieMap_5Bits_Heterogeneous_BleedingEdge;
import io.usethesource.capsule.experimental.specialized.TrieSet_5Bits_Spec0To8_IntKey;
import io.usethesource.capsule.generators.TripleGenerator;
import io.usethesource.capsule.generators.multimap.AbstractSetMultimapGenerator;
import io.usethesource.capsule.generators.multimap.SetMultimapGenerator_HCHAMP;
import io.usethesource.capsule.generators.relation.AbstractTernaryRelationGenerator;
import io.usethesource.capsule.generators.relation.BidirectionalTrieSetMultimapGenerator;
import io.usethesource.capsule.generators.relation.TernaryTrieSetMultimapGenerator;
import io.usethesource.criterion.BenchmarkUtils.Archetype;
import io.usethesource.criterion.BenchmarkUtils.DataType;
import io.usethesource.criterion.BenchmarkUtils.ValueFactoryFactory;
import io.usethesource.criterion.FootprintUtils.MemoryFootprintPreset;
import io.usethesource.criterion.api.JmhValue;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import objectexplorer.ObjectGraphMeasurer.Footprint;
import org.apache.mahout.math.map.OpenIntIntHashMap;

import static io.usethesource.capsule.util.collection.AbstractSpecialisedImmutableMap.entryOf;
import static io.usethesource.criterion.BenchmarkUtils.ValueFactoryFactory.VF_CHAMP_MULTIMAP_HCHAMP;
import static io.usethesource.criterion.BenchmarkUtils.ValueFactoryFactory.VF_CHAMP_MULTIMAP_HHAMT;
import static io.usethesource.criterion.BenchmarkUtils.ValueFactoryFactory.VF_CHAMP_MULTIMAP_HHAMT_SPECIALIZED;
import static io.usethesource.criterion.BenchmarkUtils.ValueFactoryFactory.VF_CLOJURE;
import static io.usethesource.criterion.BenchmarkUtils.ValueFactoryFactory.VF_DEXX;
import static io.usethesource.criterion.BenchmarkUtils.ValueFactoryFactory.VF_VAVR;
import static io.usethesource.criterion.BenchmarkUtils.ValueFactoryFactory.VF_PCOLLECTIONS;
import static io.usethesource.criterion.BenchmarkUtils.ValueFactoryFactory.VF_SCALA;
import static io.usethesource.criterion.BenchmarkUtils.ValueFactoryFactory.VF_PAGURO;
import static io.usethesource.criterion.BenchmarkUtils.seedFromSizeAndRun;
import static io.usethesource.criterion.FootprintUtils.MemoryFootprintPreset.DATA_STRUCTURE_OVERHEAD;
import static io.usethesource.criterion.FootprintUtils.MemoryFootprintPreset.RETAINED_SIZE_WITH_BOXED_INTEGER_FILTER;
import static io.usethesource.criterion.FootprintUtils.createExponentialRange;
import static io.usethesource.criterion.FootprintUtils.rangeExclusive;
import static io.usethesource.criterion.FootprintUtils.rangeInclusive;
import static io.usethesource.criterion.FootprintUtils.writeToFile;

public final class CalculateFootprintsHeterogeneous {

    static final String memoryArchitecture;

    static {
        /*
         * http://stackoverflow.com/questions/1518213/read-java-jvm-startup-parameters-eg-xmx
         */
        RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();
        List<String> args = bean.getInputArguments();

        if (args.contains("-XX:-UseCompressedOops")) {
            memoryArchitecture = "64bit";
        } else {
            memoryArchitecture = "32bit";
        }
    }

    private static int multimapValueSize = 2;

    private static int stepSizeOneToOneSelector = 2;

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        //    final String userHome = System.getProperty("user.home");
        //    final String userHomeRelativePath = "Research/datastructures-for-metaprogramming/hamt-heterogeneous/data";
        //    final Path directoryPath = Paths.get(userHome, userHomeRelativePath);

        final Path directoryPath = Paths.get(".", "target").toAbsolutePath().normalize();

        final boolean appendToFile = false;

        final int numberOfRuns = 1;

        final List<Integer> sizes = Arrays.asList(16, 2048, 1048576, 8388608); // createExponentialRangeWithIntermediatePoints();
        final List<Integer> runs = rangeExclusive(0, numberOfRuns);

        //    final EnumSet<MemoryFootprintPreset> presets = EnumSet
        //        .of(DATA_STRUCTURE_OVERHEAD); // also consider measuring RETAINED_SIZE

        final Function<Map.Entry<Integer, Integer>, Stream<String>> measureAllMultimaps = (
                sizeRunTuple) -> extractAndApply(sizeRunTuple,
                        (size, run) -> measurePersistentMultimaps(size, run, DATA_STRUCTURE_OVERHEAD).stream());

        writeToFile(directoryPath.resolve("map_sizes_heterogeneous_tiny.csv"), appendToFile,
                product(rangeInclusive(0, 100, 1), runs).stream().flatMap(measureAllMultimaps)
                        .collect(Collectors.toList()));

        //    writeToFile(
        //        directoryPath.resolve("map_sizes_heterogeneous_small.csv"),
        //        appendToFile,
        //        product(rangeInclusive(100, 10_000, 100), runs).stream()
        //            .flatMap(measureAllMultimaps)
        //            .collect(Collectors.toList()));
        //
        //    writeToFile(
        //        directoryPath.resolve("map_sizes_heterogeneous_medium.csv"),
        //        appendToFile,
        //        product(rangeInclusive(10_000, 100_000, 1_000), runs).stream()
        //            .flatMap(measureAllMultimaps)
        //            .collect(Collectors.toList()));
        //
        //    writeToFile(
        //        directoryPath.resolve("map_sizes_heterogeneous_large.csv"),
        //        appendToFile,
        //        product(rangeInclusive(100_000, 8_000_000, 100_000), runs).stream()
        //            .flatMap(measureAllMultimaps)
        //            .collect(Collectors.toList()));

        writeToFile(
                directoryPath.resolve("map_sizes_heterogeneous_exponential_" + memoryArchitecture + "_latest.csv"),
                appendToFile,
                product(sizes, runs).stream().flatMap(measureAllMultimaps).collect(Collectors.toList()));

        //    /*
        //     * PRIMITIVE DATA
        //     */
        //
        //    final Function<Map.Entry<Integer, Integer>, Stream<String>> measureAllPrimitiveMultimaps =
        //        (sizeRunTuple) -> extractAndApply(sizeRunTuple,
        //            (size, run) -> measureMutablePrimitiveMaps(size, run,
        //                RETAINED_SIZE_WITH_BOXED_INTEGER_FILTER).stream());
        //
        //    writeToFile(
        //        directoryPath.resolve("map_sizes_heterogeneous_exponential_"
        //            + memoryArchitecture + "_primitive_latest.csv"),
        //        appendToFile,
        //        product(sizes, runs).stream()
        //            .flatMap(measureAllPrimitiveMultimaps)
        //            .collect(Collectors.toList()));
    }

    public static List<String> measurePersistentMultimaps(int size, int run, MemoryFootprintPreset preset) {

        final Function<ValueFactoryFactory, String> executeExperiment = (
                factory) -> createAndMeasureTrieSetMultimap(factory, size, multimapValueSize,
                        stepSizeOneToOneSelector, run, preset);

        final EnumSet<ValueFactoryFactory> factories = EnumSet.of(VF_CHAMP_MULTIMAP_HCHAMP, VF_CHAMP_MULTIMAP_HHAMT,
                VF_CHAMP_MULTIMAP_HHAMT_SPECIALIZED, VF_SCALA, VF_CLOJURE);

        return factories.stream().map(executeExperiment).collect(Collectors.toList());
    }

    /**
     * Map<K, V> 3rd party libraries containing persistent data structures.
     */
    public static List<String> measurePersistentJavaLibraries(int size, int run, MemoryFootprintPreset preset) {

        final Function<ValueFactoryFactory, String> executeExperiment = (factory) -> createAndMeasurePersistentMap(
                factory, size, run, preset);

        final EnumSet<ValueFactoryFactory> factories = EnumSet.of(VF_PAGURO, VF_DEXX, VF_VAVR, VF_PCOLLECTIONS);

        return factories.stream().map(executeExperiment).collect(Collectors.toList());
    }

    /**
     * Map[int, int].
     */
    public static List<String> measureMutablePrimitiveMaps(int size, int run, MemoryFootprintPreset preset) {

        final Number[] data = createNumericData(size, run, 1.00);

        final Set<Supplier<String>> experiments = new HashSet<>();

        experiments.add(() -> createAndMeasureGuavaImmutableMap(data, size, run, preset)); // Reference
        experiments.add(() -> createAndMeasureTrieMapHeterogeneous_asMap(data, size, run, preset));

        experiments.add(() -> createAndMeasureFastUtilInt2IntOpenHashMap(data, size, run, preset));
        experiments.add(() -> createAndMeasureMahoutMutableIntIntHashMap(data, size, run, preset));
        experiments.add(() -> createAndMeasureTrove4jTIntIntHashMap(data, size, run, preset));
        experiments.add(() -> createAndMeasureGsImmutableIntIntMap(data, size, run, preset));

        //    // generic maps
        //    experiments.add(() -> createAndMeasureJavaUtilHashMap(data, size, run, preset));
        //    experiments.add(() -> createAndMeasureTrieMapHomogeneous(data, size, run, preset));
        //    experiments.add(() -> createAndMeasureTrieMapHeterogeneous(data, size, run, preset, true));
        //    experiments.add(() -> createAndMeasureTrieMapHeterogeneous(data, size, run, preset, false));

        return experiments.stream().map(Supplier::get).collect(Collectors.toList());
    }

    /**
     * SetMultimap.
     */
    public static List<String> measureXxxxMultiMaps(int size, int run, MemoryFootprintPreset preset) {

        final Number[] data = createNumericData(size, run, 1.00);

        final Set<Supplier<String>> experiments = new HashSet<>();
        experiments.add(() -> createAndMeasureGsImmutableSetMultimap(data, size, run, preset));
        experiments.add(() -> createAndMeasureGuavaImmutableSetMultimap(data, size, run, preset));

        return experiments.stream().map(Supplier::get).collect(Collectors.toList());
    }

    /**
     * Map<K, V> vs Multimap<K, V>.
     */
    public static List<String> measurePersistentMapVsMultimap(int size, int run, MemoryFootprintPreset preset) {

        final Function<ValueFactoryFactory, String> executeExperiment = (factory) -> createAndMeasureTrieMap(
                factory, size, run, preset);

        final EnumSet<ValueFactoryFactory> factories = EnumSet.of(VF_CHAMP_MULTIMAP_HCHAMP, VF_CHAMP_MULTIMAP_HHAMT,
                VF_CHAMP_MULTIMAP_HHAMT_SPECIALIZED);

        return factories.stream().map(executeExperiment).collect(Collectors.toList());
    }

    private static void measureBinaryAndTernaryMultimaps() {
        int elementCount = 1048576;

        final Class<? extends AbstractSetMultimapGenerator<? extends SetMultimap.Immutable>>[] generatorClasses = new Class[] {
                BidirectionalTrieSetMultimapGenerator.class, SetMultimapGenerator_HCHAMP.class };

        System.out.println("\n\n\n\n");

        {
            final String result = createAndMeasureXXX_2(TernaryTrieSetMultimapGenerator.class, elementCount, 2, 2,
                    0, RETAINED_SIZE_WITH_BOXED_INTEGER_FILTER);

            System.out.println("\n" + result + "\n\n\n\n");
        }

        Arrays.asList(generatorClasses).forEach(clazz -> {
            final String result = createAndMeasureXXX(clazz, elementCount, 2, 2, 0,
                    RETAINED_SIZE_WITH_BOXED_INTEGER_FILTER);

            System.out.println("\n" + result + "\n\n\n\n");
        });
    }

    // public static void testPrintStatsRandomSmallAndBigIntegers() {
    // int measurements = 4;
    //
    // for (int exp = 0; exp <= 23; exp += 1) {
    // final int thisExpSize = (int) Math.pow(2, exp);
    // final int prevExpSize = (int) Math.pow(2, exp-1);
    //
    // int stride = (thisExpSize - prevExpSize) / measurements;
    //
    // if (stride == 0) {
    // measurements = 1;
    // }
    //
    // for (int m = measurements - 1; m >= 0; m--) {
    // int size = thisExpSize - m * stride;
    // }

    public static final <X, Y, Z> Z extractAndApply(Map.Entry<X, Y> tuple, BiFunction<X, Y, Z> mapper) {
        return mapper.apply(tuple.getKey(), tuple.getValue());
    }

    public static final <X, Y> List<Map.Entry<X, Y>> product(List<X> xs, List<Y> ys) {
        List<Map.Entry<X, Y>> xys = new ArrayList<>(xs.size() * ys.size());

        for (X x : xs) {
            for (Y y : ys) {
                xys.add(entryOf(x, y));
            }
        }

        return xys;
    }

    public static List<Integer> createExponentialRangeWithIntermediatePoints() {
        List<Integer> tmpExponentialRange1 = createExponentialRange(0, 24);
        List<Integer> tmpExponentialRange2 = createExponentialRange(-1, 23);

        List<Integer> tmpExponentialRange = new ArrayList<>(2 * tmpExponentialRange1.size());
        for (int i = 0; i < tmpExponentialRange1.size(); i++) {
            tmpExponentialRange.add(tmpExponentialRange1.get(i));
            tmpExponentialRange.add(tmpExponentialRange1.get(i) + tmpExponentialRange2.get(i));
        }

        List<Integer> exponentialRange = tmpExponentialRange.stream().skip(1)
                .limit(2 * tmpExponentialRange1.size() - 2).collect(Collectors.toList());

        return exponentialRange;
    }

    public static Number[] createNumericData(int size, int run, double percentageOfPrimitives) {
        Number[] data = new Number[size];

        int countForPrimitives = (int) ((percentageOfPrimitives) * size);
        int smallCount = 0;
        int bigCount = 0;

        Random rand = new Random(13);
        for (int i = 0; i < size; i++) {
            final int j = rand.nextInt();
            final BigInteger bigJ = BigInteger.valueOf(j).multiply(BigInteger.valueOf(j));

            if (i < countForPrimitives) {
                // System.out.println("SMALL");
                smallCount++;
                data[i] = j;
            } else {
                // System.out.println("BIG");
                bigCount++;
                data[i] = bigJ;
            }
        }

        System.out.println();
        System.out.println(String.format("PRIMITIVE:   %10d (%.2f percent)", smallCount,
                100. * smallCount / (smallCount + bigCount)));
        System.out.println(String.format("BIG_INTEGER: %10d (%.2f percent)", bigCount,
                100. * bigCount / (smallCount + bigCount)));
        // System.out.println(String.format("UNIQUE: %10d (%.2f percent)",
        // map.size(), 100. * map.size() / (smallCount + bigCount)));
        System.out.println();

        return data;
    }

    // public static String createAndMeasureMultiChamp(final Object[] data, int elementCount, int run,
    // MemoryFootprintPreset preset) {
    // ImmutableSetMultimap<Integer, Integer> ys = TrieSetMultimap_BleedingEdge.of();
    //
    // for (Object o : data) {
    // for (int i = 0; i < multimapValueCount; i++) {
    // ys = ys.__put((Integer) o, (Integer) i);
    // }
    // }
    //
    // return measureAndReport(ys, "io.usethesource.capsule.TrieSetMultimap_BleedingEdge",
    // DataType.MULTIMAP,
    // Archetype.PERSISTENT, false, elementCount, run, preset);
    // }

    public static String createAndMeasureGsImmutableSetMultimap(final Object[] data, int elementCount, int run,
            MemoryFootprintPreset preset) {
        com.gs.collections.api.multimap.set.MutableSetMultimap<Integer, Integer> mutableYs = com.gs.collections.impl.factory.Multimaps.mutable.set
                .with();

        for (Object o : data) {
            for (int i = 0; i < multimapValueSize; i++) {
                mutableYs.put((Integer) o, (Integer) i);
            }
        }

        /* Note: direct creation of immutable that uses newWith(...) is tremendously slow. */
        com.gs.collections.api.multimap.set.ImmutableSetMultimap<Integer, Integer> ys = mutableYs.toImmutable();

        return measureAndReport(ys, "com.gs.collections.api.multimap.set.ImmutableSetMultimap",
                DataType.SET_MULTIMAP, Archetype.IMMUTABLE, false, elementCount, run, preset);
    }

    public static String createAndMeasureFastUtilInt2IntOpenHashMap(final Object[] data, int elementCount, int run,
            MemoryFootprintPreset preset) {
        it.unimi.dsi.fastutil.ints.AbstractInt2IntMap mutableYs = new Int2IntOpenHashMap();

        for (Object o : data) {
            for (int i = 0; i < multimapValueSize; i++) {
                mutableYs.put((Integer) o, (Integer) i);
            }
        }

        return measureAndReport(mutableYs, "it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap", DataType.MAP,
                Archetype.MUTABLE, false, elementCount, run, preset);
    }

    public static String createAndMeasureMahoutMutableIntIntHashMap(final Object[] data, int elementCount, int run,
            MemoryFootprintPreset preset) {
        org.apache.mahout.math.map.AbstractIntIntMap mutableYs = new OpenIntIntHashMap();

        for (Object o : data) {
            for (int i = 0; i < multimapValueSize; i++) {
                mutableYs.put((Integer) o, (Integer) i);
            }
        }

        return measureAndReport(mutableYs, "org.apache.mahout.math.map.OpenIntIntHashMap", DataType.MAP,
                Archetype.MUTABLE, false, elementCount, run, preset);
    }

    public static String createAndMeasureGsImmutableIntIntMap(final Object[] data, int elementCount, int run,
            MemoryFootprintPreset preset) {
        com.gs.collections.api.map.primitive.MutableIntIntMap mutableYs = new IntIntHashMap();

        for (Object o : data) {
            for (int i = 0; i < multimapValueSize; i++) {
                mutableYs.put((Integer) o, (Integer) i);
            }
        }

        com.gs.collections.api.map.primitive.ImmutableIntIntMap ys = mutableYs.toImmutable();

        return measureAndReport(ys, "com.gs.collections.api.map.primitive.ImmutableIntIntMap", DataType.MAP,
                Archetype.IMMUTABLE, false, elementCount, run, preset);
    }

    private final static JmhValue box(int i) {
        return new PureIntegerWithCustomHashCode(i);
    }

    public static String createAndMeasureGuavaImmutableMap(final Object[] data, int elementCount, int run,
            MemoryFootprintPreset preset) {
        com.google.common.collect.ImmutableMap.Builder<JmhValue, JmhValue> ysBldr = com.google.common.collect.ImmutableMap
                .builder();

        // filters duplicates (because builder can't handle them)
        Set<Object> seenKeys = new HashSet<>(data.length);

        for (Object o : data) {
            if (!seenKeys.contains(o)) {
                seenKeys.add(o);
                ysBldr.put(box((Integer) o), box((Integer) o));
            }
        }

        com.google.common.collect.ImmutableMap<JmhValue, JmhValue> ys = ysBldr.build();

        return measureAndReport(ys, "com.google.common.collect.ImmutableMap", DataType.MAP, Archetype.IMMUTABLE,
                false, elementCount, run, preset);
    }

    public static String createAndMeasureGuavaImmutableSetMultimap(final Object[] data, int elementCount, int run,
            MemoryFootprintPreset preset) {
        com.google.common.collect.ImmutableSetMultimap.Builder<JmhValue, JmhValue> ysBldr = com.google.common.collect.ImmutableSetMultimap
                .builder();

        for (int keyIdx = 0; keyIdx < data.length; keyIdx++) {
            Object o = data[keyIdx];

            if (keyIdx % stepSizeOneToOneSelector == 0) {
                ysBldr.put(box((Integer) o), box((Integer) o));
            } else {
                for (int i = 0; i < multimapValueSize; i++) {
                    ysBldr.put(box((Integer) o), box(i));
                }
            }
        }

        com.google.common.collect.ImmutableMultimap<JmhValue, JmhValue> ys = ysBldr.build();

        return measureAndReport(ys, "com.google.common.collect.ImmutableSetMultimap", DataType.SET_MULTIMAP,
                Archetype.IMMUTABLE, false, elementCount, run, preset);
    }

    public static String createAndMeasureTrieMapHeterogeneous_asMap(final Object[] data, int elementCount, int run,
            MemoryFootprintPreset preset) {
        TrieMap_5Bits_Heterogeneous_BleedingEdge ys = (TrieMap_5Bits_Heterogeneous_BleedingEdge) TrieMap_5Bits_Heterogeneous_BleedingEdge
                .of();

        // simulating a multimap with a specialized nested integer set
        for (int keyIdx = 0; keyIdx < data.length; keyIdx++) {
            Object o = data[keyIdx];
            int intValue = ((Integer) o).intValue();

            ys = (TrieMap_5Bits_Heterogeneous_BleedingEdge) ys.__put(intValue, intValue);
        }

        final String shortName = "HHAMT_AS_MAP<int, int>";

        return measureAndReport(ys, shortName, DataType.MAP, Archetype.PERSISTENT, false, elementCount, run,
                preset);
    }

    public static String createAndMeasureTrieMapHeterogeneous_asMultimap(final Object[] data, int elementCount,
            int run, MemoryFootprintPreset preset) {
        TrieMap_5Bits_Heterogeneous_BleedingEdge ys = (TrieMap_5Bits_Heterogeneous_BleedingEdge) TrieMap_5Bits_Heterogeneous_BleedingEdge
                .of();

        // simulating a multimap with a specialized nested integer set
        for (int keyIdx = 0; keyIdx < data.length; keyIdx++) {
            Object o = data[keyIdx];
            int intValue = ((Integer) o).intValue();

            if (keyIdx % stepSizeOneToOneSelector == 0) {
                ys = (TrieMap_5Bits_Heterogeneous_BleedingEdge) ys.__put(intValue, intValue);
            } else {
                io.usethesource.capsule.Set.Immutable<Integer> nestedSet = TrieSet_5Bits_Spec0To8_IntKey.of();

                for (int i = 0; i < multimapValueSize; i++) {
                    nestedSet = nestedSet.__insert(i);
                }

                ys = (TrieMap_5Bits_Heterogeneous_BleedingEdge) ys.__put(o, nestedSet);
            }
        }

        final String shortName = "HHAMT_AS_SET_MULTIMAP<int, int>";

        return measureAndReport(ys, shortName, DataType.SET_MULTIMAP, Archetype.PERSISTENT, false, elementCount,
                run, preset);
    }

    public static String createAndMeasurePersistentMap(ValueFactoryFactory valueFactoryFactory, int elementCount,
            int run, MemoryFootprintPreset preset) {
        try {
            final Object mapInstance = JmhMapBenchmarks.generateMap(valueFactoryFactory.getInstance(),
                    ElementProducer.PDB_INTEGER, false, elementCount, run);

            return measureAndReport(mapInstance, valueFactoryFactory.name(), DataType.MAP, Archetype.PERSISTENT,
                    false, elementCount, run, preset);
        } catch (Exception e) {
            e.printStackTrace();
            return "ERROR";
        }
    }

    /*
     * TODO: check where this is used; misnomer.
     */
    @Deprecated
    public static String createAndMeasureTrieMap(ValueFactoryFactory valueFactoryFactory, int elementCount, int run,
            MemoryFootprintPreset preset) {
        try {
            final int fixedMultimapValueSize = 1;
            final int fixedStepSizeOneToOneSelector = 1;

            final Object setMultimapInstance = JmhSetMultimapBenchmarks.generateSetMultimap(
                    valueFactoryFactory.getInstance(), ElementProducer.PDB_INTEGER, false, elementCount,
                    fixedMultimapValueSize, fixedStepSizeOneToOneSelector, run);

            return measureAndReport(setMultimapInstance, valueFactoryFactory.name(), DataType.MAP,
                    Archetype.PERSISTENT, false, elementCount, run, preset);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "ERROR";
    }

    public final static Size size(int min, int max) {
        return new Size() {
            @Override
            public int min() {
                return min;
            }

            @Override
            public int max() {
                return max;
            }

            @Override
            public Class<? extends Annotation> annotationType() {
                return Size.class;
            }
        };
    }

    public static String createAndMeasureXXX(
            Class<? extends AbstractSetMultimapGenerator<? extends SetMultimap.Immutable>> generatorClass,
            int elementCount, int multimapValueSize, int stepSizeOneToOneSelector, int run,
            MemoryFootprintPreset preset) {

        try {
            final AbstractSetMultimapGenerator<? extends SetMultimap.Immutable> gen = generatorClass.newInstance();

            gen.configure(size(elementCount, elementCount));

            gen.addComponentGenerators(Arrays.asList(new IntegerGenerator(), new IntegerGenerator()));

            final SourceOfRandomness random = new SourceOfRandomness(
                    new Random(seedFromSizeAndRun(elementCount, run)));

            final GenerationStatus status = new SimpleGenerationStatus(new GeometricDistribution(), random, 1);

            final Object setMultimapInstance = gen.generate(random, status);

            // System.out.println(setMultimapInstance);

            return measureAndReport(setMultimapInstance, generatorClass.getName(), DataType.SET_MULTIMAP,
                    Archetype.PERSISTENT, false, elementCount, run, preset);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return "ERROR";
    }

    public static final <T> Class<T> classCast(Class clazz) {
        return (Class<T>) clazz;
    }

    public static String createAndMeasureXXX_2(
            Class<? extends AbstractTernaryRelationGenerator<? extends TernaryRelation.Immutable>> generatorClass,
            int elementCount, int multimapValueSize, int stepSizeOneToOneSelector, int run,
            MemoryFootprintPreset preset) {

        final Class<TernaryRelation.Immutable<Integer, Integer, Integer, Triple<Integer, Integer, Integer>>> targetClass = classCast(
                TernaryRelation.Immutable.class);

        final ComponentizedGenerator<TernaryRelation.Immutable<Integer, Integer, Integer, Triple<Integer, Integer, Integer>>> generator = new ComponentizedGenerator(
                targetClass) {
            @Override
            public TernaryRelation.Immutable<Integer, Integer, Integer, Triple<Integer, Integer, Integer>> generate(
                    SourceOfRandomness sourceOfRandomness, GenerationStatus generationStatus) {
                return null;
            }
        };

        try {
            final AbstractTernaryRelationGenerator<? extends TernaryRelation.Immutable> gen = generatorClass
                    .newInstance();

            gen.configure(size(elementCount, elementCount));

            Generator<Triple> tripleGenerator = new TripleGenerator();
            tripleGenerator.addComponentGenerators(
                    Arrays.asList(new IntegerGenerator(), new IntegerGenerator(), new IntegerGenerator()));

            gen.addComponentGenerators(Arrays.asList(new IntegerGenerator(), new IntegerGenerator(),
                    new IntegerGenerator(), tripleGenerator));

            final SourceOfRandomness random = new SourceOfRandomness(
                    new Random(seedFromSizeAndRun(elementCount, run)));

            final GenerationStatus status = new SimpleGenerationStatus(new GeometricDistribution(), random, 1);

            final Object setMultimapInstance = gen.generate(random, status);

            // System.out.println(setMultimapInstance);

            return measureAndReport(setMultimapInstance, generatorClass.getName(), DataType.SET_MULTIMAP,
                    Archetype.PERSISTENT, false, elementCount, run, preset);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return "ERROR";
    }

    public static String createAndMeasureTrieSetMultimap(ValueFactoryFactory valueFactoryFactory, int elementCount,
            int multimapValueSize, int stepSizeOneToOneSelector, int run, MemoryFootprintPreset preset) {
        try {
            final Object setMultimapInstance = JmhSetMultimapBenchmarks.generateSetMultimap(
                    valueFactoryFactory.getInstance(), ElementProducer.PDB_INTEGER, false, elementCount,
                    multimapValueSize, stepSizeOneToOneSelector, run);

            return measureAndReport(setMultimapInstance, valueFactoryFactory.name(), DataType.SET_MULTIMAP,
                    Archetype.PERSISTENT, false, elementCount, run, preset);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "ERROR";
    }

    public static String createAndMeasureTrieMapHomogeneous(final Object[] data, int elementCount, int run,
            MemoryFootprintPreset preset) {
        io.usethesource.capsule.Map.Immutable<Integer, Integer> ys = PersistentTrieMap.of();

        // for (Object v : data) {
        // ys = ys.__put(v, v);
        // assert ys.containsKey(v);
        // }

        int[] convertedData = new int[elementCount];

        for (int i = 0; i < elementCount; i++) {
            final Object v = data[i];
            final int convertedValue;

            if (v instanceof Integer) {
                convertedValue = (Integer) v;
            } else if (v instanceof BigInteger) {
                convertedValue = ((BigInteger) v).intValue();
            } else {
                throw new IllegalStateException("Expecting input data of type Integer or BigInteger.");
            }

            convertedData[i] = convertedValue;
        }

        for (int value : convertedData) {
            ys = ys.__put(value, value);
            assert ys.containsKey(value);
        }

        String shortName = "TrieMap [Boxed]";

        // String longName = String.format(
        // "io.usethesource.capsule.TrieMap_5Bits_Spec0To8", isSpecialized);

        return measureAndReport(ys, shortName, DataType.MAP, Archetype.PERSISTENT, false, elementCount, run,
                preset);
    }

    public static String createAndMeasureTrieMapHeterogeneous(final Object[] data, int elementCount, int run,
            MemoryFootprintPreset preset, boolean storePrimivesBoxed) {
        TrieMap_5Bits_Heterogeneous_BleedingEdge ys = (TrieMap_5Bits_Heterogeneous_BleedingEdge) TrieMap_5Bits_Heterogeneous_BleedingEdge
                .of();

        for (Object v : data) {
            if (v instanceof Integer && storePrimivesBoxed) {
                // PureInteger boxedValue = new PureInteger(((Integer) v).intValue());
                Integer boxedValue = (Integer) v;

                ys = (TrieMap_5Bits_Heterogeneous_BleedingEdge) ys.__put(boxedValue, boxedValue);
                assert ys.containsKey(boxedValue);
            } else if (v instanceof Integer && !storePrimivesBoxed) {
                int unboxedValue = ((Integer) v).intValue();

                ys = (TrieMap_5Bits_Heterogeneous_BleedingEdge) ys.__put(unboxedValue, unboxedValue);
                assert ys.containsKey(unboxedValue);
                // } else {
                // ys = (TrieMap_5Bits_Heterogeneous_BleedingEdge) ys.__put(v, v);
                // assert ys.containsKey(v);
            }
        }

        final String shortName = storePrimivesBoxed ? "HTrieMap [Boxed]" : "HTrieMap [Primitive]";

        // String shortName = String.format("TrieMap[%13s, storePrimivesBoxed =
        // %5s]",
        // "heterogeneous", storePrimivesBoxed);
        //
        // String longName = String.format(
        // "io.usethesource.capsule.TrieMap_Heterogeneous[storePrimivesBoxed =
        // %5s]",
        // storePrimivesBoxed);

        return measureAndReport(ys, shortName, DataType.MAP, Archetype.PERSISTENT, false, elementCount, run,
                preset);
    }

    public static String createAndMeasureJavaUtilHashMap(final Object[] data, int elementCount, int run,
            MemoryFootprintPreset preset) {
        Map<Object, Object> ys = new HashMap<>();

        for (Object v : data) {
            ys.put(v, v);
            assert ys.containsKey(v);
        }

        String shortName = String.format("HashMap");

        String.format("java.util.HashMap");

        return measureAndReport(ys, shortName, DataType.MAP, Archetype.MUTABLE, false, elementCount, run, preset);
    }

    public static String createAndMeasureTrove4jTIntIntHashMap(final Object[] data, int elementCount, int run,
            MemoryFootprintPreset preset) {
        TIntIntHashMap ys = new TIntIntHashMap(elementCount);

        int[] convertedData = new int[elementCount];

        for (int i = 0; i < elementCount; i++) {
            final Object v = data[i];
            final int convertedValue;

            if (v instanceof Integer) {
                convertedValue = (Integer) v;
            } else if (v instanceof BigInteger) {
                convertedValue = ((BigInteger) v).intValue();
            } else {
                throw new IllegalStateException("Expecting input data of type Integer or BigInteger.");
            }

            convertedData[i] = convertedValue;
        }

        for (int value : convertedData) {
            ys.put(value, value);
            assert ys.containsKey(value);
        }

        return measureAndReport(ys, "gnu.trove.map.hash.TIntIntHashMap", DataType.MAP, Archetype.MUTABLE, false,
                elementCount, run, preset);
    }

    private static String measureAndReport(final Object objectToMeasure, final String className, DataType dataType,
            Archetype archetype, boolean supportsStagedMutability, int size, int run,
            MemoryFootprintPreset preset) {
        final Predicate<Object> predicate;

        switch (preset) {
        case DATA_STRUCTURE_OVERHEAD:
            // TODO: create JmhLeaf
            // predicate = Predicates
            // .not(Predicates.or(Predicates.instanceOf(Integer.class),
            // Predicates.instanceOf(BigInteger.class),
            // Predicates.instanceOf(JmhValue.class), Predicates.instanceOf(PureInteger.class)));
            predicate = Predicates.not(Predicates.or(Predicates.instanceOf(PureInteger.class),
                    Predicates.instanceOf(PureIntegerWithCustomHashCode.class)));
            break;
        case RETAINED_SIZE:
            predicate = Predicates.alwaysTrue();
            break;
        case RETAINED_SIZE_WITH_BOXED_INTEGER_FILTER:
            predicate = Predicates.not(Predicates.instanceOf(Integer.class));
            break;
        default:
            throw new IllegalStateException();
        }

        return measureAndReport(objectToMeasure, className, dataType, archetype, supportsStagedMutability, size,
                run, predicate);
    }

    private static String measureAndReport(final Object objectToMeasure, final String className, DataType dataType,
            Archetype archetype, boolean supportsStagedMutability, int size, int run) {
        return measureAndReport(objectToMeasure, className, dataType, archetype, supportsStagedMutability, size,
                run, DATA_STRUCTURE_OVERHEAD);
    }

    private static String measureAndReport(final Object objectToMeasure, final String className, DataType dataType,
            Archetype archetype, boolean supportsStagedMutability, int size, int run, Predicate<Object> predicate) {
        // System.out.println(GraphLayout.parseInstance(objectToMeasure).totalSize());

        long memoryInBytes = objectexplorer.MemoryMeasurer.measureBytes(objectToMeasure, predicate);
        Footprint memoryFootprint = objectexplorer.ObjectGraphMeasurer.measure(objectToMeasure, predicate);

        final String statString = String.format("%d (%d@%d)\t %60s\t\t %s", size, run, memoryInBytes, className,
                memoryFootprint);
        System.out.println(statString);

        // final String statLatexString = String.format("%s & %s & %s & %b & %d
        // & %d & %d & \"%s\" \\\\", className, dataType, archetype,
        // supportsStagedMutability, memoryInBytes,
        // memoryFootprint.getObjects(), memoryFootprint.getReferences(),
        // memoryFootprint.getPrimitives());
        // System.out.println(statLatexString);

        final String statFileString = String.format("%d,%d,%s,%s,%s,%b,%d,%d,%d", size, run, className, dataType,
                archetype, supportsStagedMutability, memoryInBytes, memoryFootprint.getObjects(),
                memoryFootprint.getReferences());

        return statFileString;
        // writeToFile(statFileString);
    }

}