没有绑定检查的 C# byte[] 比较

Posted

技术标签:

【中文标题】没有绑定检查的 C# byte[] 比较【英文标题】:C# byte[] comparison without bound checks 【发布时间】:2011-01-11 12:26:59 【问题描述】:

我正在寻找性能有效的方法来比较两个 byte[] 是否相等。大小超过 1 MB,因此每个数组元素的开销应该最小化。

我的目标是通过avoiding the repetitive bound checks 这两个阵列的速度超过SequenceEqual 或hand-coded for-loop over every item。就像Array.Copy 可以导致快速memcpy 一样,什么会导致memcmp

【问题讨论】:

您需要只比较两个块,还是一个块与多个块进行比较?也许如果你告诉我们更多关于你正在这样做的场景,甚至可以找到更好的解决方案?例如,如果您需要将一系列块与许多其他块进行比较,一个简单的哈希至少可以用最少的工作为您提供很多有保证的差异,然后您可以专注于潜在的误报。 【参考方案1】:

您可以使用不安全的代码来执行指针操作。您可以一次将四个字节作为整数进行比较:

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 用一个简单的循环对此进行了测试,速度大约快了六倍。

正如 Josh Einstein 所建议的,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;

【讨论】:

+1 很好的例子。不过,在 x64 系统上,您应该使用 Int64。 我假设可以使用相同的技术来一次比较 8 个或 16 个字节(长、十进制......)? +1 确实非常好,SequenceEqual 给我大约 1 秒的 50mb 数组,而你的给了一个不错的 77ms :) @Josh:是的,实际上它在 32 位系统上似乎也更快。 @Aistina:很长,是的,但不是小数。十进制中有不同的位组合给出相同的值,因此您可能会得到误报。此外,比较小数不是简单的按位比较,因此您不会获得任何好的性能。【参考方案2】:

如果性能真的很重要,那么最快的方法是使用每个版本的 Windows 中包含的 CRT 库。这段代码在我的 poky 笔记本电脑上需要大约 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。在 CRT 版本中可能会考虑其他一些事情,例如内存对齐。不要在不安全的代码中重新发明***是要走的路。当然,只有在分析并证明它值得之后——标准的免责声明。 +1。使用经过良好测试的优化例程比使用自己的例程并希望它在您碰巧运行的任何平台上都能以某种方式运行要好得多。 别忘了将阵列固定到位! 这值得为短数组做吗?阵列需要多长时间才能使其值得使用?使用 C++/CLI 调用它怎么样? 发现后告诉我们。【参考方案3】:

发件人:http://www.pinvoke.net/default.aspx/msvcrt.memcmp: 下面提到的 memcmp 签名(由 Saar 提供)是仅 x64 签名。在 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。

显然你需要一个快速的算法来将位图转换为字节[]。

【讨论】:

【参考方案4】:

[DllImport("msvcrt.dll")] unsafe static extern int memcmp(void* b1, void* b2, long count);

    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;
        
    

【讨论】:

以上是关于没有绑定检查的 C# byte[] 比较的主要内容,如果未能解决你的问题,请参考以下文章

C# - 如何检查用户的输入是不是存在于组合框中(使用的数据绑定项)

C# FreeType 的使用

C# 相同的数据源 + 多个 DataGridViews = 数据绑定问题?

C# ComboBox枚举量绑定的 两种方法

C#如何在没有 XAML 的情况下创建到父元素的(两种方式)数据绑定

C#:WPF MVVM 中的按钮绑定