Question

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;
    }

}
Was it helpful?

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.

OTHER TIPS

Swapping data to disk is the main reason for MappedByteBuffer to be slower than DirectByteBuffer. cost of allocation and deallocation is high with direct buffers , including MappedByteBuffer, and this is cost is accrued to both the examples hence the only difference in writing to disk , which is the case with MappedByteBuffer but not with Direct Byte Buffer

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top