C#中的高性能文件复制?

我有一个大约500k jpg文件的庞大目录,我想归档所有早于某个日期的文件。 目前,该脚本需要数小时才能运行。

这与GoGrid的存储服务器非常糟糕的性能有很大关系,但与此同时,我确信有一种方法可以更有效地实现Ram / Cpu,以实现我正在做的事情。

这是我的代码:

var dirInfo = new DirectoryInfo(PathToSource); var fileInfo = dirInfo.GetFiles("*.*"); var filesToArchive = fileInfo.Where(f => f.LastWriteTime.Date = StopThresholdInDays.Days().Ago().Date ); foreach (var file in filesToArchive) { file.CopyTo(PathToTarget+file.Name); } 

Days()。Ago()的东西只是语法糖。

我认为你可以改进的唯一部分是dirInfo.GetFiles("*.*") 。 在.NET 3.5及更早版本中,它返回一个包含所有文件名的数组,这需要时间来构建并使用大量RAM。 在.NET 4.0中,有一个新的Directory.EnumerateFiles方法,它返回一个IEnumerable ,并在从磁盘读取结果时立即获取结果。 这可以提高性能,但不要指望奇迹……

您应该考虑使用第三方实用程序为您执行复制。 像robocopy这样的东西可能会显着加快你的处理速度。 另请参见https://serverfault.com/questions/54881/quickest-way-of-moving-a-large-number-of-files

我会记住80/20规则并注意,如果减速的大部分是file.CopyTo ,并且这种减速远远超过LINQ查询的性能,那么我不担心。 您可以通过删除file.CopyTo行并将其替换为Console.WriteLine操作来测试此操作。 与真实副本相比的时间。 你会发现GoGrid与其余操作的开销。 我的预感是你不会有任何现实的大收获

编辑 :好的,所以80%是GetFiles操作,如果实际上目录中有一百万个文件,这并不奇怪。 您最好的选择可能是直接开始使用Win32 API(如FindFirstFile和系列 )和P / Invoke :

 [DllImport("kernel32.dll", CharSet=CharSet.Auto)] static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData); 

如果可能的话,我还建议改变目录结构以减少每个目录的文件数量。 这将极大地改善这种情况。

EDIT2 :我还考虑从GetFiles("*.*")更改为GetFiles() 。 既然你要求一切,那么在每一步都应用全局规则是没有意义的。

您可以尝试使用(有限数量的)线程来执行CopyTo()。 目前整个操作仅限于1个核心。

如果它现在受CPU限制,这只会提高性能。 但如果它在RAID上运行,它可能会起作用。

听听这个Hanselminutes播客 。 斯科特与Banshee媒体播放器的作者Aaron Bockover谈话,他们讨论了这个问题并在播客的8:20谈论它。

如果您可以使用.Net 4.0,那么请使用Thomas Levesque提到的Directory.EnumerateFiles。 如果没有,那么您可能需要编写自己的目录行走代码,就像使用本机Win32 API在Mono.Posix中一样。