异步日志 vs. 内存映射文件

高频交易 HFT 系统目标是降低I/O延迟,当你写磁盘或通过网络传输不可避免地你将延迟差性能导入你的系统,一种解决方案是使用NIO (non-blocking I/O). 对于磁盘I/O使用内存映射文件memory-mapped file ,而对于网络I/O 你可以使用非堵塞通道. NIO是一种异步输出,意味着你将字节传交付给OS (i.e. copy them to a direct byte buffer),希望底层OS能够做好这部分工作。

下面以日志记录为案例:

先树立的一个比较样本,如果日志信息在内存中直接拷贝,大概需要多少时间,用ByteBuffer 带来的结果:


String msg = "This is a log message not so small that you can use to test. I hope you have fun and it works well!";
byte[] msgBytes = msg.getBytes();

ByteBuffer bb = ByteBuffer.allocate(1024);

long start = System.nanoTime();
bb.put(msgBytes);
long end = System.nanoTime();

System.out.println(
"Time: " + (end - start));

内存中直接拷贝最短执行时间:
Avg Time: 26.42 nanos

下面引入文件磁盘系统,比较 内存映射文件和异步日志 哪个最快。
1.1MappedByteBuffer内存映射文件带来的延迟


File file = new File("mMapFile.txt");
RandomAccessFile raf = new RandomAccessFile(file,
"rw");
MappedByteBuffer mappedBuffer = raf.getChannel().map(MapMode.READ_WRITE, 0, 1024);

long start = System.nanoTime();
mappedBuffer.put(msgBytes);
long end = System.nanoTime();

System.out.println(
"Time: " + (end - start));

执行时间:
Avg Time: 53.52 nanos


2. Lock-free Queue 无锁队列也就是异步的延迟:


Builder<ByteBuffer> builder = new Builder<ByteBuffer>() {
@[author]Override[/author]
public ByteBuffer newInstance() {
// max log message size is 1024
return ByteBuffer.allocate(1024);
}
};

final BatchingQueue<ByteBuffer> queue =
new AtomicQueue<ByteBuffer>(1024 * 1024, builder);

final WaitStrategy consumerWaitStrategy = new ParkWaitStrategy();

Thread asyncLogger = new Thread(new Runnable() {

@[author]Override[/author]
public void run() {

while (true) {

long avail;
while((avail = queue.availableToPoll()) == 0) {
consumerWaitStrategy.waitForOtherThread();
}

consumerWaitStrategy.reset();

for(int i = 0; i < avail; i++) {
ByteBuffer bb = queue.poll();
bb.flip();
if (bb.remaining() == 0) {
// EOF
return;
} else {
// log your byte buffer here
// any way you want since you
// are not disrupting the main
// thread anymore...
}
bb.clear();
}

queue.donePolling();
}
}
},
"AsyncLogger");

asyncLogger.start();

long start = System.nanoTime();

ByteBuffer bb;
while((bb = queue.nextToDispatch()) == null) {
// busy spin in case queue is full
}
bb.put(msgBytes);
queue.flush(true);
// true = lazySet()

long end = System.nanoTime();

System.out.println(
"Time: " + (end - start));

执行时间:
Avg Time: 30.95 nanos


总结,异步日志时间要比使用内存映射文件委托底层OS操作更快。
异步日志 vs. 内存映射文件Asynchronous logging versus Memory Mapped Files | MentaBlog

你好,banq,这些例子执行的结果是你在你的机器上运行的吗?如果是的话能不能晒下你的配置呢?

-------------------
抱歉没看清,最底下还有个链接。以为是原创,不过翻译的不错。我看的时候都把例子跑了一遍,我机器上的结果都是上万毫微秒。和例子运行的差距甚大,机器配置令人伤心。
[该贴被crixus于2012-12-09 21:48修改过]

我认为前面的理论还是ok的,但一次运行的结果说明不了什么吧,而且结果是ns级别的,机器稍微影响一下这个就差很多了。