1. 程式人生 > >.NET CORE下最快比較兩個檔案內容是否相同的方法 - 續

.NET CORE下最快比較兩個檔案內容是否相同的方法 - 續

在上一篇博文中, 我使用了幾種方法試圖找到哪個是.NET CORE下最快比較兩個檔案的方法.文章釋出後,引起了很多博友的討論, 在此我對大家的支援表示由衷的感謝.

其中也有博友提出了對於我最後使用ReadOnlySpan的方法的結果的懷疑, 認為它的結果快的不正常, 幾乎超出了磁碟IO速度的限制. 對此我要深刻的進行反省------我把ReadOnlySpan放在最後執行,使它利用了磁碟快取,而大大加快了比較速度, 當發現這點時,我立即取消釋出了之前的博文,並重新設計了整個測試方案, 使用更嚴謹公平的方式測試每個比較方法, 並寫下此篇博文以正視聽.

另外, 在重新測試的過程中, 也充分採取了博友的意見, 改進了以下幾點:

  • 全部測試使用BenchmarkDotNet來進行

    為了更專業公平的結果, 重構程式碼使用BenchmarkDotNet來測試

  • 嘗試不同快取大小

    為了充分測試不同快取大小對速度的影響, 位元組陣列的快取大小分為3種, 分別是:

    • 4096 * 10
    • 4096 * 100
    • 4096 * 1000
  • 嘗試使用非同步IO方法

    以觀察非同步IO對速度的影響

  • 執行前清除磁碟快取

    當然這是本篇博文最重要的一點. 這裡利用了Win32API在每個比較方法執行開始時清除磁碟快取(程式碼見文後,如果程式碼需要在Windows以外的平臺上執行, 需要自行實現清除快取的方法)

關於比較方法的原理請參看上一篇博文,這裡就不再贅述, 只是展示結果:


BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=2.2.401
  [Host]     : .NET Core 2.2.6 (CoreCLR 4.6.27817.03, CoreFX 4.6.27818.02), 64bit RyuJIT
  DefaultJob : .NET Core 2.2.6 (CoreCLR 4.6.27817.03, CoreFX 4.6.27818.02), 64bit RyuJIT

Method buffer_size Mean Error StdDev Median
CompareByMD5 40960 3.294 s 0.0608 s 0.0539 s 3.311 s
CompareByMD5Async 40960 4.723 s 0.0137 s 0.0128 s 4.720 s
CompareByToInt64 40960 4.883 s 0.0140 s 0.0131 s 4.886 s
CompareByByteArray 40960 4.713 s 0.0059 s 0.0052 s 4.714 s
CompareByByteArrayAsync 40960 4.687 s 0.0070 s 0.0066 s 4.688 s
CompareByString 40960 5.491 s 0.1066 s 0.0997 s 5.483 s
CompareBySequenceEqual 40960 5.185 s 0.1028 s 0.1337 s 5.180 s
CompareByWin32API 40960 4.334 s 0.0209 s 0.0195 s 4.331 s
CompareByReadOnlySpan 40960 4.316 s 0.0209 s 0.0195 s 4.313 s
CompareByReadOnlySpanAsync 40960 4.699 s 0.0235 s 0.0220 s 4.695 s
CompareByMD5 409600 3.329 s 0.0639 s 0.0808 s 3.334 s
CompareByMD5Async 409600 4.727 s 0.0192 s 0.0179 s 4.720 s
CompareByToInt64 409600 4.881 s 0.0111 s 0.0104 s 4.879 s
CompareByByteArray 409600 3.017 s 0.0583 s 0.0798 s 3.014 s
CompareByByteArrayAsync 409600 3.038 s 0.0935 s 0.1370 s 2.996 s
CompareByString 409600 5.086 s 0.0871 s 0.0815 s 5.075 s
CompareBySequenceEqual 409600 5.019 s 0.0978 s 0.0915 s 4.998 s
CompareByWin32API 409600 3.048 s 0.1061 s 0.1263 s 3.017 s
CompareByReadOnlySpan 409600 3.079 s 0.0862 s 0.1264 s 3.045 s
CompareByReadOnlySpanAsync 409600 2.976 s 0.0484 s 0.0452 s 2.988 s
CompareByMD5 4096000 3.456 s 0.0850 s 0.2410 s 3.369 s
CompareByMD5Async 4096000 4.766 s 0.0412 s 0.0385 s 4.762 s
CompareByToInt64 4096000 5.003 s 0.0789 s 0.0659 s 4.998 s
CompareByByteArray 4096000 2.558 s 0.0505 s 0.1055 s 2.607 s
CompareByByteArrayAsync 4096000 2.500 s 0.0492 s 0.0766 s 2.508 s
CompareByString 4096000 6.024 s 0.0655 s 0.0613 s 6.020 s
CompareBySequenceEqual 4096000 4.949 s 0.0793 s 0.0742 s 4.931 s
CompareByWin32API 4096000 2.582 s 0.0511 s 0.0881 s 2.620 s
CompareByReadOnlySpan 4096000 2.677 s 0.0503 s 0.0420 s 2.666 s
CompareByReadOnlySpanAsync 4096000 2.460 s 0.0492 s 0.0657 s 2.458 s

"buffers_size"即是位元組陣列快取的大小

"Mean"列即平均耗時, 數值越小越好

這次我測試的檔案大小是500MB, 從資料中我們能觀察到以下現象:

  • CompareByMD5方法因為未使用位元組陣列快取,所以在各組測試中的結果基本穩定.甚至在40960組中是最快的方法
  • CompareByString在所有組中表現最差, 第2差的是CompareBySequenceEqual, 所以我都沒有動力為它們寫非同步方法
  • 使用位元組陣列快取的方法, 基本上是隨著快取的增大, 速度越快.
  • 非同步方法在快取最大那組(409600)中才開始有勝出的結果, 不過整體來看意義並不大
  • CompareByReadOnlySpan同樣表現優異, 這讓我對上一篇博文中犯的錯安心了很多
  • CompareByByteArray在各組中都相當有競爭力, 幾乎與CompareByReadOnlySpan並駕齊驅.不過當命中磁碟快取時, CompareByReadOnlySpan會像開掛一樣速度起飛.
  • CompareByWin32API也非常出色, 不過只能在Windows平臺使用

結論:

  • 最簡單的是MD5, 速度也可以接受
  • 最樸實的是ByteArray, 程式碼通俗易懂, 速度出色
  • 最實用的還是CompareByReadOnlySpan, 速度出色, 還可利用磁碟快取

程式碼放在GITHUB上, 其中清除磁碟的快取需要管理員許可權, 所以需要以管理員許可權執行Visual Studio

關於檔案比較的方法希望通過這篇博文能得到正確的結論了, 也歡迎廣大博友積極評論!