通过零拷贝技术拷贝一个文件究竟有多快

概念

零拷贝作为文件拷贝技术,它是指的一类技术,而不是一技术框架之类的东西,它通常的实现方式有两种,一个是DMA(direct memory access)翻译过来叫直接内存访问,是一种硬件层面的技术,它的原理是在进行文件拷贝时不需要cpu的介入,可以直接把文件从内存里面拷贝到另一个硬件中,比如磁盘、或者网卡中,另一种技术就是MMAP(Memory Mapping)是一种将文件或其他资源映射到进程的地址空间的技术,使得进程可以像访问普通内存一样访问这些资源,它则是属于软件层面的技术,相比于传统的文件拷贝需要CPU的介入,文件数据需要再内核空间和用户空间拷贝几次,零拷贝技术在文件拷贝时具有明显的优势。

实验

下面我们来通过代码来进行实验,同样拷贝一个从idea官网下载下来的zip文件(768M),对比一下三种拷贝的效率:

    private File file = new File("D:\\java开发\\ideaIU-2019.3.5.win.zip");
    private String targetPath = "D:\\java_code\\filecopy\\";

1、传统的拷贝方式:平均时间在9秒左右

    @Test
    public void testInputstream() throws Exception {
        FileInputStream fileInputStream = new FileInputStream(file);
        File targetFile = new File(targetPath + "inputstream.zip");
        FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
        byte[] bytes = new byte[1024];
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        while (fileInputStream.read(bytes) != -1) {
            fileOutputStream.write(bytes);
        }
        stopWatch.stop();
        System.err.println(stopWatch.toString());
    }

 2、使用BufferedOutputStream缓冲输出流拷贝方式:平均时间在2秒4左右,BufferedOutputStream默认会用一个8kb的数组作为缓冲区,这样就不用每次写入一个字节都去通过系统调用来刷盘,可以减少io,自然比第一种方式快,但是我试过调整这个缓冲区的大小,耗时区别不大

    @Test
    public void testBuffedInputstream() throws Exception {
        FileInputStream fileInputStream = new FileInputStream(file);
        File targetFile = new File(targetPath + "bufferedinputstream.zip");
        FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream, 8192 * 4);
        byte[] bytes = new byte[1024];
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        while (fileInputStream.read(bytes) != -1) {
            bufferedOutputStream.write(bytes);
        }
        bufferedOutputStream.flush();
        stopWatch.stop();
        System.err.println(stopWatch.toString());
    }

3、MMAP方式:平均时间在2秒左右

@Test
    public void testMmap() throws Exception {
        FileChannel sourceChannel = new RandomAccessFile(file, "r").getChannel();
        File targetFile = new File(targetPath + "mmap.zip");
        FileChannel targetChannel = new RandomAccessFile(targetFile, "rw").getChannel();
        FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
        long size = sourceChannel.size();
        // 创建源文件和目标文件的 MappedByteBuffer
        MappedByteBuffer sourceBuffer = sourceChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
        MappedByteBuffer targetBuffer = targetChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
        // 使用 MappedByteBuffer 拷贝文件
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (long i = 0; i < size; i++) {
            byte b = sourceBuffer.get();
            targetBuffer.put(b);
        }
        stopWatch.stop();
        System.err.println(stopWatch.toString());
    }

 4、DMA方式(这个不确定是不是用的DMA,因为是硬件层面的技术,无法直接验证,取决于操作系统和硬件):平均时间在1秒左右

    @Test
    public void testDma() throws Exception {
        File targetFile = new File(targetPath + "dma.zip");
        FileChannel sourceChannel = new FileInputStream(file).getChannel();
        FileChannel targetChannel = new FileOutputStream(targetFile).getChannel();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 使用transferFrom或transferTo方法拷贝文件
//        targetChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
        // 或者
        sourceChannel.transferTo(0, sourceChannel.size(), targetChannel);
        stopWatch.stop();
        System.err.println(stopWatch.toString());
    }

 总结:

1、零拷贝技术明显比传统的文件拷贝要快的多,而基于硬件层面实现的DMA要比软件层面的MMAP更快

2、适用场景:不需要cpu介入做运算的,比如mq消息中间件,消费者在消费消息的时候,mq服务器只需要把消息读出来发送给消费者,而不需要做运算,比较有名的kafka就使用了DMA,而rocketmq就使用了MMAP

猜你喜欢

转载自blog.csdn.net/qq_17805707/article/details/132233044