7

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

这与 GoGrid 存储服务器的性能非常差有很大关系,但与此同时,我确信有一种更有效的方式 Ram/Cpu 明智地完成我正在做的事情。

这是我的代码:

var dirInfo = new DirectoryInfo(PathToSource);
var fileInfo = dirInfo.GetFiles("*.*");
var filesToArchive = fileInfo.Where(f => 
    f.LastWriteTime.Date < StartThresholdInDays.Days().Ago().Date
      && f.LastWriteTime.Date >= StopThresholdInDays.Days().Ago().Date
);

foreach (var file in filesToArchive)
{
    file.CopyTo(PathToTarget+file.Name);
}

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

4

6 回答 6

10

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

于 2009-11-04T22:58:48.690 回答
2

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

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

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

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

EDIT2:我也会考虑从GetFiles("*.*")改为 just GetFiles()。既然你要求一切,让它在每一步都应用通配规则是没有意义的。

于 2009-11-04T22:56:26.183 回答
2

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

于 2009-11-04T23:09:52.233 回答
1

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

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

于 2009-11-04T22:53:10.403 回答
0

收听这个Hanselminutes 播客。Scott 与 Banshee 媒体播放器的作者 Aaron Bockover 交谈,他们遇到了这个确切的问题,并在 8:20 在播客中谈论它。

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

于 2009-11-04T23:25:57.050 回答