// Copyright © 2011-2013, Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
package fi.jumi.core.ipc;
import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
public class MmfIpcSpike {
private static final int MESSAGES = 10 * 1000 * 1000;
private static final byte CAN_WRITE = 0;
private static final byte CAN_READ = 1;
private static final int FILE_SIZE = 1024 * 1024;
private static final int TAG_INDEX = 0;
private static final int DATA_INDEX = 1; // same cache line
// private static final int DATA_INDEX = FILE_SIZE - 4; // different cache line
// ways of creating a memory barrier
public static volatile boolean volatileField;
public static final AtomicBoolean atomicBoolean = new AtomicBoolean();
private static MappedByteBuffer openMemoryMappedFile() throws IOException {
RandomAccessFile file = new RandomAccessFile("MmfIpcSpike.tmp", "rw");
return file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, FILE_SIZE);
}
private static void loopWait() throws Exception {
atomicBoolean.lazySet(true);
// atomicBoolean.set(true);
// volatileField = true;
// Thread.yield();
// LockSupport.parkNanos(1);
// Thread.sleep(1);
// ## IPC latency results ##
//
// Intel Core 2 Quad Q6600 @ 3.0GHz (Kentsfield), Windows 7 64-bit
// 1 cache line 2 cache lines
// lazySet()
// set()
// volatile 99.9 ns 125.55 ns
// yield() 208.25 ns
// parkNanos(1) 695000.0 ns
// sleep(1) 704000.0 ns
// none deadlock deadlock
//
// Intel Core i7 860 @ 2.8GHz (Lynnfield), Windows 7 64-bit, jdk1.7.0_17 64-bit
// 1 cache line 2 cache lines
// lazySet() 68.85 ns 91.9 ns
// set() 47.7 ns 95.0 ns
// volatile 47.65-78.6 ns 91.65 ns
// yield() 74.1 ns 110.3 ns
// parkNanos(1) 640900.0 ns 691700.0 ns
// sleep(1) 711150.0 ns 655000.0 ns
// none deadlock deadlock
}
public static class Writer {
public static void main(String[] args) throws Exception {
MappedByteBuffer map = openMemoryMappedFile();
for (int value = 0; value < MESSAGES; value++) {
while (map.get(TAG_INDEX) != CAN_WRITE) {
loopWait();
}
map.putInt(DATA_INDEX, value);
map.put(TAG_INDEX, CAN_READ);
}
}
}
public static class Reader {
public static void main(String[] args) throws Exception {
MappedByteBuffer map = openMemoryMappedFile();
long start = System.currentTimeMillis();
for (int expected = 0; expected < MESSAGES; expected++) {
while (map.get(TAG_INDEX) != CAN_READ) {
loopWait();
}
int value = map.getInt(DATA_INDEX);
assertThat(value, is(expected));
map.put(TAG_INDEX, CAN_WRITE);
}
long end = System.currentTimeMillis();
long durationMs = end - start;
double nsPerMessage = durationMs * 1000000.0 / MESSAGES;
System.out.println("read " + MESSAGES + " messages in " + durationMs + " ms");
System.out.println(nsPerMessage + " ns/roundtrip, " + (nsPerMessage / 2) + " ns IPC latency");
}
}
}