非同步日誌 vs. 記憶體對映檔案

banq發表於2012-12-07
高頻交易 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));
<p class="indent">

記憶體中直接複製最短執行時間:
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));
<p class="indent">

執行時間:
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));
<p class="indent">

執行時間:
Avg Time: 30.95 nanos


總結,非同步日誌時間要比使用記憶體對映檔案委託底層OS操作更快。

非同步日誌 vs. 記憶體對映檔案Asynchronous logging versus Memory Mapped Files | MentaBlog

相關文章