我怎么能快速做到这一点?
当然,我可以这样做:
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 .
请注意我的超快答案here .
28 回答
如果您不反对这样做,可以导入J#assembly "vjslib.dll"并使用其Arrays.equals(byte[], byte[]) method ...
如果有人嘲笑你,不要怪我...
编辑:为了它的 Value ,我使用Reflector来反汇编代码,这就是它的样子:
您可以使用Enumerable.SequenceEqual方法 .
如果由于某种原因无法使用.NET 3.5,那么您的方法就可以了 .
编译器\运行时环境将优化您的循环,因此您不必担心性能 .
P/Invoke权力激活!
在.NET 4中有一个新的内置解决方案 - IStructuralEquatable
用户gil提出了产生此解决方案的不安全代码:
这对于尽可能多的阵列进行基于64位的比较 . 这种情况取决于数组启动qword对齐的事实 . 如果没有qword对齐它就会起作用,只是没有像它那样快 .
它比简单的
for
循环执行大约七个定时器 . 使用J#库与原始for
循环等效地执行 . 使用.SequenceEqual运行大约慢七倍;我想是因为它使用的是IEnumerator.MoveNext . 我认为基于LINQ的解决方案至少要慢或者差 .如果你看一下.NET如何执行string.Equals,你会看到它使用一个名为EqualsHelper的私有方法,该方法具有"unsafe"指针实现 . .NET Reflector是你的朋友,看看内部是如何完成的 .
这可以用作字节数组比较的模板,我在博客文章Fast byte array comparison in C#中进行了实现 . 我还做了一些基本的基准测试,看看安全实施何时比不安全更快 .
也就是说,除非你真的需要杀手性能,否则我会选择简单的fr循环比较 .
.NET 3.5和更新版本有一个新的公共类型,
System.Data.Linq.Binary
封装byte[]
. 它实现IEquatable<Binary>
(实际上)比较两个字节数组 . 注意System.Data.Linq.Binary
也有来自byte[]
的隐式转换运算符 .MSDN文档:System.Data.Linq.Binary
反射器反编译的Equals方法:
有趣的是,如果两个二进制对象的哈希值相同,它们只会进行逐字节比较循环 . 然而,这是以在
Binary
对象的构造函数中计算哈希为代价的(通过使用for
循环遍历数组:-)) .上面的实现意味着在最坏的情况下你可能需要遍历数组三次:首先计算array1的散列,然后计算array2的散列,最后(因为这是最坏的情况,长度和散列相等)来比较array1中的字节,数组2中的字节数 .
总的来说,即使
System.Data.Linq.Binary
内置于BCL中,我也不认为这是比较两个字节数组的最快方法: - | .Span<T>
提供极具竞争力的替代方案,无需将混乱和/或不便携的绒毛扔进您自己的应用程序代码库:(实施的)内容可以找到here .
我已经revised @ EliArbel的要点是将此方法添加为
SpansEqual
,删除其他基准测试中大多数不那么有趣的执行者,使用不同的数组大小运行它,输出图形并标记SpansEqual
作为基线,以便它报告不同的方法与SpansEqual
比较 .以下数字来自结果,轻微编辑以删除“错误”列 .
我很惊讶地看到
SpansEqual
并没有出现在max-array-size方法的顶部,但差别非常小,以至于我无论如何都不重要 .我的系统信息:
I posted关于检查byte []是否充满零的类似问题 . (SIMD代码遭到殴打,所以我从这个答案中删除了它 . )这是我比较中最快的代码:
在两个256MB字节数组上测量:
我们再添加一个!
最近微软发布了一个特殊的NuGet包,System.Runtime.CompilerServices.Unsafe . 它's special because it'写在IL中,并提供C#中不能直接使用的低级功能 .
其中一个方法
Unsafe.As<T>(object)
允许将任何引用类型转换为另一个引用类型,跳过任何安全检查 . 这通常是一个坏主意,但如果两种类型具有相同的结构,它可以工作 . 所以我们可以使用它来将byte[]
转换为long[]
:请注意,
long1.Length
仍将返回存储在数组内存结构中的字段中的原始数组's length, since it' .这个方法并不像这里演示的其他方法那么快,但它比朴素方法快得多,不使用不安全的代码或P / Invoke或pinning,并且实现非常简单(IMO) . 以下是我机器的一些BenchmarkDotNet结果:
我也创建了一个gist with all the tests .
我会使用不安全的代码并运行比较Int32指针的
for
循环 .也许您还应该考虑将数组检查为非null .
我开发了一种方法,略微超过了
memcmp()
(基座的答案)并且非常轻微地打败了EqualBytesLongUnrolled()
(Arek Bulski的回答) . 基本上,它将循环展开4而不是8 .这比_679717快约25%,比快6%快
EqualBytesLongUnrolled()
在我的机器上 .似乎EqualBytesLongUnrolled是上面建议的最好的 .
跳过的方法(Enumerable.SequenceEqual,StructuralComparisons.StructuralEqualityComparer.Equals),不是慢病人 . 在265MB阵列上我测量了这个:
为了比较短字节数组,以下是一个有趣的黑客:
然后我可能会遇到问题中列出的解决方案 .
对此代码进行性能分析会很有趣 .
无法找到我完全满意的解决方案(合理的性能,但没有不安全的代码/ pinvoke)所以我想出了这个,没有什么真正的原创,但有效:
性能与此页面上的其他一些解决方案相比:
简单循环:19837年,1.00
不安全比较:1636蜱,12.12
EqualBytesLongUnrolled:637个刻度,31.09
P / Invoke memcmp:369个刻度,53.67
在linqpad中测试,1000000字节相同的数组(最坏情况),每次500次迭代 .
我想到了许多显卡内置的块传输加速方法 . 但是你必须按字节顺序复制所有数据,所以如果你不想在非托管和硬件相关的代码中实现逻辑的整个部分,这对你没有多大帮助......
与上述方法类似的另一种优化方法是从一开始就将尽可能多的数据存储在long []而不是byte []中,例如,如果您从二进制文件中顺序读取它,或者如果使用内存映射文件,请将数据读入long []或单个long值 . 然后,你的比较循环只需要为包含相同数据量的byte []所需的迭代次数的1/8 . 需要比较的时间和频率与您需要以逐字节方式访问数据的时间和频率有关,例如:在API调用中将它用作需要byte []的方法中的参数 . 最后,你只能知道你是否真的知道用例......
我使用附加的程序.net 4.7发布版本进行了一些测量,没有附加调试器 . 我认为人们一直在使用错误的度量标准,因为如果您关心速度,那么您需要多长时间才能确定两个字节数组是否相等 . 即以字节为单位的吞吐
正如你所看到的,没有比_679727更好的方法了,它的速度要快几个数量级 . 一个简单的
for
循环是第二个最佳选择 . 而且我仍然难以理解为什么微软不能简单地包含一个Buffer.Compare
方法 .[Program.cs中]:
对不起,如果您正在寻找一种托管方式,那么您已经正确地进行了操作,据我所知,BCL中没有内置方法来执行此操作 .
您应该添加一些初始空值检查,然后只需将其重用,就像它在BCL中的位置一样 .
这几乎肯定比这里给出的任何其他版本慢得多,但写起来很有趣 .
我这里没有看到很多linq解决方案 .
我不确定性能的影响,但是我通常坚持使用
linq
作为经验法则,然后在必要时进行优化 .请注意,这只适用于尺寸相同的阵列 . 扩展可能看起来像这样
我决定采用ArekBulski发布的EqualBytesLongUnrolled方法启发的解决方案,并进行额外的优化 . 在我的实例中,数组中的数组差异往往接近数组的尾部 . 在测试中,我发现当大型阵列就是这种情况时,能够以相反的顺序比较阵列元素,这使得该解决方案比基于memcmp的解决方案具有巨大的性能提升 . 这是解决方案:
使用
SequenceEquals
进行比较 .简短的回答是这样的:
通过这种方式,您可以使用优化的.NET字符串比较来进行字节数组比较,而无需编写不安全的代码 . 这是在background中完成的:
由于上面的许多奇特的解决方案都不适用于UWP,因为我喜欢Linq和功能方法,所以我向你提出了我的版本问题 . 为了在第一个差异发生时逃避比较,我选择.FirstOrDefault()
如果您正在寻找一个非常快速的字节数组相等比较器,我建议您看一下这个STSdb实验室文章:Byte array equality comparer.它具有一些最快的byte []数组相等比较实现,它们被呈现,性能测试和总结 .
您还可以专注于这些实现:
BigEndianByteArrayComparer - 快速byte []数组比较器从左到右(BigEndian)BigEndianByteArrayEqualityComparer - 快速字节[]相等比较器从左到右(BigEndian)LittleEndianByteArrayComparer - 快速byte []数组比较器从右到左(LittleEndian)LittleEndianByteArrayEqualityComparer - 快速字节[]从右到左的平等比较(LittleEndian)
有点蛮力,但它直接将字节数组转换为Base64字符串并比较两个字符串 . 如果您有大型数组可供比较,那就太方便了 . 或者,如果其中一个字节数组已经是Base64格式 .
我想最快的方法(对于大型数组具有最佳性能)是散列两个字节数组并比较散列 .
如果你有一个巨大的字节数组,你可以通过将它们转换为字符串来比较它们 .
你可以使用类似的东西
我使用过这个,我看到了巨大的性能影响 .