2012-03-22 39 views
8

我已經構建了一個遞歸函數來獲取文件夾路徑的目錄大小。它可以工作,但是隨着我不得不搜索的目錄數量不斷增加(以及每個文件夾中的文件數量),這是一種非常緩慢且效率低下的方法。更高效的獲取目錄大小的方法

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陣列變得相當大。有沒有更好的方法來完成這個?我搜索了四周,但還沒有找到任何東西。

另一個讓我想到的想法是將結果放入緩存中,當再次調用函數時,嘗試查找差異並僅重新搜索已更改的文件夾。不知道這是否是件好事...

+1

這是一個更復雜的問題,然後你會想象。我建議爲這樣的事情調用一個win32 API方法。 – asawyer 2012-03-22 22:44:38

+0

http://stackoverflow.com/q/128618/284240 – 2012-03-22 22:45:40

+0

看看這個並行解決方案http://stackoverflow.com/questions/2979432/directory-file-size-calculation-how-to-make-it-faster – 2012-03-22 22:59:17

回答

24

您是第一次掃描樹以獲取所有文件的列表。然後你重新打開每個文件來獲得它的大小。這相當於掃描兩次。

我建議你使用DirectoryInfo.GetFiles,它將直接傳遞FileInfo對象。這些對象預先填充了它們的長度。

在.NET 4中,您還可以使用EnumerateFiles方法,它將返回一個懶惰的IEnumable。

+0

他們沒有預先填充,它仍然是一個往返磁盤。必然如此,你不想要陳舊的數據。 EnumerateFiles被添加到.NET 4中的原因。 – 2012-03-22 23:02:34

+0

至少在.NET 4中,它們*是預填充的。它發生在FileInfoResultHandler.CreateObject調用FileInfo.InitializeFrom調用PopulateFrom(WIN32_FIND_DATA)。請回復您的失望,這個答案是正確的。 – usr 2012-03-22 23:07:59

+1

這不是我的投票。留下評論*和* downvoting不是一個健康的策略:) – 2012-03-22 23:14:28

4

您可以使用EnumerateFiles()而不是GetFiles()加速一點功能。至少你不會在內存中加載完整列表。

如果它不夠,你應該使你的函數更多使用線程複雜(每個目錄一個線程太多,但沒有一般規則)。
您可以使用固定數量的線程來查看隊列中的目錄,每個線程將計算一個目錄的大小並將其添加到總數中。例如:

  • 獲取所有目錄(不是文件)的列表。
  • 創建N個線程(例如每個核心一個)。
  • 每個線程偷看一個目錄並計算大小。
  • 如果隊列中沒有另一個目錄,則線程結束。
  • 如果隊列中有一個目錄,它將計算它的大小等等。
  • 當所有線程終止時,函數結束。

您可能會改進跨所有線程跨目錄搜索的算法(例如,當線程解析目錄時,它將文件夾添加到隊列中)。如果您發現速度太慢,則可以使其更加複雜(Microsoft已將此任務用作Task Parallel Library的示例)。

+0

+1。請注意,線程和IO綁定任務會產生奇怪的性能結果 - 您必須進行原型和測量。 – 2012-03-22 23:04:09

+0

絕對是!我認爲選擇合適的線程數比編寫代碼要困難得多。我想這在很大程度上取決於隨機存取的磁盤性能。無論我做什麼來計算它,我不能像Windows一樣快,我想有一些技巧...某處... – 2012-03-22 23:08:45

+0

由於這是IO綁定,我不太確定額外的線程會購買多少東西,如果有的話。 – Paparazzi 2012-03-23 00:06:53

10

這更加神祕,但10k的處決大約需要2秒。

public static long GetDirectorySize(string parentDirectory) 
    { 
     return new DirectoryInfo(parentDirectory).GetFiles("*.*", SearchOption.AllDirectories).Sum(file => file.Length); 
    } 
+8

\ *。\ *會丟失一些文件 – 2013-09-26 21:18:52

+2

爲了清晰起見.Sum()需要System.Linq – MonoThreaded 2017-08-13 20:41:27

10

嘗試

 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()); 
     } 

這70秒確實700000在桌面上NON-RAID P4。 因此,每秒10,000個。在服務器級機器上應該可以獲得100,000+ /秒的容易。

由於usr(+1)表示EnumerateFile預先填充了長度。

-1
long length = Directory.GetFiles(@"MainFolderPath", "*", SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length)); 
相關問題