java利用“對應檔訪問”(MapperByteBuffer)處理檔案與單純利用Buffer來處理檔案的快慢比較,bytebuffer
處理檔案是java經常使用的操作,在對一個“大檔案”(比如超過64M)進行操作時一點點速度的提高都會帶來效能的巨大提升。然而我們經常使用的BufferxxStream,來直接處理大檔案時,往往力不從心。
java中“對應檔訪問”機制則解決了這一問題,它把大檔案的較小部分先放在記憶體裡,將其餘待讀取的資料仍然放在硬碟裡面。但是我們完全可以通過這樣一個機制把這個大檔案當作非常大的數組來使用。是不是很像作業系統裡面對於應用的使用方法?沒錯,其實java正是利用了底層作業系統的檔案對應工具來最大化提高效能。
一般為了即能讀又能寫,我們使用RandomAccessFile來進行測試,同過該檔案上的通道,然後調用FileChannel中的map()所產生的MappedByteBuffer,在map方法中有三個參數map(FileChannel.MapMode mode, long position, long size)。
其中mode 來決定映射的檔案訪問的讀/寫機制,可以設定MapMode.READ_ONLY, MapMode.READ_WRITE,MapMode.PRIVATE,通過名字可以看得出他們的作用,特別要注意MapMode.PRIVATE方法,如何你操作不當會導致你操作的檔案無法被共用。
而position為地區內的檔案對應的位置開始,必須是非負數
size為地區的大小是映射;必須是非負數,不大於Integer.MAX_VALUE
其實position和size的設定就意味著我們映射某個大檔案的較小的部分
下面就來看我們使用了“對應檔訪問”時,的效能提升把!
以下是代碼部分:
1 package com.company; 2 3 import java.io.*; 4 import java.nio.IntBuffer; 5 import java.nio.channels.FileChannel; 6 7 /** 8 * Created by chunmiao on 17-3-15. 9 */ 10 public class MappedIOTest { 11 //輸入值的數量 12 private static int numOfInts = 4000000; 13 //測試讀寫值的數量大小 14 private static int numOfUbuffInts = 200000; 15 16 private abstract static class Tester{ 17 private String name; 18 public Tester(String name){ 19 this.name = name; 20 } 21 22 //測試計時 23 public void runTest(){ 24 System.out.println(name + ": "); 25 try { 26 long start = System.nanoTime(); 27 test(); 28 long runtime = System.nanoTime() - start; 29 //輸出test()已耗用時間,來判定操作時間大小 30 System.out.format("%.2f\n",runtime/1.0e9); 31 }catch (IOException e){ 32 throw new RuntimeException(e); 33 } 34 } 35 36 public abstract void test() throws IOException; 37 } 38 39 private static Tester[] tests = { 40 //直接寫入資料 41 new Tester("Stream Write") { 42 @Override 43 public void test() throws IOException { 44 DataOutputStream dos = new DataOutputStream( 45 new BufferedOutputStream( 46 new FileOutputStream(new File("temp.tmp")))); 47 for (int i = 0; i < numOfInts; i ++){ 48 dos.writeInt(i); 49 } 50 dos.close(); 51 } 52 }, 53 //使用MappedByteBuffer進行寫檔案操作 54 new Tester("Mapped Write") { 55 @Override 56 public void test() throws IOException { 57 FileChannel fc = new RandomAccessFile("temp.tmp","rw").getChannel(); 58 //fc.size()返回的是以byte為單位的數量值。 59 IntBuffer ib = fc.map(FileChannel.MapMode.READ_WRITE,0,fc.size()).asIntBuffer(); 60 for(int i =0 ; i < numOfInts ; i ++){ 61 try { 62 ib.put(i); 63 }catch (Exception e){ 64 System.out.println(i); 65 } 66 } 67 fc.close(); 68 } 69 }, 70 //直接讀資料 71 new Tester("Stream Read") { 72 @Override 73 public void test() throws IOException { 74 DataInputStream dis = new DataInputStream( 75 new BufferedInputStream(new FileInputStream("temp.tmp"))); 76 for (int i = 0 ; i < numOfInts ; i ++){ 77 dis.readInt(); 78 } 79 dis.close(); 80 } 81 }, 82 //使用MappedByteBuffer進行讀檔案操作 83 new Tester("Mapped Read") { 84 @Override 85 public void test() throws IOException { 86 FileChannel fc = new FileInputStream(new File("temp.tmp")).getChannel(); 87 IntBuffer ib = fc.map(FileChannel.MapMode.READ_ONLY,0,fc.size()).asIntBuffer(); 88 while (ib.hasRemaining()){ 89 ib.get(); 90 } 91 fc.close(); 92 } 93 }, 94 //直接進行讀/寫操作 95 new Tester("Stream Read/Write") { 96 @Override 97 public void test() throws IOException { 98 RandomAccessFile raf = new RandomAccessFile(new File("temp.tmp"),"rw"); 99 //對資料進行進行錯位,以便同時進行讀寫操作100 raf.writeInt(1);101 for (int i = 0 ; i < numOfUbuffInts ; i ++){102 //之所以減4的原因是raf.length()返回以byte為單位的空間大小,而1 (int) = 4 (byte),因此需要減4103 raf.seek(raf.length() - 4);104 raf.writeInt(raf.readInt());105 }106 raf.close();107 }108 },109 //使用MappedByteBuffer進行讀/寫操作110 new Tester("Mapped Read/Write") {111 @Override112 public void test() throws IOException {113 FileChannel fc = new RandomAccessFile(new File("temp.tmp"),"rw").getChannel();114 IntBuffer ib = fc.map(FileChannel.MapMode.READ_WRITE,0,fc.size()).asIntBuffer();115 ib.put(0);116 for (int i = 1 ; i < numOfUbuffInts ; i ++){117 //對資料進行進行錯位,以便同時進行讀寫操作118 ib.put(ib.get(i - 1));119 }120 fc.close();121 }122 }123 };124 125 public static void main(String[] args) {126 //進行測試127 for (Tester tester : tests){128 tester.runTest();129 }130 }131 }
以下是運行結果: