2010-01-31 3 views
6

2 바이트 []를 비교하는 성능 효율적인 방법을 찾고 있습니다. 크기가 1MB를 초과하므로 각 배열 요소의 오버 헤드를 최소화해야합니다.바운드 검사를 사용하지 않고 C# byte [] 비교

나는 두 배열에 대한 avoiding the repetitive bound checks에 의해, SequenceEqual 또는 hand-coded for-loop over every item의 속도를 이길 것을 목표로하고 있습니다. Array.Copy과 같은 방법으로 memcpy이 빨리 이어질 수 있으며, 무엇이 memcmp이 될까요?

+0

두 블록 만 비교하거나 한 블록을 여러 블록 비교해야합니까? 아마 당신이 시나리오에서 더 많은 것을 이야기한다면, 더 나은 해결책이 발견 될 것입니다. 예를 들어 블록 시퀀스를 다른 많은 블록과 비교해야 할 경우 간단한 해시는 최소한의 작업으로 많은 보장 된 차이점을 제공하고 잠재적 인 오탐에 집중할 수 있습니다. –

답변

12

: 사실 거의 두 배 빠른 속도로 모두 32 비트 및 64 비트 시스템을 것 같다. 이 코드는, 내 비좁은 노트북 ~ 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 버전에서 아마도 고려되는 메모리 정렬과 같은 다른 것들이 있습니다. 안전하지 않은 코드에서 바퀴를 다시 만들지 않는 것이 길입니다.물론, 프로파일 링을 한 후에 가치가 있다는 것을 증명해야만합니다. 표준 면책 조항입니다. –

+0

+1. 자신의 롤링보다 잘 테스트 된 최적화 된 루틴을 사용하는 것이 훨씬 더 좋으며 어떤 플랫폼에서 실행 되더라도 빠른 속도로 진행되기를 기대합니다. –

+0

배열을 고정하는 것을 잊지 마세요! –

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

A는 간단한 루프에 대해이 테스트를하고, 약 6 배 빠른 속도입니다 : 당신은 정수로 한 번에 바이트 사를 비교할 수 있습니다.

조쉬 아인슈타인 (Josh Einstein)이 제안한대로 64 비트 시스템에서 long을 사용할 수 있습니다. 성능이 정말로 다음은 CRT 라이브러리는 윈도우의 모든 버전에 포함 된 사용하는 것입니다 할 수있는 가장 빠른 방법을 중요한 경우

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

+0

그리고 같은 기술을 사용하여 한 번에 8 또는 16 바이트를 비교할 수 있다고 가정합니다 (길고 십진 ..)? – Aistina

+0

+1 SequenceEqual은 50mb 배열에 대해 ~ 1 초를 제공하지만 좋은 77ms를 제공합니다. – Diadistis

0

[같이 DllImport ("MSVCRT.DLL")] 안전하지 않은 정적 통근자의 INT의 memcmp는 (무효 *의 B1, B2 * 무효 , 긴 카운트); http://www.pinvoke.net/default.aspx/msvcrt.memcmp :에서

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는의 (사르에 의해) Belowmentioned의 서명은 64 만 서명입니다. x86 시스템에서 x64 전용 서명을 사용하면 PInvoke 스택 불균형이 발생합니다. x86 및 x64 플랫폼 호환성 확인을 위해 당신은 CDECL 호출 규칙을 지정하고 UIntPtr 형식을 사용하는 서명을 사용 올바르게 마샬를 size_t 카운트 인수 :

[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 []로 변환하려면 빠른 알고리즘이 필요합니다.

관련 문제