为什么 nio.files.copy 比 nio.fileChannel 复制慢很多?

Why does nio.files.copy is a lot slower than nio.fileChannel copy?

我是初学者 Java 程序员。
今天,我练习了如何在 java 中复制文件并尝试按照本教程进行操作 http://www.journaldev.com/861/4-ways-to-copy-file-in-java
完成本教程后,我 运行 使用 JMH Benchmark 来检查性能,其中包含一个 57MB 的 txt 文件。
而且nioFiles和NIOChannel之间的性能差距比我预期的还要大

Benchmark                                   Mode  Cnt   Score   Error  Units
CompressTest.fileCopyUsingNIOChannelClass  thrpt   10  22.465 ± 2.996  ops/s
CompressTest.fileCopyWithNIOFiles          thrpt   10   0.843 ± 0.488  ops/s

这是我使用的代码

    @Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
    @Measurement(iterations = 10, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
    @Fork(1)
    @State(Scope.Benchmark)
    public class CompressTest
    {
    final static Path source = Paths.get("c:/temp/system.out.lambda.txt");
    final static Path target = Paths.get("c:/temp/copied.lambda.txt");

    public static void main(String[] args) throws RunnerException, IOException {
        Main.main(args);
    }

    @Benchmark
    public static void fileCopyWithNIOFiles() throws IOException{
        Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
    }
    @Benchmark
    public static void fileCopyUsingNIOChannelClass() throws IOException
        {
            File fileToCopy = new File("c:/temp/system.out.lambda.txt");
            FileInputStream inputStream = new FileInputStream(fileToCopy);
            FileChannel inChannel = inputStream.getChannel();

            File newFile = new File("c:/temp/testcopied.txt");
            FileOutputStream outputStream = new FileOutputStream(newFile);
            FileChannel outChannel = outputStream.getChannel();

            inChannel.transferTo(0, fileToCopy.length(), outChannel);
            inputStream.close();
            outputStream.close();
        }
}

所以我想问一下,我做错了吗?或者你能解释为什么会这样吗?


有人问,所以我尝试了另一个文件。 348MB的avi文件,结果如下。

Benchmark                                   Mode  Cnt  Score   Error  Units
CompressTest.fileCopyUsingNIOChannelClass  thrpt   10  3.142 ± 0.738  ops/s
CompressTest.fileCopyWithNIOFiles          thrpt   10  1.991 ± 0.350  ops/s

使用 nioFile 复制文件仍然比使用 NIOChannel 慢。


我刷新了所有内容并再次测试,这就是结果。使用 57MB 的 txt 文件并将预热迭代设置为 10。

Benchmark                                   Mode  Cnt   Score   Error  Units
CompressTest.fileCopyUsingNIOChannelClass  thrpt   10  23.442 ± 3.224  ops/s
CompressTest.fileCopyWithNIOFiles          thrpt   10  12.328 ± 2.128  ops/s

这个结果比第一个更容易接受,但是nioFile复制的速度仍然是NIOChannel的将近一半。

P.S。 : 如果有更好的复制文件的方法,请告诉我。我真的很想了解更多关于 java,谢谢。

TL;DR - This is the caching issue

当 运行 基准测试打开任务管理器中的 性能 选项卡时,您将看到不同之处。

Files.copy 测试期间,您可能会发现磁盘写入速度很高。但是当 transferTo test 是 运行 时,磁盘将几乎空闲!这意味着实际上没有数据写入设备 - 复制是在内存中执行的。而且这显然要快得多。

Java Files.copy 方法是使用 CopyFileEx WinAPI 函数实现的。没有明确说明 CopyFileEx 如何在内部工作,但观察到做一些实际的磁盘 I/O.

反过来 transferTo 执行一系列 ReadFile/WriteFile 调用。 WriteFile函数不保证数据立即写入磁盘。它可能会将数据放入 OS 磁盘缓存并稍后在后台执行真实设备 I/O。

我运行 Windows 中的资源监视器在阅读@apangin 的解释和跟随 img 后是帮助我理解的结果。谢谢大家