com.persistit.JournalManagerBench Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of akiban-persistit Show documentation
Show all versions of akiban-persistit Show documentation
Java B+Tree Key-Value Store Library
/**
* Copyright © 2012 Akiban Technologies, Inc. All rights reserved.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This program may also be available under different license terms.
* For more information, see www.akiban.com or contact [email protected].
*
* Contributors:
* Akiban Technologies, Inc.
*/
package com.persistit;
import static com.persistit.util.Util.NS_PER_S;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import com.persistit.util.ArgParser;
/**
* Benchmark for primitive I/O simulating HARD (durable) commit. This code is
* intended to explore two elements:
*
* (1) Pre-extending the journal file (so that FileChannel.force(false) usually
* does not need to write any metadata.
*
* (2) Performing I/O in fixed-length blocks so that to write a some bytes the
* file system does not first need to read existing data from disk.
*
* Parameters
*
* align - smallest unit of I/O (default = 1) datapath - directory in which fake
* journal file will be written (default = /tmp/persistit_test_data) buffersize
* - emulated journal buffer size (default = 64M)
*
*
* @author peter
*
*/
public class JournalManagerBench {
private final byte[] NULLS = new byte[65536];
private final String[] ARG_TEMPLATE = new String[] { "duration|int:10:10:86400|Duration of test in seconds",
"policy|String:HARD|Commit policy: SOFT, HARD or GROUP",
"datapath|String:/tmp/persistit_test_data|Datapath property",
"buffersize|int:64:1:1024|Emulated journal buffer size in MBytes",
"extension|int:0:0:1024|MBytes by which to extend file when full",
"prealloc|int:0:0:1024|Preallocated file size in MBytes",
"align|int:1:1:65536|Blocking factor for I/O size",
"recsize|int:123:64:65536|Emulated transaction record size" };
final ByteBuffer buffer;
final ArgParser ap;
private File file;
private FileChannel fc;
private long writeAddress = 0;
private long currentAddress = 0;
long count = 0;
long minTime = Long.MAX_VALUE;
long maxTime = Long.MIN_VALUE;
final byte[] bytes = new byte[65536];
public static void main(final String[] args) throws Exception {
final JournalManagerBench jmb = new JournalManagerBench(args);
jmb.runTest();
}
private JournalManagerBench(final String[] args) throws Exception {
ap = new ArgParser("JournalManagerBench", args, ARG_TEMPLATE).strict();
buffer = ByteBuffer.allocate(ap.getIntValue("buffersize") * 1024 * 1024);
}
@SuppressWarnings("resource")
private void runTest() throws Exception {
file = new File(ap.getStringValue("datapath"), "JManBench_TestFile");
fc = new RandomAccessFile(file, "rw").getChannel();
preallocateFile(ap.getIntValue("prealloc") * 1024 * 1024);
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) ('-');
}
final int align = ap.getIntValue("align");
final long extension = ap.getIntValue("extension") * 1024 * 1024;
final long start = System.nanoTime();
final long expires = start + ap.getIntValue("duration") * NS_PER_S;
long now = System.nanoTime();
while (now < expires) {
doOneCycle(now - start, align, extension, 100);
final long then = System.nanoTime();
count++;
minTime = Math.min(minTime, then - now);
maxTime = Math.max(maxTime, then - now);
now = then;
}
final long elapsed = now - start;
System.out.printf("%,d commits took %,dms at a rate of %,d/second minimum time=%,dns maximumTime=%,dns\n",
count, elapsed, (count * NS_PER_S) / elapsed, minTime, maxTime);
}
private void preallocateFile(final long size) throws Exception {
if (size > 0 && fc.size() > size) {
System.out.printf("Truncating file %s from %,d to %,d\n", file, fc.size(), size);
fc.truncate(size);
} else if (fc.size() < size) {
System.out.printf("Preallocating file %s to size %,d ", file, size);
while (true) {
long remaining = size - fc.size();
if (remaining <= 0) {
break;
}
if (remaining > buffer.capacity()) {
remaining = buffer.capacity();
final long unaligned = fc.size() % 16384;
if (unaligned > 0) {
remaining = remaining - (16384 - unaligned);
}
}
buffer.position(0).limit((int) remaining);
fc.write(buffer, fc.size());
System.out.print(".");
}
fc.force(true);
System.out.println("done");
}
}
private void doOneCycle(final long time, final int align, final long extension, final int size) throws Exception {
// Make a fake transaction record
final String header = String.format("\nsize=%06d count=%06d time=%012d\n", size, count, time);
final byte[] b = header.getBytes();
System.arraycopy(b, 0, bytes, 0, b.length);
// Add the record, possibly offset to maintaining alignment
final int toRewrite = (int) (currentAddress - writeAddress);
buffer.position(toRewrite);
buffer.put(bytes, 0, size);
boolean extended = false;
int position = buffer.position();
// If extension is needed, add those bytes
long currentSize;
if (extension > 0 && writeAddress + buffer.position() > (currentSize = fc.size())) {
long newSize = currentSize + extension;
if (newSize - writeAddress > buffer.capacity()) {
newSize = writeAddress + buffer.capacity();
assert newSize > currentSize;
}
int add = (int) (newSize - writeAddress - buffer.position());
while (add > 0) {
buffer.put(NULLS, 0, Math.min(NULLS.length, add));
add -= NULLS.length;
}
extended = true;
}
// Write and force the buffer
buffer.flip();
fc.write(buffer, writeAddress);
fc.force(extended);
// Align the bytes to the beginning of the buffer as needed
currentAddress = writeAddress + position;
buffer.limit(position);
position = (position / align) * align;
buffer.position(position);
buffer.compact();
writeAddress += position;
}
}