2010-01-31 3 views
6

Ich bin auf der Suche nach leistungsfähigen Möglichkeiten, zwei Byte [] für die Gleichheit zu vergleichen. Größen liegen über 1 MB, daher sollte der Overhead für jedes Array-Element minimiert werden.C# byte [] Vergleich ohne gebundene Überprüfungen

Ich ziele darauf ab, die Geschwindigkeiten von SequenceEqual oder hand-coded for-loop over every item, von avoiding the repetitive bound checks für beide Arrays zu schlagen. In der gleichen Weise, wie Array.Copy zu schnell memcpy führen könnte, was führt zu einem memcmp?

+0

Müssen Sie nur zwei Blöcke oder einen Block mit mehreren vergleichen? Vielleicht, wenn Sie uns mehr über das Szenario erzählen, in dem Sie dies tun, könnten noch bessere Lösungen gefunden werden? Zum Beispiel, wenn Sie eine Sequenz von Blöcken mit vielen anderen Blöcken vergleichen müssen, würde ein einfacher Hash Ihnen zumindest viele garantierte Unterschiede mit minimaler Arbeit geben, und dann könnten Sie sich auf die potentiell falschen Positive konzentrieren. –

Antwort

12

Wenn Leistung wirklich zählt dann dem schnellsten Weg, es zu tun ist durch die CRT-Bibliothek mit jeder Version von Windows enthalten. Dieser Code nimmt ~ 51 msec auf meinem poky Laptop arbeitet auf 64-Bit-Maschinen zu:

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. Es gibt andere Dinge wie Speicherausrichtung, die wahrscheinlich in der CRT-Version berücksichtigt werden. Das Rad im unsicheren Code nicht neu zu erfinden ist der richtige Weg.Natürlich erst nach dem Profiling und dem Beweis, dass es sich lohnt - der Standard Disclaimer. –

+0

+1. Viel besser ist es, eine gut getestete optimierte Routine zu verwenden, als selbst zu rollen und zu hoffen, dass es auf jeder Plattform, auf der Sie gerade laufen, genauso schnell sein wird. –

+0

Vergessen Sie nicht, die Arrays an Ort und Stelle zu setzen! –

16

Sie können unsicheren Code verwenden, um Zeigeroperationen auszuführen. Sie können die Bytes vier zu einem Zeitpunkt, als ganze Zahlen vergleichen:

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

A diese gegen eine einfache Schleife getestet, und es ist etwa sechsmal schneller.

Wie von Josh Einstein vorgeschlagen, könnte lange auf einem 64-Bit-System verwendet werden. Eigentlich scheint es fast doppelt so schnell sowohl auf 32- und 64-Bit-Systeme zu sein:

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 Großes Beispiel. Auf x64-Systemen sollten Sie Int64 verwenden. – Josh

+0

Und ich nehme an, die gleiche Technik kann verwendet werden, um acht oder sechzehn Bytes gleichzeitig zu vergleichen (lang, dezimal ..)? – Aistina

+0

+1 Sehr gut in der Tat, SequenceEqual gibt mir ~ 1sec für eine 50mb-Array, während deine gibt eine schöne 77ms :) – Diadistis

0

[DllImport ("msvcrt.dll")] unsicheres statischen extern int memcmp (void * b1, b2 void * , lange Zählung);

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

Von: http://www.pinvoke.net/default.aspx/msvcrt.memcmp: Belowmentioned Signatur (von Saar) von memcmp ist eine x64 nur Signatur. Die Verwendung der x64-Einzelsignaturen auf einer x86-Maschine führt zu einem PInvoke-Stack-Ungleichgewicht. Für x86 und x64-Plattform-Kompatibilität sicherstellen, verwenden Sie eine Signatur, die die Cdecl Aufrufkonvention und verwenden den UIntPtr Typen gibt richtig marshall das size_t Argument 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; 
    } 

ich diesen Code mit Erfolg verwenden, aber ich habe nicht haben Zeit, Leistung zu messen (noch). Ich benutze kleine Arrays von ungefähr 600 Bytes. Ich muss x86-kompatiblen Code verwenden, da die große Mehrheit der Computer in unserer Non-Profit-Organisation x86 ist.

Offensichtlich benötigen Sie einen schnellen Algorithmus, um Bitmap in Byte [] zu konvertieren.