Environment
mac 10.14
2.2 GHz Intel Core i7
APPLE SSD AP0512M (a partner below shows the opposite result, which is related to the hard drive)
problem description
when you look at the RocketMQ source code, you can see that there are two ways to write data to MappedFile:
- write writeBuffer first, then write writeBuffer to FileChannel and then call force () to flush disk;
- data is directly written to MappedByteBuffer, and force () is called to brush the disk.
my question is why not just use the second method? So I verified the write performance in two ways through the following code.
related codes
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class MMapTest {
static File file= new File("./test.txt");
static ByteBuffer buffer;
static int fileSize = 8 * 1024 * 1024;
static boolean del = true;
public static void main(String[] args) {
init(1);
deleteFile();
int[] sizes = {128,256,512,4096,8192,1024*16,1024*32,1024*128,1024*512};
try {
for (int size : sizes) {
testDBChannel(size);
testMappedByteBuffer(size);
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void init(int size) {
buffer = ByteBuffer.allocateDirect(size);
}
private static void deleteFile() {
file.delete();
}
private static void testDBChannel(int size) throws IOException {
init(size);
RandomAccessFile rw = new RandomAccessFile(file, "rw");
FileChannel channel = rw.getChannel();
int writeSize = 0;
Long start = System.currentTimeMillis();
while (writeSize < fileSize) {
buffer.clear();
buffer.put(new byte[size]);
buffer.flip();
channel.position(writeSize);
channel.write(buffer);
channel.force(false);
writeSize += size;
}
//channel.force(false);
System.out.println("DirectBuffer + FileChannel write " + size + " bytes every time cost: " + (System.currentTimeMillis() - start) + "ms");
if(del)
deleteFile();
}
private static void testMappedByteBuffer(int size) throws IOException {
init(size);
RandomAccessFile rw = new RandomAccessFile(file, "rw");
FileChannel channel = rw.getChannel();
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);
int writeSize = 0;
Long start = System.currentTimeMillis();
while (writeSize < fileSize) {
map.put(new byte[size]);
map.force();
writeSize += size;
}
//map.force();
System.out.println("MappedByteBuffer write " + size + " bytes every time cost: " + (System.currentTimeMillis() - start) + "ms");
if(del)
deleteFile();
}
}
output:
DirectBuffer + FileChannel write 128 bytes every time cost: 3577ms
MappedByteBuffer write 128 bytes every time cost: 13518ms
DirectBuffer + FileChannel write 256 bytes every time cost: 1968ms
MappedByteBuffer write 256 bytes every time cost: 7044ms
DirectBuffer + FileChannel write 512 bytes every time cost: 1001ms
MappedByteBuffer write 512 bytes every time cost: 3037ms
DirectBuffer + FileChannel write 1024 bytes every time cost: 659ms
MappedByteBuffer write 1024 bytes every time cost: 1274ms
DirectBuffer + FileChannel write 4096 bytes every time cost: 214ms
MappedByteBuffer write 4096 bytes every time cost: 331ms
DirectBuffer + FileChannel write 8192 bytes every time cost: 137ms
MappedByteBuffer write 8192 bytes every time cost: 168ms
DirectBuffer + FileChannel write 16384 bytes every time cost: 77ms
MappedByteBuffer write 16384 bytes every time cost: 86ms
DirectBuffer + FileChannel write 32768 bytes every time cost: 44ms
MappedByteBuffer write 32768 bytes every time cost: 58ms
DirectBuffer + FileChannel write 131072 bytes every time cost: 16ms
MappedByteBuffer write 131072 bytes every time cost: 25ms
DirectBuffer + FileChannel write 524288 bytes every time cost: 10ms
MappedByteBuffer write 524288 bytes every time cost: 21ms
my understanding is that both methods write data to pageCache and then refresh the disk. Why does it take so much time? what is the implementation principle of the two methods?
in general, the use of RocketMQ is asynchronous flushing, and the pageCache mechanism of OS will be used to achieve high performance. The problem described above is aimed at synchronous flushing. According to the first test of @ Tyrael, the performance of mbb is higher than that of db+fc, which makes me wonder why we don"t use mbb directly.