我怎样才能做得快呢?

当然我可以这样做:

static bool ByteArrayCompare(byte[] a1, byte[] a2)
{
    if (a1.Length != a2.Length)
        return false;

    for (int i=0; i<a1.Length; i++)
        if (a1[i]!=a2[i])
            return false;

    return true;
}

但我正在寻找一个BCL函数或一些高度优化的已证明的方法来做到这一点。

java.util.Arrays.equals((sbyte[])(Array)a1, (sbyte[])(Array)a2);

工作得很好,但这似乎不适用于x64。

注意我的快速回答。


当前回答

编辑:现代的快速方法是使用a1.SequenceEquals(a2)

用户gil提出了不安全的代码,产生了这个解决方案:

// Copyright (c) 2008-2013 Hafthor Stefansson
// Distributed under the MIT/X11 software license
// Ref: http://www.opensource.org/licenses/mit-license.php.
static unsafe bool UnsafeCompare(byte[] a1, byte[] a2) {
  unchecked {
    if(a1==a2) return true;
    if(a1==null || a2==null || a1.Length!=a2.Length)
      return false;
    fixed (byte* p1=a1, p2=a2) {
      byte* x1=p1, x2=p2;
      int l = a1.Length;
      for (int i=0; i < l/8; i++, x1+=8, x2+=8)
        if (*((long*)x1) != *((long*)x2)) return false;
      if ((l & 4)!=0) { if (*((int*)x1)!=*((int*)x2)) return false; x1+=4; x2+=4; }
      if ((l & 2)!=0) { if (*((short*)x1)!=*((short*)x2)) return false; x1+=2; x2+=2; }
      if ((l & 1)!=0) if (*((byte*)x1) != *((byte*)x2)) return false;
      return true;
    }
  }
}

它对尽可能多的数组进行基于64位的比较。这依赖于数组以qword对齐开始的事实。它会工作,如果不是qword对齐,只是没有那么快,如果它是。

它比简单的“for”循环快了大约7个计时器。使用j#库执行相当于原来的' for '循环。使用.SequenceEqual会慢7倍左右;我想只是因为它使用了ienumerator。movenext。我认为基于linq的解决方案至少会这么慢,甚至更糟。

其他回答

我会使用不安全的代码并运行for循环比较Int32指针。

也许您还应该考虑检查数组是否为非空。

如果你不反对这样做,你可以导入j#程序集“vjslib.dll”并使用它的数组。= (byte[], byte[])方法…

如果有人嘲笑你,不要怪我……


编辑:为了它的价值,我使用Reflector来反汇编代码,下面是它的样子:

public static bool equals(sbyte[] a1, sbyte[] a2)
{
  if (a1 == a2)
  {
    return true;
  }
  if ((a1 != null) && (a2 != null))
  {
    if (a1.Length != a2.Length)
    {
      return false;
    }
    for (int i = 0; i < a1.Length; i++)
    {
      if (a1[i] != a2[i])
      {
        return false;
      }
    }
    return true;
  }
  return false;
}

编辑:现代的快速方法是使用a1.SequenceEquals(a2)

用户gil提出了不安全的代码,产生了这个解决方案:

// Copyright (c) 2008-2013 Hafthor Stefansson
// Distributed under the MIT/X11 software license
// Ref: http://www.opensource.org/licenses/mit-license.php.
static unsafe bool UnsafeCompare(byte[] a1, byte[] a2) {
  unchecked {
    if(a1==a2) return true;
    if(a1==null || a2==null || a1.Length!=a2.Length)
      return false;
    fixed (byte* p1=a1, p2=a2) {
      byte* x1=p1, x2=p2;
      int l = a1.Length;
      for (int i=0; i < l/8; i++, x1+=8, x2+=8)
        if (*((long*)x1) != *((long*)x2)) return false;
      if ((l & 4)!=0) { if (*((int*)x1)!=*((int*)x2)) return false; x1+=4; x2+=4; }
      if ((l & 2)!=0) { if (*((short*)x1)!=*((short*)x2)) return false; x1+=2; x2+=2; }
      if ((l & 1)!=0) if (*((byte*)x1) != *((byte*)x2)) return false;
      return true;
    }
  }
}

它对尽可能多的数组进行基于64位的比较。这依赖于数组以qword对齐开始的事实。它会工作,如果不是qword对齐,只是没有那么快,如果它是。

它比简单的“for”循环快了大约7个计时器。使用j#库执行相当于原来的' for '循环。使用.SequenceEqual会慢7倍左右;我想只是因为它使用了ienumerator。movenext。我认为基于linq的解决方案至少会这么慢,甚至更糟。

抱歉,如果你正在寻找一种管理的方式,你已经正确地做了,据我所知,在BCL中没有内置的方法来做这个。

你应该添加一些初始的空检查,然后重用它,就好像它在BCL。

找不到一个我完全满意的解决方案(合理的性能,但没有不安全的代码/pinvoke),所以我想出了这个,没有真正的原创,但工作:

    /// <summary>
    /// 
    /// </summary>
    /// <param name="array1"></param>
    /// <param name="array2"></param>
    /// <param name="bytesToCompare"> 0 means compare entire arrays</param>
    /// <returns></returns>
    public static bool ArraysEqual(byte[] array1, byte[] array2, int bytesToCompare = 0)
    {
        if (array1.Length != array2.Length) return false;

        var length = (bytesToCompare == 0) ? array1.Length : bytesToCompare;
        var tailIdx = length - length % sizeof(Int64);

        //check in 8 byte chunks
        for (var i = 0; i < tailIdx; i += sizeof(Int64))
        {
            if (BitConverter.ToInt64(array1, i) != BitConverter.ToInt64(array2, i)) return false;
        }

        //check the remainder of the array, always shorter than 8 bytes
        for (var i = tailIdx; i < length; i++)
        {
            if (array1[i] != array2[i]) return false;
        }

        return true;
    }

与本页上的其他解决方案相比,性能:

简单循环:19837滴答,1.00

*位收敛器:4886 ticks, 4.06

unsafcompare: 1636 ticks, 12.12

EqualBytesLongUnrolled: 637 tick, 31.09

P/Invoke memcmp: 369 ticks, 53.67

在linqpad上测试,1000000字节的相同数组(最坏的情况),每个数组500次迭代。