复制许多小文件(不通过网络)时.net File.Copy非常慢

时间:2012-07-06 22:41:23

标签: c# .net windows performance copy

我正在为自己制作一个简单的文件夹同步备份工具,并使用File.Copy遇到了相当大的障碍。进行测试将大约44,000个小文件(Windows邮件文件夹)的文件夹复制到我系统中的另一个驱动器时,我发现使用File.Copy比使用命令行并运行xcopy复制相同的文件/文件夹慢3倍。我的C#版本需要超过16分钟来复制文件,而xcopy只需要5分钟。我试图在这个主题上寻求帮助,但我发现所有人都在抱怨通过网络缓慢复制大文件的文件。这既不是大文件问题,也不是网络复制问题。

我发现了一个interesting article about a better File.Copy replacement,但是发布的代码有一些错误会导致堆栈出现问题,而且我的知识还远远不足以解决代码中的问题。

是否有任何常见或简单的方法可以更快速地替换File.Copy?

5 个答案:

答案 0 :(得分:8)

要考虑的一件事是您的副本是否具有在复制期间更新的用户界面。如果是这样,请确保您的副本在单独的线程上运行,或者在复制过程中您的UI都将冻结,并且通过阻止调用来更新UI将减慢副本速度。

我编写了一个类似的程序,根据我的经验,我的代码比Windows资源管理器副本运行得更快(不确定命令提示符中的xcopy)。

此外,如果您有UI,请不要更新每个文件;而是更新每个X兆字节或每个Y文件(以先到者为准),这样可以将更新量保持在UI实际可以处理的范围内。我使用了每个.5MB或10个文件;这些可能不是最佳的,但它显着提高了我的复制速度和UI响应能力。

另一种加快速度的方法是使用枚举函数而不是获取函数(例如EnumerateFiles而不是GetFiles)。这些函数尽快开始返回结果,而不是在列表构建完成后等待返回所有内容。它们返回一个Enumerable,所以你可以在结果上调用foreach:foreach(System.IO.Directory.EnumerateDirectories(path))中的字符串文件。对于我的程序,这也在速度方面产生了明显的差异,并且在像你这样的情况下会更有帮助处理包含许多文件的目录。

答案 1 :(得分:4)

在旋转磁盘上减慢IO操作的一个原因是移动磁盘头。

可以合理地假设并且可能非常准确的是,您的许多小文件(彼此都相关)在磁盘上比靠近副本的目的地更靠近(假设您&#39 ;从磁盘的一部分复制到同一磁盘的另一部分)。如果你复制一点然后写一点,你打开一个机会窗口,让其他进程在源磁盘或目标磁盘上移动磁盘头。

XCopy比复制(在两种情况下都是命令)做得更好的一件事是XCopy在开始将这些文件写到目的地之前读入一堆文件。

如果要在同一磁盘上复制文件,请尝试分配一个大缓冲区以同时读入多个文件,然后在缓冲区已满时写出这些文件。

如果您正在从一个磁盘读取并写入另一个磁盘,请尝试启动一个线程以从源磁盘读取,并尝试另一个线程写入另一个磁盘。

答案 2 :(得分:2)

有两种算法可以更快地复制文件:

如果源和目标是不同的磁盘那么:

  • 一个线程连续读取文件并存储在缓冲区中。
  • 另一个线程从该缓冲区连续写入文件。

如果源和目标是相同的磁盘,那么:

  • 读取一个固定的字节块,一次说8K,无论有多少文件。
  • 将固定的块写入目标,可以是一个文件,也可以是多个文件。

通过这种方式,您将获得显着的性能。

替代方法是从.net代码调用xcopy。为什么要使用File.Copy来做这件事。您可以使用Process.StandardOutput捕获xcopy输出并在屏幕上显示,以向用户显示正在进行的操作。

答案 3 :(得分:1)

我认为你至少可以平行化它,这样你就可以同时做两个文件。当一个线程正在写另一个线程时,已经可以读取下一个文件。如果你有一个文件列表,你可以这样做。使用多个线程无济于事,因为这会使驱动器移动更多而不是能够顺序写入。

 var files = new List<string>();
 // todo: fill the files list using directoryenumeration or so...
 var po = new ParallelOptions() {MaxDegreeOfParallelism = 2};
 Parallel.ForEach(files, po, CopyAFile);

 // Routine to copy a single file
 private void CopyAFile(string file) { }

答案 4 :(得分:0)

我在这个级别没有很好的经验。为什么不尝试运行包含xcopy命令的批处理文件?查看此帖子:Executing Batch File in C#