code_money_guji's Blog

Happy coding

NIO Memory-Mapped Files

参考资料:

     http://www.linuxtopia.org/online_books/programming_books/thinking_in_java/TIJ314_029.htm Thinking in java

    //一个比较好的测试Sample

     Reille_java_NIO

http://www.javadocexamples.com/java/nio/channels/java.nio.channels.FileChannel.MapMode.html  map参数中的 READ_ONLY|READ_WRITE|PRIVATE示例

//JDK 文档的使用示例,有很多的例子.十分有用.

Memory-Mapped Files:

Memory-Mapped Files 允许将文件直接映射到内存中读取. 在map()方法中, 其中的一个参数是:MapMode,有如下说明:

MapMode.READ_ONLY: 但尝试从这种模式写入内容的时候会触发:ReadOnlyBufferException

MapMode.READ_WRITE : 映射读|写,能够把同一个文件的更改结果传播.

MapMode.PRIVATE :  使用Copy-on-write的方式,私有修改.

NOTE:

MapMode.READ_ONLY: 使用这种模式的时候,在同一个文件中的不同buffer之间的更改能够更改文件, 所以其他buffer能够"觉察"到改变.

MapMode.PRIVATE:这种模式下, 由于使用了Copy的方式, 改变的内容不会传播到文件中, 每个buffer中的内容是独立的, 假设有buffer1 和buffer2.那么在buffer1中的改变时不会在buffer2中观察到.  根本原因是这种方式下buffer1中的改变不会回显到文件中.

下面是Thinking in java 中的性能测试代码,除了其能进行结果证明外,其用的测试方法也编写代码的一个规范,感谢作者:

    //: c12:MappedIO.java
    // {Clean: temp.tmp}
    import java.io.*;
    import java.nio.*;
    import java.nio.channels.*;

    public class MappedIO {
      private static int numOfInts = 4000000;
      private static int numOfUbuffInts = 200000;
      private abstract static class Tester {
        private String name;
        public Tester(String name) { this.name = name; }
        public long runTest() {
          System.out.print(name + ": ");
          try {
            long startTime = System.currentTimeMillis();
            test();
            long endTime = System.currentTimeMillis();
            return (endTime - startTime);
          } catch (IOException e) {
            throw new RuntimeException(e);
          }
        }
        public abstract void test() throws IOException;
      }
 //上述的代使用的模板方法很不仅仅是在测试中,在框架使用中处处可见.
      private static Tester[] tests = { 
        new Tester("Stream Write") {
          public void test() throws IOException {
            DataOutputStream dos = new DataOutputStream(
              new BufferedOutputStream(
                new FileOutputStream(new File("temp.tmp"))));
            for(int i = 0; i < numOfInts; i++)
              dos.writeInt(i);
            dos.close();
          }
        }, 
        new Tester("Mapped Write") {
          public void test() throws IOException {
            FileChannel fc = 
              new RandomAccessFile("temp.tmp", "rw")
              .getChannel();
            IntBuffer ib = fc.map(
              FileChannel.MapMode.READ_WRITE, 0, fc.size())
              .asIntBuffer();
            for(int i = 0; i < numOfInts; i++)
              ib.put(i);
            fc.close();
          }
        }, 
        new Tester("Stream Read") {
          public void test() throws IOException {
            DataInputStream dis = new DataInputStream(
              new BufferedInputStream(
                new FileInputStream("temp.tmp")));
            for(int i = 0; i < numOfInts; i++)
              dis.readInt();
            dis.close();
          }
        }, 
        new Tester("Mapped Read") {
          public void test() throws IOException {
            FileChannel fc = new FileInputStream(
              new File("temp.tmp")).getChannel();
            IntBuffer ib = fc.map(
              FileChannel.MapMode.READ_ONLY, 0, fc.size())
              .asIntBuffer();
            while(ib.hasRemaining())
              ib.get();
            fc.close();
          }
        }, 
        new Tester("Stream Read/Write") {
          public void test() throws IOException {
            RandomAccessFile raf = new RandomAccessFile(
              new File("temp.tmp"), "rw");
            raf.writeInt(1);
            for(int i = 0; i < numOfUbuffInts; i++) {
              raf.seek(raf.length() - 4);
              raf.writeInt(raf.readInt());
            }
            raf.close();
          }
        }, 
        new Tester("Mapped Read/Write") {
          public void test() throws IOException {
            FileChannel fc = new RandomAccessFile(
              new File("temp.tmp"), "rw").getChannel();
            IntBuffer ib = fc.map(
              FileChannel.MapMode.READ_WRITE, 0, fc.size())
              .asIntBuffer();
            ib.put(0);
            for(int i = 1; i < numOfUbuffInts; i++)
              ib.put(ib.get(i - 1));
            fc.close();
          }
        }
      };
      public static void main(String[] args) {
        for(int i = 0; i < tests.length; i++)
          System.out.println(tests[i].runTest());
      }
    } ///:~

