2017-05-23 26 views
0

我正在編寫一個控制檯應用程序,該應用程序遍歷二叉樹並根據其md5校驗和搜索新文件或更改文件。 整個過程可以很快接受(對於~70.000個文件,14秒),但生成校驗和需要大約5分鐘,這相當慢...創建文件校驗和時的性能問題

有關改進此過程的任何建議?我的哈希函數如下:

private string getMD5(string filename) 
     { 
      using (var md5 = new MD5CryptoServiceProvider()) 
      { 
       if (File.Exists(@filename)) 
       { 
        try 
        { 
         var buffer = md5.ComputeHash(File.ReadAllBytes(filename)); 
         var sb = new StringBuilder(); 
         for (var i = 0; i < buffer.Length; i++) 
         { 
          sb.Append(buffer[i].ToString("x2")); 
         } 
         return sb.ToString(); 
        } 
        catch (Exception) 
        { 
         Program.logger.log("Error while creating checksum!", Program.logger.LOG_ERROR); 
         return ""; 
        } 
       } 
       else 
       { 
        return ""; 
       } 
      } 
     } 
+0

您可以使用ComputeHash(Stream)重載並傳遞文件流,而不是將文件內容讀入內存。 – Lee

+0

@李,這使我的代碼甚至運行得更慢..但感謝您的意圖 – Tobi

回答

2

那麼,接受的答案是無效的,因爲當然,有一種方法可以提高您的代碼性能。但是對於其他一些想法是有效的)

這裏主要停止,除了磁盤I/O,是內存分配。在這裏,應該提高速度的一些想法:

  • 不讀內存進行計算整個文件,它是緩慢的,它會通過LOH的物體產生大量的內存壓力。改爲以流的形式打開文件,然後通過塊計算哈希值。
  • 的原因,爲什麼你使用ComputeHash流覆蓋的時候,因爲在內部它使用非常小的緩衝區(4KB),因此選擇合適緩衝區大小有放緩(256KB以上,最優值通過實驗發現)
  • 使用TransformBlockTransformFinalBlock函數來計算散列值。您可以傳遞null作爲outputBuffer參數。
  • 重複使用該緩衝區進行後續文件哈希計算,因此不需要額外的分配。
  • 此外,您可以重複使用MD5CryptoServiceProvider,但好處是可疑的。
  • 最後,您可以應用異步模式從流中讀取塊,因此當您計算前一個塊的部分散列時,OS將同時從磁盤讀取下一個塊。當然,這樣的代碼更難編寫,並且至少需要兩個緩衝區(也可以重複使用它們),但它可以對速度產生很大影響。
  • 作爲一項小改進,不要檢查文件是否存在。我相信,你的函數是從某個枚舉中調用的,而且幾乎沒有機會,那個文件同時被刪除。

以上所有內容適用於大中型文件。如果你有很多非常小的文件,你可以通過並行處理文件來加速計算。實際上,並行化也可以幫助處理大文件,但它需要進行測量。最後,如果碰撞不會打擾你太多,那麼你可以選擇比較便宜的哈希算法,例如CRC。

+0

感謝您的建議,立即開始實施! :) – Tobi

1

爲了創建哈希,您必須讀取文件的每個最後一個字節。所以這個操作是磁盤限制的,而不是CPU限制的,並且與文件大小成比例地進行縮放。多線程將無濟於事。

除非FS能以某種方式計算並存儲散列給你,否則就沒有辦法加快速度。你依賴於FS爲你追蹤變化。

一般來說,檢查「已更改文件」(如備份例程)不要準確計算Hashvalue。他們仍然可以計算並存儲它以進行驗證,但就是這樣。

除非用戶做了一些嚴重的(NTFS驅動程序加載級別)破壞,文件大小的「上次更改」日期足以檢測到更改。也許還可以檢查存檔位,但是現在很少使用該存檔位。

這些類型的場景(列表文件和處理它們)的較小改進是使用「Enumerate Files」而不是列表文件。但是在14秒的清單/ 5分鐘的處理過程中,它們沒有任何相關的效果。