2010-01-31 25 views
6

Ben eşitlik için iki byte [] karşılaştırmak için performans verimli yollarını arıyorum. Boyutlar 1 MB'nin üzerindedir, bu nedenle her dizi elemanının yükü en aza indirilmelidir.C# bayt

Her iki dizi için de SequenceEqual veya hand-coded for-loop over every item hızlarını avoiding the repetitive bound checks ile bitirmeyi hedefliyorum. Array.Copy hızlı memcpy yol açabileceğini Aynı şekilde, bir memcmp ne götürecek?

+0

birkaç karşı iki blok sadece, ya bir blok karşılaştırmak gerekir mi? Belki de bunu yaptığınız senaryo hakkında daha fazla şey anlatırsanız, daha iyi çözümler de bulunabilir mi? Örneğin, bir çok bloğu diğer birçok bloğa karşı karşılaştırmanız gerekirse, basit bir karma, en azından minimum çalışma ile çok fazla garantili farklılıklar sağlar ve sonra potansiyel olarak yanlış pozitiflere odaklanabilirsiniz. –

cevap

12

: Aslında neredeyse iki kat daha hızlı iki 32 ve 64 bit sistemler gibi görünüyor. Bu kod, benim daracık dizüstü ~ 51 milisaniye sürer de 64 bit makinelerde çalışır:

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 sürümünde muhtemelen dikkate alınan bellek hizalama gibi başka şeyler vardır. Tekerleği güvensiz kodda yeniden icat etmenin yolu yok.Tabii ki, sadece profil yaptıktan ve buna değdiğini kanıtladıktan sonra - standart sorumluluk reddi beyanı. –

+0

+1. İyi bir şekilde test edilmiş optimize edilmiş bir rutini, kendi yuvarlanmanızdan daha iyi kullanmak için çok daha iyi ve üzerinde çalıştığınız herhangi bir platformda bir şekilde hızlı olacağını umuyoruz. –

+0

Dizileri yerinde sabitlemeyi unutmayın! –

16

Sen işaretçi işlemlerini yapmak güvensiz kodu kullanabilirsiniz.

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

bir basit döngü karşı bu test, ve yaklaşık altı kat daha hızlı: Sen tamsayı olarak bir defada bayt dört karşılaştırabilirsiniz. Josh Einstein tarafından önerildiği gibi

, uzun bir 64 bit sistemde kullanılabilir. performans gerçekten o zaman CRT kütüphane Windows her yeni versiyonu ile birlikte kullanmaktır yapmak için en hızlı yol konularda ise

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 Harika bir örnek. Yine de, x64 sistemlerinde Int64'ü kullanmalısınız. – Josh

+0

Ve aynı teknik, bir seferde sekiz veya on altı bayt karşılaştırmak için kullanılabilir (uzun, ondalık ..)? – Aistina

+0

+1 Gerçekten çok iyi, SequenceEqual sizin için güzel bir 77ms verirken bir 50mb dizisi için ~ 1sec verir :) – Diadistis

0

[DllImport ("msvcrt.dll")] güvensiz statik extern int memcmp (void * b1, b2 * boşluk uzun sayım); http://www.pinvoke.net/default.aspx/msvcrt.memcmp: Nereden

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

memcmp ait (Saar tarafından) Belowmentioned imzası x64 yalnızca imzadır. Bir x86 makinesinde sadece x64 imzalarının kullanılması bir PInvoke yığın dengesizliği ile sonuçlanacaktır. x86 ve x64 platformu uyumluluğu sağlamak için, CDECL çağırma kuralını belirtir ve UIntPtr türünü kullanan bir imza kullanmak doğru Marshall size_t sayım argüman:

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

Ben başarı ile bu kodu kullanmak ama yoktu Performansı ölçmek için gereken süre (henüz). Küçük dizilerden yaklaşık 600 bayt kullanıyorum. Kâr amacı gütmeyen kuruluşumuzdaki bilgisayarların büyük çoğunluğu x86 olduğu için x86 uyumlu kodu kullanmalıyım.

Açıkçası [] byte bitmap dönüştürmek için hızlı algorhythm gerekir.