Java tutorial
/*- * #%L * LmdbJava Benchmarks * %% * Copyright (C) 2016 The LmdbJava Open Source Project * %% * 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. * #L% */ package org.lmdbjava.bench; import java.io.File; import java.io.IOException; import static java.lang.Integer.BYTES; import static java.lang.Integer.MIN_VALUE; import static java.lang.System.getProperty; import static java.lang.System.out; import java.util.zip.CRC32; import jnr.posix.FileStat; import jnr.posix.POSIX; import static jnr.posix.POSIXFactory.getPOSIX; import org.agrona.collections.IntHashSet; import org.apache.commons.math3.random.BitsStreamGenerator; import org.apache.commons.math3.random.MersenneTwister; import org.openjdk.jmh.annotations.Param; import static org.openjdk.jmh.annotations.Scope.Benchmark; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.infra.BenchmarkParams; /** * Common JMH {@link State} superclass for all DB benchmark states. * * <p> * Members do not reflect the typical code standards of the LmdbJava project due * to compliance requirements with JMH {@link Param} and {@link State}. */ @State(Benchmark) @SuppressWarnings({ "checkstyle:designforextension", "checkstyle:visibilitymodifier" }) public class Common { static final byte[] RND_MB = new byte[1_048_576]; static final int STRING_KEY_LENGTH = 16; private static final POSIX POSIX = getPOSIX(); private static final BitsStreamGenerator RND = new MersenneTwister(); private static final int S_BLKSIZE = 512; // from sys/stat.h private static final File TMP_BENCH; File compact; CRC32 crc; /** * Keys are always an integer, however they are actually stored as integers * (taking 4 bytes) or as zero-padded 16 byte strings. Storing keys as * integers offers a major performance gain. */ @Param("true") boolean intKey; /** * Determined during {@link #setup()} based on {@link #intKey} value. */ int keySize; /** * Keys in designated (random/sequential) order. */ int[] keys; /** * Number of entries to read/write to the database. */ @Param("1000000") int num; /** * Whether the keys are to be inserted into the database in sequential order * (and in the "readKeys" case, read back in that order). For LMDB, sequential * inserts use {@link org.lmdbjava.PutFlags#MDB_APPEND} and offer a major * performance gain. If this field is false, the append flag will not be used * and the keys will instead be inserted (and read back via "readKeys") in a * random order. */ @Param("true") boolean sequential; File tmp; /** * Whether the values contain random bytes or are simply the same as the key. * If true, the random bytes are obtained sequentially from a 1 MB random byte * buffer. */ @Param("false") boolean valRandom; /** * Number of bytes in each value. */ @Param("100") int valSize; static { RND.nextBytes(RND_MB); final String tmpParent = getProperty("java.io.tmpdir"); TMP_BENCH = new File(tmpParent, "lmdbjava-benchmark-scratch"); } public void setup(final BenchmarkParams b) throws IOException { keySize = intKey ? BYTES : STRING_KEY_LENGTH; crc = new CRC32(); final IntHashSet set = new IntHashSet(num, MIN_VALUE); keys = new int[num]; for (int i = 0; i < num; i++) { if (sequential) { keys[i] = i; } else { while (true) { int candidateKey = RND.nextInt(); if (candidateKey < 0) { candidateKey *= -1; } if (!set.contains(candidateKey)) { set.add(candidateKey); keys[i] = candidateKey; break; } } } } rmdir(TMP_BENCH); tmp = create(b, ""); compact = create(b, "-compacted"); } public void reportSpaceBeforeClose() { if (tmp.getName().contains(".readKey-")) { reportSpaceUsed(tmp, "before-close"); } } public void teardown() throws IOException { // we only output for key, as all impls offer it and it should be fixed if (tmp.getName().contains(".readKey-")) { reportSpaceUsed(tmp, "after-close"); } rmdir(TMP_BENCH); } @SuppressWarnings("UseOfSystemOutOrSystemErr") protected void reportSpaceUsed(final File dir, final String desc) { final File[] files = dir.listFiles(); if (files == null) { return; } long bytes = 0; for (final File f : files) { if (f.isDirectory()) { throw new UnsupportedOperationException("impl created directory"); } final FileStat stat = POSIX.stat(f.getAbsolutePath()); bytes += stat.blocks() * S_BLKSIZE; } out.println("\nBytes\t" + desc + "\t" + bytes + "\t" + dir.getName()); } final String padKey(final int key) { final String skey = Integer.toString(key); return "0000000000000000".substring(0, 16 - skey.length()) + skey; } private File create(final BenchmarkParams b, final String suffix) { final File f = new File(TMP_BENCH, b.id() + suffix); if (!f.mkdirs()) { throw new IllegalStateException("Cannot mkdir " + f); } return f; } @SuppressWarnings("checkstyle:ReturnCount") private void rmdir(final File file) { if (!file.exists()) { return; } if (file.isDirectory()) { final File[] files = file.listFiles(); if (files == null) { return; } for (final File f : files) { rmdir(f); } } if (!file.delete()) { throw new IllegalStateException("Cannot delete " + file); } } }