2010-01-31 47 views
6

我正在尋找性能高效的方法來比較兩個字節[]是否相等。尺寸大於1MB,所以每個數組元素的開銷應該最小化。沒有綁定檢查的C#字節[]比較

我的目標是打敗SequenceEqualhand-coded for-loop over every item,通過avoiding the repetitive bound checks兩個陣列的速度。如同Array.Copy可能導致快速memcpy一樣,什麼會導致memcmp

+0

你需要比較兩個塊還是一個塊?也許如果你更多地告訴我們你正在做的這個場景,甚至可以找到更好的解決方案?例如,如果您需要將塊序列與許多其他塊進行比較,那麼一個簡單的散列函數至少會爲您提供很多保證的差異,並且只需很少的工作,然後您就可以專注於潛在的誤報。 – 2010-01-31 22:39:06

回答

12

如果性能真的很重要,然後做到這一點是通過使用包含在每個版本的Windows CRT庫的最快方法。此代碼發生在我的筆記本電腦狹小〜51毫秒,工作在64位機器太:

using System; 
using System.Runtime.InteropServices; 
using System.Diagnostics; 

class Program { 
    static void Main(string[] args) { 
    byte[] arr1 = new byte[50 * 1024 * 1024]; 
    byte[] arr2 = new byte[50 * 1024 * 1024]; 
    var sw = Stopwatch.StartNew(); 
    bool equal = memcmp(arr1, arr2, arr1.Length) == 0; 
    sw.Stop(); 
    Console.WriteLine(sw.ElapsedMilliseconds); 
    Console.ReadLine(); 
    } 
    [DllImport("msvcrt.dll")] 
    private static extern int memcmp(byte[] arr1, byte[] arr2, int cnt); 
} 
+1

+1。還有其他的東西,比如CRT版本中可能考慮的內存對齊。不要在不安全的代碼中重新發明輪子是要走的路。當然,只有在分析並證明它是值得的 - 標準免責聲明之後。 – 2010-01-31 22:24:51

+0

+1。使用經過良好測試的優化程序比使用自己的程序更好,希望它能以某種方式在您碰巧運行的任何平臺上快速運行。 – 2010-01-31 22:43:06

+0

別忘了將陣列固定到位! – 2010-02-22 18:53:52

16

您可以使用不安全的代碼來執行指針操作。您可以一次爲整數比較字節四:

public static bool ArrayCompare(byte[] a, byte[] b) { 
    if (a.Length != b.Length) return false; 
    int len = a.Length; 
    unsafe { 
    fixed(byte* ap = a, bp = b) { 
     int* aip = (int*)ap, bip = (int*)bp; 
     for (;len >= 4;len-=4) { 
     if (*aip != *bip) return false; 
     aip++; 
     bip++; 
     } 
     byte* ap2 = (byte*)aip, bp2 = (byte*)bip; 
     for (;len>0;len--) { 
     if (*ap2 != *bp2) return false; 
     ap2++; 
     bp2++; 
     } 
    } 
    } 
    return true; 
} 

一個測試,這對一個簡單的循環,而且速度更快,約六倍。根據喬什·愛因斯坦的建議,long可以在64位系統上使用。實際上,它似乎是幾乎快一倍都在32位和64位系統:

public static bool ArrayCompare64(byte[] a, byte[] b) { 
    if (a.Length != b.Length) return false; 
    int len = a.Length; 
    unsafe { 
    fixed (byte* ap = a, bp = b) { 
     long* alp = (long*)ap, blp = (long*)bp; 
     for (; len >= 8; len -= 8) { 
     if (*alp != *blp) return false; 
     alp++; 
     blp++; 
     } 
     byte* ap2 = (byte*)alp, bp2 = (byte*)blp; 
     for (; len > 0; len--) { 
     if (*ap2 != *bp2) return false; 
     ap2++; 
     bp2++; 
     } 
    } 
    } 
    return true; 
} 
+0

+1很好的例子。儘管如此,在x64系統上,您應該使用Int64。 – Josh 2010-01-31 21:46:34

+0

我假設可以使用相同的技術一次比較八個或十六個字節(long,decimal ..)? – Aistina 2010-01-31 21:47:40

+0

+1非常好,SequenceEqual給我一個50mb的陣列約1秒,而你的給出一個不錯的77ms :) – Diadistis 2010-01-31 21:50:07

0

函數[DllImport( 「MSVCRT.DLL」) 不安全的靜態外部INT memcmp(void *的B1,無效* B2 ,長計);

unsafe static int ByteArrayCompare1(byte[] b1, int b1Index, int b1Length, byte[] b2, int b2Index, int b2Length) 
    { 
     CompareCount++; 
     fixed (byte* p1 = b1) 
     fixed (byte* p2 = b2) 
     { 
      int cmp = memcmp(p1 + b1Index, p2 + b2Index, Math.Min(b1Length, b2Length)); 
      if (cmp == 0) 
      { 
       cmp = b1Length.CompareTo(b2Length); 
      } 

      return cmp; 
     } 
    } 
1

來自:http://www.pinvoke.net/default.aspx/msvcrt.memcmp:memcmp的 Belowmentioned簽名(由薩爾)是僅x64簽名。在x86機器上使用x64 only簽名會導致PInvoke堆棧不平衡。對於x86和x64平臺的兼容性確保您使用的簽名指定cdecl調用約定,並使用UIntPtr類型正確馬歇爾的size_t count參數:

[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] 
    static extern int memcmp(byte[] b1, byte[] b2, UIntPtr count); 

    static bool doImagesMatch(byte[] b1, byte[] b2) 
    {  
     return b1.Length == b2.Length && memcmp(b1, b2, new UIntPtr((uint)b1.Length)) == 0; 
    } 

我使用此代碼成功,但我沒有時間衡量表現(還)。我正在使用大約600字節的小數組。我必須使用與x86兼容的代碼,因爲我們的非營利組織中絕大多數計算機都是x86。

顯然你需要一個快速的算法將位圖轉換爲byte []。