运行结果[趋势相同,不同配置时间花费会不一样]:
Stream Write: 1719
Mapped Write: 359
Stream Read: 750
Mapped Read: 125
Stream Read/Write: 5188
Mapped Read/Write: 16

 如果是文件太大,不妨考取在使用map()方法的时候,限定length的范围.

 注意点,在 REILLE_JAVA_NIO 一书中,有如下的话:

Accessing a file through the memory-mapping mechanism can be far more efficient than reading or writing data by conventional means, even when using channels. No explicit system calls need to be made, which can be time-consuming. More importantly, the virtual memory system of the operating system automatically caches memory pages. These pages will be cached using system memory and will not consume space from the JVM's memory heap.

从上面的话看出,使用这种方式是映射到OS的虚拟内存中,并不是jvm heap中.

 

结束语:

至于并行操作MappedByteBuffer性能如何,笔者未曾测试,有机会发现应用场景的话会及时更新文章. 也希望大家能够多提出这方面的需求与应用. thanks.

to be continue..

 

 

Nio Scatter/Gather

参考资料:

          Reille_java_NIO 

      http://hg.openjdk.java.net/jdk7/tl/jdk/rev/11ee8b471f9c OpenJDK improve Scatter/Gather implementation

      http://www.javaperformancetuning.com/tips/nio.shtml Java Performance Tuning

 

Nio Scatter/Gather

Scatter/Gather模式能够将从一个Channel读取的信息分散到N个Buufer里面,[Scatter].  同时也能够把N各Buffer里面的东西按照顺序写到一个Channel中.   尤其是在读取有一定格式的消息的时候,把消息头和消息体分别放到相应的Buffer里面进行解析是相当有用的.

 

下面给出一个Channel类结构的一部分:

       

上面的类结构中,ReadableByteChannel 负责读取而WriteByteBufferChannel用于写操作. 其中底层的ScatteringByteChannel和GatheringByteChannel是负责Scatter/Gather模式的接口. 实现了这两个接口就能够相应的实现Scatter/Gather模式. 从接口的结构上来看, ScatteringByteChannel是继承了ReadableByteChannel的,也就是负责"读",而GathringByteChannel是继承了WritableByteChannel的接口,负责"写".  经常使用文件读取的Channel是FIleChannel, 它继承了上述接口中的所有接口,可读/可写, 同时也实现了Scatter/Gather模式.

 

使用示例代码:

RandomAccessFile file = new RandomAccessFile("testFile.txt","rw"); 
FileChannel channel = file.getChannel();

Scatter:
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);

ByteBuffer[] bufferArray = { header, body };

channel.read(buffers);

Gather:
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);

//write data into buffers

ByteBuffer[] bufferArray = { header, body };

channel.write(buffers);

 

[友情提示: NIO是使用块的方式来处理文件,并不是流的方式. 如果要考取并行操作大文件的时候, 可以使用"生产者-消费者"的模型来对一个byteBuffer进行操作,而后考虑使用Gather的方式来进行数据的输出或者读取分析.当然,如果内存允许的情况下,是可以使用NIO 中的Memory-Mapped Files的方式进行文件操作的. 当然,也可以先映射一部分. 详细请参考map(...)方法. ]   

 

to be continnue..