Search code examples
javaniomemory-mapped-files

Performance of MappedByteBuffer vs ByteBuffer


I'm trying to do a few performance enhancements and am looking to use memory mapped files for writing data. I did a few tests and surprisingly, MappedByteBuffer seems slower than allocating direct buffers. I'm not able to clearly understand why this would be the case. Can someone please hint at what could be going on behind the scenes? Below are my test results:

I'm allocating 32KB buffers. I've already created the files with sizes 3Gigs before starting the tests. So, growing the file isn't the issue.

Test results DirectBuffer vs MappedByteBuffer

I'm adding the code that I used for this performance test. Any input / explanation about this behavior is much appreciated.

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;

public class MemoryMapFileTest {

    /**
     * @param args
     * @throws IOException 
     */
    public static void main(String[] args) throws IOException { 

        for (int i = 0; i < 10; i++) {
            runTest();
        }

    }   

    private static void runTest() throws IOException {  

        // TODO Auto-generated method stub
        FileChannel ch1 = null;
        FileChannel ch2 = null;
        ch1 = new RandomAccessFile(new File("S:\\MMapTest1.txt"), "rw").getChannel();
        ch2 = new RandomAccessFile(new File("S:\\MMapTest2.txt"), "rw").getChannel();

        FileWriter fstream = new FileWriter("S:\\output.csv", true);
        BufferedWriter out = new BufferedWriter(fstream);


        int[] numberofwrites = {1,10,100,1000,10000,100000};
        //int n = 10000;
        try {
            for (int j = 0; j < numberofwrites.length; j++) {
                int n = numberofwrites[j];
                long estimatedTime = 0;
                long mappedEstimatedTime = 0;

                for (int i = 0; i < n ; i++) {
                    byte b = (byte)Math.random();
                    long allocSize = 1024 * 32;

                    estimatedTime += directAllocationWrite(allocSize, b, ch1);
                    mappedEstimatedTime += mappedAllocationWrite(allocSize, b, i, ch2);

                }

                double avgDirectEstTime = (double)estimatedTime/n;
                double avgMapEstTime = (double)mappedEstimatedTime/n;
                out.write(n + "," + avgDirectEstTime/1000000 + "," + avgMapEstTime/1000000);
                out.write("," + ((double)estimatedTime/1000000) + "," + ((double)mappedEstimatedTime/1000000));
                out.write("\n");
                System.out.println("Avg Direct alloc and write: " + estimatedTime);
                System.out.println("Avg Mapped alloc and write: " + mappedEstimatedTime);

            }


        } finally {
            out.write("\n\n"); 
            if (out != null) {
                out.flush();
                out.close();
            }

            if (ch1 != null) {
                ch1.close();
            } else {
                System.out.println("ch1 is null");
            }

            if (ch2 != null) {
                ch2.close();
            } else {
                System.out.println("ch2 is null");
            }

        }
    }


    private static long directAllocationWrite(long allocSize, byte b, FileChannel ch1) throws IOException {
        long directStartTime = System.nanoTime();
        ByteBuffer byteBuf = ByteBuffer.allocateDirect((int)allocSize);
        byteBuf.put(b);
        ch1.write(byteBuf);
        return System.nanoTime() - directStartTime;
    }

    private static long mappedAllocationWrite(long allocSize, byte b, int iteration, FileChannel ch2) throws IOException {
        long mappedStartTime = System.nanoTime();
        MappedByteBuffer mapBuf = ch2.map(MapMode.READ_WRITE, iteration * allocSize, allocSize);
        mapBuf.put(b);
        return System.nanoTime() - mappedStartTime;
    }

}

Solution

  • You're testing the wrong thing. This is not how to write the code in either case. You should allocate the buffer once, and just keep updating its contents. You're including allocation time in the write time. Not valid.