Java学习笔记程序员

java 四种io实现速度对比

2017-03-02  本文已影响571人  徐士林
import java.io.*;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class SpeedTest {
    private static final String INPUT_FILE_PATH = "io_speed.pdf";
    private static final String OUTPUT_FILE_PATH = "io_speed_copy.pdf";

    public static void main(String[] args) {
        long ioStreamTime1 = ioStreamCopy();
        System.out.println("io stream copy:" + ioStreamTime1);

        long ioStreamTime2 = bufferedStreamCopy();
        System.out.println("buffered stream copy:" + ioStreamTime2);

        long ioStreamTime3 = nioStreamCopy();
        System.out.println("nio stream copy:" + ioStreamTime3);

        long ioStreamTime4 = nioMemoryStreamCopy();
        System.out.println("nio memory stream copy:" + ioStreamTime4);
    }

    private static long ioStreamCopy() {
        long costTime = -1;
        FileInputStream is = null;
        FileOutputStream os = null;
        try {
            long startTime = System.currentTimeMillis();
            is = new FileInputStream(INPUT_FILE_PATH);
            os = new FileOutputStream(OUTPUT_FILE_PATH);
            int read = is.read();
            while (read != -1) {
                os.write(read);
                read = is.read();
            }
            long endTime = System.currentTimeMillis();
            costTime = endTime - startTime;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return costTime;
    }

    private static long bufferedStreamCopy() {
        long costTime = -1;
        FileReader reader = null;
        FileWriter writer = null;
        try {
            long startTime = System.currentTimeMillis();
            reader = new FileReader(INPUT_FILE_PATH);
            writer = new FileWriter(OUTPUT_FILE_PATH);
            int read = -1;
            while ((read = reader.read()) != -1) {
                writer.write(read);
            }
            writer.flush();
            long endTime = System.currentTimeMillis();
            costTime = endTime - startTime;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return costTime;
    }

    private static long nioStreamCopy() {
        long costTime = -1;
        FileInputStream is = null;
        FileOutputStream os = null;
        FileChannel fi = null;
        FileChannel fo = null;
        try {
            long startTime = System.currentTimeMillis();
            is = new FileInputStream(INPUT_FILE_PATH);
            os = new FileOutputStream(OUTPUT_FILE_PATH);
            fi = is.getChannel();
            fo = os.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (true) {
                buffer.clear();
                int read = fi.read(buffer);
                if (read == -1) {
                    break;
                }
                buffer.flip();
                fo.write(buffer);
            }
            long endTime = System.currentTimeMillis();
            costTime = endTime - startTime;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fi != null) {
                    fi.close();
                }
                if (fo != null) {
                    fo.close();
                }
                if (is != null) {
                    is.close();
                }
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return costTime;
    }

    private static long nioMemoryStreamCopy() {
        long costTime = -1;
        FileInputStream is = null;
        //映射文件输出必须用RandomAccessFile
        RandomAccessFile os = null;
        FileChannel fi = null;
        FileChannel fo = null;
        try {
            long startTime = System.currentTimeMillis();
            is = new FileInputStream(INPUT_FILE_PATH);
            os = new RandomAccessFile(OUTPUT_FILE_PATH, "rw");
            fi = is.getChannel();
            fo = os.getChannel();
            IntBuffer iIb = fi.map(FileChannel.MapMode.READ_ONLY, 0, fi.size()).asIntBuffer();
            IntBuffer oIb = fo.map(FileChannel.MapMode.READ_WRITE, 0, fo.size()).asIntBuffer();
            while (iIb.hasRemaining()) {
                int read = iIb.get();
                oIb.put(read);
            }
            long endTime = System.currentTimeMillis();
            costTime = endTime - startTime;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fi != null) {
                    fi.close();
                }
                if (fo != null) {
                    fo.close();
                }
                if (is != null) {
                    is.close();
                }
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return costTime;
    }
}

程序来源

复制大小约90MB的文件所需的时间(单位:毫秒)

io stream copy:182797
buffered stream copy:14699
nio stream copy:1251
nio memory stream copy:884

很显然传统的IO方式效率很低,甚至于差了nio两个数量级。nio内存映射访问速度最快。

java I/O的工作机制

data-buffering-at-os-level.png

上图很简单的显示了数据传输的指示图,当应用进程执行到read()操作时操作系统底层一下操作

  1. 内核通知DMA读取某块数据
  2. 磁盘控制器通过DMA将数据读入到内核的缓冲区
  3. 当缓冲完成是,内核会把数据从内核缓冲区读到进程指定缓冲区。

这一系列操作涉及到底层调用,而jvm进程属于用户进程,一旦涉及到系统调用就会从用户态切换到内核态。这个切换也是一个相当耗时的操作。

其实还有比较重要的是,用户进程一次读取一个字节或者读到一个字节数组中,但是内核通过磁盘控制器一般是读取某一块或者是某几块的数据。
不然每次都进如内核态不是一个很愚蠢的事情呒?所以第一次read()的时候会访问到磁盘数据,后续可能直接从内核缓冲中拿数据。

如果数据不可用,process将会被挂起,并需要等待内核从磁盘上把数据取到内核缓冲区中。

由于DMA不能直接访问用户空间(用户缓冲区),普通IO操作需要将数据来回地在 用户缓冲区 和 内核缓冲区移动,这在一定程序上影响了IO的速度,并且这是一种阻塞式的数据读取方式,并不能很有效的利用cpu。

而java io流中的缓冲流输入输出时都会把数据储存一个数组中,也就是进程的缓冲区(默认是8192字节),这样就可以等数据满了以后一次性切换到内核态并写入磁盘,相比于io流的多次切换到内核态,90M的文件读写就可以提升一个数量级了。

IO 是基于流来读取的,而NIO则是基于块读取,面向流 的 I/O 系统一次一个字节地处理数据。 面向块 的 I/O 系统以块的形式处理数据。按块处理数据比按(流式的)字节处理数据要快得多。 所以NIO通过buffer和channel读取数据又要比IO流快上一个数量级。并且 NIO 支持 Direct Memory, 可以减少一次数据拷贝。可见通过FileChannel读取数据还是有很大优势的,并且NIO可以通过selector实现异步读取的操作。

那么速度最快的内存映射又是什么呢?


715283-20160804114034684-1256880404.png

首先NIO提供了内存映射或者说是直接内存,这可以使得数据少一次数据的拷贝,又可以节省大量时间。

内核空间和用户空间会映射到同一块物理内存,这样减少一次数据的拷贝,所以这种方式的文件操作速度会非常快(和文件大小也有关系,并没有绝对好的方式)
MappedByteBuffer可以通过java.nio.channels.FileChannel的 map方法创建。具体关于MappedByteBuffer还是可以在写一篇博客详细论述的。

这一篇就到这吧,写的越多发现自己不会的就越多,以后再写博客来填了这些坑吧

参考

上一篇下一篇

猜你喜欢

热点阅读