Java IO性能测试

在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() {

			@Override
			protected void test() {
				FileIO.writeIO(fileName);
			}
		}, new TestIO() {

			@Override
			protected void test() {
				FileIO.readIO(fileName);
			}
		}, new TestIO() {

			@Override
			protected void test() {
				FileIO.writeIOWithBuffer(fileName);
			}
		}, new TestIO() {

			@Override
			protected void test() {
				FileIO.readIOWithBuffer(fileName);
			}
		}, new TestIO() {

			@Override
			protected void test() {
				FileIO.writeNIO(fileName);
			}
		}, new TestIO() {

			@Override
			protected void test() {
				FileIO.readNIO(fileName);
			}
		},

		new TestIO() {

			@Override
			protected void test() {
				FileIO.writeNIOWithRan(fileName);

			}
		}, new TestIO() {

			@Override
			protected 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

你可能感兴趣的:(Java IO性能测试)