在JDK 1.4版本中,新增加了nio包,目前在於提升IO速度。 不過大家都知道,增加了nio包之後,舊的io包其實也進行了重寫。就算不顯示的使用nio包,也可以明顯的感覺到速度的提升。
而且很多人在使用io包的時候,也只是知道裝飾一個Buffer的InputStream或者OutputStream,速度會更快。
那麼,在這幾者之間,速度上到底有差距沒?差距有多大?我們將進行一次IO操作的效能測試。
測試的IO操作為,普通的檔案讀寫(不帶Buffer),帶Buffer的檔案讀寫,使用nio的管道的普通檔案讀寫,使用nio的管道的隨機檔案讀寫。
先寫一個TestIO測試類別。
/** * 測試IO的抽象類別 * @author wing * @date 2012/7/22 */public abstract class TestIO { private long start, time;public void testTime(){start = System.nanoTime();test();time = System.nanoTime() - start;System.out.format("%.3f\n", time/1.0e9);}protected abstract void test();}
用來測試test方法的操作耗時。
然後是各種IO操作的FileIO類。
package org.wing.nio.test;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.RandomAccessFile;import java.nio.IntBuffer;import java.nio.channels.FileChannel;import java.nio.channels.FileChannel.MapMode;/** * IO和NIO檔案操作 * @author wing * @date 2012/7/22 */public class FileIO { private static final int count = 400000;private static final int bufferSize = 400000; /** * 不使用Buffer封裝的輸出資料流寫入檔案 */ public static void writeIO(String fileName){ DataOutputStream mDos = null; try {mDos = new DataOutputStream(new FileOutputStream(new File(fileName)));for(int i = 0; i < count; i++){mDos.writeInt(1);}} catch (FileNotFoundException e) {System.out.println("File Not Found:" + e.toString());} catch (IOException e) {System.out.println("IO Exception:" + e.toString());}finally{ if(mDos != null){try {mDos.close();} catch (IOException e) {System.out.println("Stream Close Exception:" + e.toString());} }} } /** * 不使用Buffer封裝輸入資料流讀入檔案 */ public static void readIO(String fileName){ DataInputStream mDis = null; try {mDis = new DataInputStream(new FileInputStream(new File(fileName)));for(int i = 0; i < count; i++){mDis.readInt();}} catch (FileNotFoundException e) {System.out.println("File Not Found:" + e.toString());} catch (IOException e) {System.out.println("IO Exception:" + e.toString());}finally{ if(mDis != null){try {mDis.close();} catch (IOException e) {System.out.println("Stream Close Exception:" + e.toString());} }} } /** * 使用Buffer封裝的輸出資料流寫入檔案 */ public static void writeIOWithBuffer(String fileName){ DataOutputStream mDos = null; try {mDos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(new File(fileName))));for(int i = 0; i < count; i++){mDos.writeInt(1);}} catch (FileNotFoundException e) {System.out.println("File Not Found:" + e.toString());} catch (IOException e) {System.out.println("IO Exception:" + e.toString());}finally{ if(mDos != null){try {mDos.close();} catch (IOException e) {System.out.println("Stream Close Exception:" + e.toString());} }} } /** * 使用Buffer封裝輸入資料流讀入檔案 */ public static void readIOWithBuffer(String fileName){ DataInputStream mDis = null; try {mDis = new DataInputStream(new BufferedInputStream(new FileInputStream(new File(fileName))));for(int i = 0; i < count; i++){mDis.readInt();}} catch (FileNotFoundException e) {System.out.println("File Not Found:" + e.toString());} catch (IOException e) {System.out.println("IO Exception:" + e.toString());}finally{ if(mDis != null){try {mDis.close();} catch (IOException e) {System.out.println("Stream Close Exception:" + e.toString());} }} } /** * 使用NIO管道來進行資料寫入 */ public static void writeNIO(String fileName){ FileChannel mFc = null; try {mFc = new FileOutputStream(new File(fileName)).getChannel();IntBuffer mBuffer = IntBuffer.allocate(bufferSize);for(int i = 0; i < count; i++){mBuffer.put(1);}} catch (FileNotFoundException e) {System.out.println("File Not Found:" + e.toString());}finally{ if(mFc != null){try {mFc.close();} catch (IOException e) {System.out.println("Channel Close Exception:" + e.toString());} }} } /** * 使用NIO管道來進行資料讀取 */ public static void readNIO(String fileName){ FileChannel mFc = null; try { mFc = new FileInputStream(new File(fileName)).getChannel(); IntBuffer mBuffer = IntBuffer.allocate(bufferSize);for(int i = 0; i < count; i++){mBuffer.get();}} catch (FileNotFoundException e) {System.out.println("File Not Found:" + e.toString());}finally{ if(mFc != null){try {mFc.close();} catch (IOException e) {System.out.println("Channel Close Exception:" + e.toString());} }} } /** * 使用NIO管道來進行資料寫入 */ public static void writeNIOWithRan(String fileName){ FileChannel mFc = null; try {mFc = new RandomAccessFile(fileName, "rw").getChannel();IntBuffer mBuffer = mFc.map(MapMode.READ_WRITE, 0, 4 * bufferSize).asIntBuffer();for(int i = 0; i < count; i++){mBuffer.put(1);}} catch (FileNotFoundException e) {System.out.println("File Not Found:" + e.toString());} catch (IOException e) {System.out.println("IO Exception:" + e.toString());}finally{ if(mFc != null){try {mFc.close();} catch (IOException e) {System.out.println("Channel Close Exception:" + e.toString());} }} } /** * 使用NIO管道來進行資料讀取 */ public static void readNIOWithRan(String fileName){ FileChannel mFc = null; try {mFc = new RandomAccessFile(fileName, "rw").getChannel();IntBuffer mBuffer = mFc.map(MapMode.READ_WRITE, 0, 4 * bufferSize).asIntBuffer();for(int i = 0; i < count; i++){mBuffer.get();}} catch (FileNotFoundException e) {System.out.println("File Not Found:" + e.toString());} catch (IOException e) {System.out.println("IO Exception:" + e.toString());}finally{ if(mFc != null){try {mFc.close();} catch (IOException e) {System.out.println("Channel Close Exception:" + e.toString());} }} } }
大家可以看到,上面有各種IO的操作。操作次數是讀寫int類型400000次。
然後是我們的主類。
package org.wing.nio.test;public class MainClass {public static void main(String[] args) {final String fileName = "test";TestIO[] testList = new TestIO[] { new TestIO() {@Overrideprotected void test() {FileIO.writeIO(fileName);}}, new TestIO() {@Overrideprotected void test() {FileIO.readIO(fileName);}}, new TestIO() {@Overrideprotected void test() {FileIO.writeIOWithBuffer(fileName);}}, new TestIO() {@Overrideprotected void test() {FileIO.readIOWithBuffer(fileName);}}, new TestIO() {@Overrideprotected void test() {FileIO.writeNIO(fileName);}}, new TestIO() {@Overrideprotected void test() {FileIO.readNIO(fileName);}},new TestIO() {@Overrideprotected void test() {FileIO.writeNIOWithRan(fileName);}}, new TestIO() {@Overrideprotected void test() {FileIO.readNIOWithRan(fileName);}},};for(TestIO testIO : testList){testIO.testTime();}}}
建立了一個TestIO類的數組,分別重寫test方法,使用自己的IO操作。然後迴圈執行TestIO的testTime方法。
運行程式,看看結果。
大家可以看到,在不使用Buffer裝飾的IO操作中,進行400000次的int寫操作耗時5.764秒,讀操作3.301秒。而使用Buffer裝飾的IO操作讀0.021秒,寫0.020秒。 但是直接使用nio的管道進行普通檔案和隨機讀寫檔案的IO操作,耗時更小,寫操作僅0.010秒,讀操作僅0.006秒。
在第一次測試中,大家可以很明顯的看出來,使用Buffer裝飾的IO操作已經遠遠的超過了不使用Buffer裝飾的IO操作的速度。而在400000次操作中與nio包的管道讀寫速度相差無幾。
那麼,io包跟nio包的IO讀寫速度就真的差不多嗎?
我們進行下一輪的測試。
注釋掉,不使用Buffer裝飾的TestIO類的建立,因為我們要增加IO操作次數了,不使用Buffer裝飾的IO操作將會極為耗時。
我們將IO操作次數改為4000000次。
運行查看結果。
大家可以看到,在4000000次的IO操作中,buffer的普通IO操作讀寫耗時在0.6秒左右,而使用管道的IO操作,讀寫時間在0.05秒左右,使用隨機訪問檔案的管道的IO操作讀寫時間在0.065秒左右。
那麼,我們繼續增加IO操作次數,增加到40000000次。
運行程式,看看結果。
大家可以看到,增加了IO操作次數之後,io包裡帶buffer的IO操作,寫操作耗時達到了5.8秒,而讀操作達到了1.7秒!!但是使用nio包管道的IO操作,寫操作耗時僅0.33秒,讀操作耗時僅0.27秒。使用隨機訪問檔案的管道的IO操作耗時略微高一點,寫操作0.57秒,讀操作0.4秒。
由此可以看出,即使在jdk 1.4之後重寫了io包,提升了效率。但是在達到一定的IO次數之後,io包與nio包管道操作的效率的差距已經越來越大了。而使用隨機訪問檔案的普通IO操作,雖然耗時略高一點,但整體不受太大影響。不過我這裡沒進行seek操作,然後讀寫。估計還是會影響比較大的效率。
當然,在我們日常開發中,使用io包的裝飾buffer的IO操作已經夠用了。不過這也從另外一方面顯示出了nio包的強大與高效。
測試可能有很多地方不正確,但整體應該沒有很大的問題,大家看看即可。
轉載請註明出處:http://blog.csdn.net/ml3947