获取目录大小的更有效方法
我已经构建了一个递归函数来获取文件夹路径的目录大小。 它的工作原理,但随着我必须搜索的目录数量不断增加(以及每个相应文件夹中的文件数量),这是一种非常缓慢,低效的方法。
static string GetDirectorySize(string parentDir) { long totalFileSize = 0; string[] dirFiles = Directory.GetFiles(parentDir, "*.*", System.IO.SearchOption.AllDirectories); foreach (string fileName in dirFiles) { // Use FileInfo to get length of each file. FileInfo info = new FileInfo(fileName); totalFileSize = totalFileSize + info.Length; } return String.Format(new FileSizeFormatProvider(), "{0:fs}", totalFileSize); }
这是在所有子目录中搜索参数路径,因此dirFiles
数组变得非常大。 有没有更好的方法来实现这一目标? 我已经四处寻找,但还没有找到任何东西。
我想到的另一个想法是将结果放入缓存中,当再次调用该函数时,尝试查找差异并仅重新搜索已更改的文件夹。 不确定这是不是一件好事……
您首先扫描树以获取所有文件的列表。 然后,您将重新打开每个文件以获得其大小。 这相当于扫描两次。
我建议你使用DirectoryInfo.GetFiles
,它将直接传递你的FileInfo
对象。 这些物体预先填充了它们的长度。
在.NET 4中,您还可以使用EnumerateFiles
方法,该方法将返回惰性IEnumable
。
这更加神秘,但10k执行需要大约2秒钟。
public static long GetDirectorySize(string parentDirectory) { return new DirectoryInfo(parentDirectory).GetFiles("*.*", SearchOption.AllDirectories).Sum(file => file.Length); }
尝试
DirectoryInfo DirInfo = new DirectoryInfo(@"C:\DataLoad\"); Stopwatch sw = new Stopwatch(); try { sw.Start(); Int64 ttl = 0; Int32 fileCount = 0; foreach (FileInfo fi in DirInfo.EnumerateFiles("*", SearchOption.AllDirectories)) { ttl += fi.Length; fileCount++; } sw.Stop(); Debug.WriteLine(sw.ElapsedMilliseconds.ToString() + " " + fileCount.ToString()); } catch (Exception Ex) { Debug.WriteLine(Ex.ToString()); }
这在台式机NON-RAID P4上在70秒内完成了70万次。 所以像每秒10,000。 在服务器类机器上应该容易获得100,000+ /秒。
正如usr(+1)所说,EnumerateFile预先填充了长度。
您可以使用EnumerateFiles()
而不是GetFiles()
开始加速您的function。 至少你不会在内存中加载完整列表。
如果它还不够,你应该使用线程使你的函数更复杂(每个目录一个线程太多但没有一般规则)。
您可以使用固定数量的线程从队列中查看目录,每个线程计算目录的大小并添加到总计。 就像是:
- 获取所有目录(不是文件)的列表。
- 创建N个线程(例如,每个核心一个)。
- 每个线程都会查看目录并计算大小。
- 如果队列中没有另一个目录,则该线程结束。
- 如果队列中有目录,则计算其大小,依此类推。
- 当所有线程终止时,函数结束。
您可以改进跨越所有线程搜索目录的算法(例如,当线程解析目录时,它会将文件夹添加到队列中)。 如果您认为它太慢,则由您决定使其变得更复杂(Microsoft已将此任务用作新 任务并行库的示例)。
long length = Directory.GetFiles(@"MainFolderPath", "*", SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length));