/* * Copyright (C) ${year} Omry Yadan <${email}> * All rights reserved. * * See https://github.com/omry/banana/blob/master/BSD-LICENSE for licensing information */ package net.yadan.banana.map; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; import net.yadan.utils.JVMSpawn; import net.yadan.utils.Util; public class LongToFixedSizeObjectBenchmark { public static int MILLION = 1000 * 1000; /** * Java object representing some fixed some data structure of size * * <pre> * name : size in bytes * -----------------:-------------- * long session_id : 8 * byte ipv6[16] : 16 * short port; : 2 * short type; : 2 * ---------------------- * total bytes | 28 * </pre> */ static class JavaObject { long session_id; int ipv6[] = new int[4]; short port; short type; JavaObject(long sid) { session_id = sid; } } /** * Raw banana offsets (in ints) for the Java Object representation * * <pre> * name : ints size : OFFSET * ----------------------------------- * long session_id : 2 : 0 * byte ipv6[16] : 4 : 2 * short port; : 0.5 : 6 (upper short) * short type; : 0.5 : 6 (lower short) * ---------------------- * total ints : 7 * </pre> */ public static int RECORD_SIZE = 7; public static int SESSION_ID_OFFSET = 0; public static int IPV6_OFFSET = 2; public static int PORT_OFFSET = 6; // upper short public static int TYPE_OFFSET = 6; // lower short public static int testBanana(String desc, int keys[], Stats stats) { long t; long e; int num = keys.length; int max = keys.length; t = System.currentTimeMillis(); IHashMap h = new HashMap(num, RECORD_SIZE, 0, 1); for (int i = 0; i < keys.length; i++) { int key = keys[i]; int r = h.createRecord(key, RECORD_SIZE); h.setLong(r, SESSION_ID_OFFSET, key); h.setInt(r, IPV6_OFFSET + 0, 1); h.setInt(r, IPV6_OFFSET + 1, 2); h.setInt(r, IPV6_OFFSET + 2, 3); h.setInt(r, IPV6_OFFSET + 3, 4); h.setUpperShort(r, PORT_OFFSET, 9999); h.setLowerShort(r, TYPE_OFFSET, 0); } e = System.currentTimeMillis() - t; stats.insert_rate = max / (e / 1000f); System.out.println(String.format("%s : %s/inserts sec", desc, Util.formatNum(stats.insert_rate))); t = System.currentTimeMillis(); for (int i = 0; i < keys.length; i++) { int key = keys[i]; int r = h.findRecord(key); if (h.getLong(r, SESSION_ID_OFFSET) != key) { throw new RuntimeException("failed"); } } e = System.currentTimeMillis() - t; stats.get_rate = max / (e / 1000f); System.out.println(String.format("%s : %s/gets sec", desc, Util.formatNum(stats.get_rate))); t = System.currentTimeMillis(); h = new HashMap(num, RECORD_SIZE, 0, 1); for (int i = 0; i < keys.length; i++) { int key = keys[i]; int r = h.createRecord(key, RECORD_SIZE); h.setLong(r, SESSION_ID_OFFSET, key); h.setInt(r, IPV6_OFFSET + 0, 1); h.setInt(r, IPV6_OFFSET + 1, 2); h.setInt(r, IPV6_OFFSET + 2, 3); h.setInt(r, IPV6_OFFSET + 3, 4); h.setUpperShort(r, PORT_OFFSET, 9999); h.setLowerShort(r, TYPE_OFFSET, 0); r = h.findRecord(key); if (h.getLong(r, SESSION_ID_OFFSET) != key) { throw new RuntimeException("failed"); } } e = System.currentTimeMillis() - t; stats.mixed_rate = max / (e / 1000f); System.out.println(String.format("%s : %s/inserts+gets (mixed) sec", desc, Util.formatNum(stats.mixed_rate))); System.gc(); MemoryMXBean mx = ManagementFactory.getMemoryMXBean(); MemoryUsage usage = mx.getHeapMemoryUsage(); stats.mem_used = usage.getUsed(); System.out.println(String.format("%s : %s used", desc, Util.formatSize(stats.mem_used))); return h.size(); } public static int testJava(String desc, int keys[], Stats stats) { long t; long e; int num = keys.length; int max = keys.length; t = System.currentTimeMillis(); java.util.HashMap<Long, JavaObject> h = new java.util.HashMap<Long, JavaObject>(num, 1.0f); for (int i = 0; i < num; i++) { long key = keys[i]; JavaObject o = new JavaObject(key); o.ipv6[0] = 1; o.ipv6[1] = 2; o.ipv6[2] = 3; o.ipv6[3] = 4; o.port = 9999; o.type = 0; h.put(key, o); } e = System.currentTimeMillis() - t; stats.insert_rate = max / (e / 1000f); System.out.println(String.format("%s : %s/inserts sec", desc, Util.formatNum(stats.insert_rate))); t = System.currentTimeMillis(); for (int i = 0; i < num; i++) { long key = keys[i]; JavaObject r = h.get(key); if (r.session_id != key) throw new RuntimeException("failed"); } e = System.currentTimeMillis() - t; stats.get_rate = max / (e / 1000f); System.out.println(String.format("%s : %s/gets sec", desc, Util.formatNum(stats.get_rate))); h = new java.util.HashMap<Long, JavaObject>(num, 1.0f); for (int i = 0; i < num; i++) { long key = keys[i]; JavaObject o = new JavaObject(key); o.ipv6[0] = 1; o.ipv6[1] = 2; o.ipv6[2] = 3; o.ipv6[3] = 4; o.port = 9999; o.type = 0; h.put(key, o); JavaObject r = h.get(key); if (r.session_id != key) throw new RuntimeException("failed"); } e = System.currentTimeMillis() - t; stats.mixed_rate = max / (e / 1000f); System.out.println(String.format("%s : %s/inserts+gets (mixed) sec", desc, Util.formatNum(stats.mixed_rate))); System.gc(); MemoryMXBean mx = ManagementFactory.getMemoryMXBean(); MemoryUsage usage = mx.getHeapMemoryUsage(); stats.mem_used = usage.getUsed(); System.out.println(String.format("%s : %s used", desc, Util.formatSize(stats.mem_used))); return h.size(); } public static int testFastUtil(String desc, int keys[], Stats stats) { long t; long e; int num = keys.length; int max = keys.length; t = System.currentTimeMillis(); Long2ObjectMap<JavaObject> h = new Long2ObjectOpenHashMap<JavaObject>(num, 1.0f); for (int i = 0; i < num; i++) { long key = keys[i]; JavaObject o = new JavaObject(key); o.ipv6[0] = 1; o.ipv6[1] = 2; o.ipv6[2] = 3; o.ipv6[3] = 4; o.port = 9999; o.type = 0; h.put(key, o); } e = System.currentTimeMillis() - t; stats.insert_rate = max / (e / 1000f); System.out.println(String.format("%s : %s/inserts sec", desc, Util.formatNum(stats.insert_rate))); t = System.currentTimeMillis(); for (int i = 0; i < num; i++) { long key = keys[i]; JavaObject r = h.get(key); if (r.session_id != key) throw new RuntimeException("failed"); } e = System.currentTimeMillis() - t; stats.get_rate = max / (e / 1000f); System.out.println(String.format("%s : %s/gets sec", desc, Util.formatNum(stats.get_rate))); h = new Long2ObjectOpenHashMap<JavaObject>(num, 1.0f); for (int i = 0; i < num; i++) { long key = keys[i]; JavaObject o = new JavaObject(key); o.ipv6[0] = 1; o.ipv6[1] = 2; o.ipv6[2] = 3; o.ipv6[3] = 4; o.port = 9999; o.type = 0; h.put(key, o); JavaObject r = h.get(key); if (r.session_id != key) throw new RuntimeException("failed"); } e = System.currentTimeMillis() - t; stats.mixed_rate = max / (e / 1000f); System.out.println(String.format("%s : %s/inserts+gets (mixed) sec", desc, Util.formatNum(stats.mixed_rate))); System.gc(); MemoryMXBean mx = ManagementFactory.getMemoryMXBean(); MemoryUsage usage = mx.getHeapMemoryUsage(); stats.mem_used = usage.getUsed(); System.out.println(String.format("%s : %s used", desc, Util.formatSize(stats.mem_used))); return h.size(); } // / ------------------------- // This section deals with launching a new JVM instance for each iteration. // Ideally this would be a part of a generic framework (or I would use an // existing generic framework like jmh or caliper) static class Stats { int max; float insert_rate; float get_rate; float mixed_rate; long mem_used; public Stats(int max) { this.max = max; } } static enum Type { Java, Banana, FastUtil } public static void main(String[] args) throws IOException { if (args.length == 0) { createFile("", Type.Java); createFile("", Type.Banana); createFile("", Type.FastUtil); // createFile("warmup_", Type.Java); // createFile("warmup_", Type.Banana); // createFile("warmup_", Type.FastUtil); int STEP = 5; for (int i = 12; i < 25; i++) { int m = i * STEP * MILLION; testType(Type.Java, m); testType(Type.FastUtil, m); testType(Type.Banana, m); } } else { int max = Integer.parseInt(args[1]); System.out.println(max / (float) MILLION + " million items"); int keys[] = new int[max]; for (int i = 0; i < max; i++) { keys[i] = i; } Util.shuffleArray(keys); Type type = Type.valueOf(args[0]); Stats stats = new Stats(max); runTypeTest("Warmup", keys, stats, type); // append("warmup_" + type.toString(), stats); // and now for real stats = new Stats(max); runTypeTest("Benchmark", keys, stats, type); append(type.toString(), stats); } } private static void runTypeTest(String desc, int[] keys, Stats stats, Type type) { String d = type + " : " + desc; switch (type) { case Java: testJava(d, keys, stats); break; case FastUtil: testFastUtil(d, keys, stats); break; case Banana: testBanana(d, keys, stats); break; default: throw new IllegalArgumentException("Unknown type " + type); } } private static void testType(Type t, int max) { int r = JVMSpawn.spawn("JVM For " + t + " (" + max + " items)", LongToFixedSizeObjectBenchmark.class.getName(), "-Xmx20g -XX:ParallelCMSThreads=1 -XX:ParallelGCThreads=1", new String[] { t.toString(), String.valueOf(max) }); if (r != 0) { throw new RuntimeException("Error " + r); } } private static void createFile(String prefix, Type type) throws IOException { String header = String.format("%s,%s,%s,%s,%s\n", "Number if items", type + " insert rate", type + " get rate", type + " mixed get/sec rate", type + " memory used"); Util.saveData(header.getBytes(), new File(prefix + type + ".csv")); } private static void append(String name, Stats stats) throws IOException { BufferedWriter out = null; try { int trys = 10; // in case someone is trying to open the file and it gets locked (ahem, // windows). while (true) { try { out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(name + ".csv", true))); break; } catch (IOException e) { try { Thread.sleep(2000); } catch (InterruptedException e1) { } if (trys-- == 0) throw e; } } String line = String.format("%d,%.1f,%.1f,%.1f,%d\n", stats.max / MILLION, stats.insert_rate / MILLION, stats.get_rate / MILLION, stats.mixed_rate / MILLION, stats.mem_used / MILLION); out.append(line); } finally { if (out != null) { out.close(); } } } }