比较两个字节数组的最快方法是啥?

Posted

技术标签:

【中文标题】比较两个字节数组的最快方法是啥?【英文标题】:What is the fastest way to compare two byte arrays?比较两个字节数组的最快方法是什么? 【发布时间】:2009-03-09 19:51:12 【问题描述】:

我正在尝试比较 VB.NET 中的两个长字节数组,但遇到了障碍。比较两个 50 兆字节的文件需要将近两分钟,所以我显然做错了什么。我在具有大量内存的 x64 机器上,所以那里没有问题。这是我目前正在使用并希望更改的代码。

_Bytesitem.Bytes 是要比较的两个不同数组,并且长度已经相同。

For Each B In item.Bytes
   If B <> _Bytes(I) Then
        Mismatch = True
        Exit For
   End If
   I += 1
Next

我需要能够尽可能快地比较可能有数百兆字节甚至可能是一两个千兆字节的文件。有什么建议或算法可以更快地做到这一点?

Item.bytes 是从数据库/文件系统中取出的对象,返回比较,因为它的字节长度与用户想要添加的项目相匹配。通过比较这两个数组,我可以确定用户是否向数据库添加了新内容,如果没有,我可以将它们映射到另一个文件,而不会浪费硬盘空间。

[更新]

我将数组转换为 Byte() 的局部变量,然后进行相同的比较,相同的代码,它运行了大约一秒钟(我必须对其进行基准测试并将其与其他变量进行比较),但如果你这样做带有局部变量的东西并使用通用数组会变得非常慢。我不知道为什么,但它对我提出了更多关于数组使用的问题。

【问题讨论】:

使用简单的方法比较两个 50MB 的数组只需要不到一秒钟的时间。你应该有另一个问题。 检查 ***.com/q/43289/276648 这与 C# 的问题相同。很多答案。我喜欢不安全的版本***.com/a/8808245/276648,因为它也可以在 Mono Linux 上运行。 【参考方案1】:

_Bytes(I) 呼叫在做什么?它不是每次都加载文件,是吗?即使有缓冲,那也是个坏消息!

有很多方法可以微优化这方面,一次查看多头,可能使用不安全的代码等 - 但我只专注于让合理 性能第一。显然发生了一些非常奇怪的事情。

我建议您将比较代码提取到一个单独的函数中,该函数采用两个字节数组。这样你就知道你不会做任何奇怪的事情。在这种情况下,我还会使用简单的For 循环而不是For Each - 它会更简单。哦,先检查长度是否正确:)

编辑:这是我要使用的代码(未经测试,但足够简单)。它暂时在 C# 中 - 我将在几秒钟内转换它:

public static bool Equals(byte[] first, byte[] second)

    if (first == second)
    
        return true;
    
    if (first == null || second == null)
    
        return false;
    
    if (first.Length != second.Length)
    
        return false;
    
    for (int i=0; i < first.Length; i++)
    
        if (first[i] != second[i])                
        
            return false;
        
    
    return true;

编辑:这是 VB:

Public Shared Function ArraysEqual(ByVal first As Byte(), _
                                   ByVal second As Byte()) As Boolean
    If (first Is second) Then
        Return True
    End If

    If (first Is Nothing OrElse second Is Nothing) Then
        Return False
    End If
    If  (first.Length <> second.Length) Then
         Return False
    End If

    For i as Integer = 0 To first.Length - 1
        If (first(i) <> second(i)) Then
            Return False
        End If
    Next i
    Return True
End Function

【讨论】:

_Bytes(I) 是一个已经在内存中的字节数组。 i 只是查找字节的索引 了不起的 Jon,我很高兴 Stack Celeb 能在这方面提供帮助! 试试我刚刚提供的代码 - 但我真的很惊讶它花了两分钟。上面的代码在我的笔记本电脑上大约需要 140 毫秒(经过优化构建,不可在调试器下运行)。 嗨,乔恩,您在 VB 代码中的第一个条件 Not 太多了。也不需要括号,但它们不会造成任何伤害(Is == object.ReferenceEquals == 大致为 == 用于在未定义 operator == 时进行引用)。【参考方案2】:

比较大小相等的两个字节数组的最快方法是使用互操作。在控制台应用程序上运行以下代码:

using System;
using System.Runtime.InteropServices;
using System.Security;

namespace CompareByteArray

    class Program
    
        static void Main(string[] args)
        
            const int SIZE = 100000;
            const int TEST_COUNT = 100;

            byte[] arrayA = new byte[SIZE];
            byte[] arrayB = new byte[SIZE];

            for (int i = 0; i < SIZE; i++)
            
                arrayA[i] = 0x22;
                arrayB[i] = 0x22;
            

            
                DateTime before = DateTime.Now;
                for (int i = 0; i < TEST_COUNT; i++)
                
                    int result = MemCmp_Safe(arrayA, arrayB, (UIntPtr)SIZE);

                    if (result != 0) throw new Exception();
                
                DateTime after = DateTime.Now;

                Console.WriteLine("MemCmp_Safe: 0", after - before);
            

            
                DateTime before = DateTime.Now;
                for (int i = 0; i < TEST_COUNT; i++)
                
                    int result = MemCmp_Unsafe(arrayA, arrayB, (UIntPtr)SIZE);

                    if (result != 0) throw new Exception();
                
                DateTime after = DateTime.Now;

                Console.WriteLine("MemCmp_Unsafe: 0", after - before);
            


            
                DateTime before = DateTime.Now;
                for (int i = 0; i < TEST_COUNT; i++)
                
                    int result = MemCmp_Pure(arrayA, arrayB, SIZE);

                    if (result != 0) throw new Exception();
                
                DateTime after = DateTime.Now;

                Console.WriteLine("MemCmp_Pure: 0", after - before);
            
            return;
        

        [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint="memcmp", ExactSpelling=true)]
        [SuppressUnmanagedCodeSecurity]
        static extern int memcmp_1(byte[] b1, byte[] b2, UIntPtr count);

        [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "memcmp", ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        static extern unsafe int memcmp_2(byte* b1, byte* b2, UIntPtr count);

        public static int MemCmp_Safe(byte[] a, byte[] b, UIntPtr count)
        
            return memcmp_1(a, b, count);
        

        public unsafe static int MemCmp_Unsafe(byte[] a, byte[] b, UIntPtr count)
        
            fixed(byte* p_a = a)
            
                fixed (byte* p_b = b)
                
                    return memcmp_2(p_a, p_b, count);
                
            
        

        public static int MemCmp_Pure(byte[] a, byte[] b, int count)
        
            int result = 0;
            for (int i = 0; i < count && result == 0; i += 1)
            
                result = a[0] - b[0];
            

            return result;
        

    

【讨论】:

你的测试中哪一个最快?时间安排? MemCmp_Safe: 00:00:00.0060003 MemCmp_Unsafe: 00:00:00.0020002 MemCmp_Pure: 00:00:00.0270015【参考方案3】:

如果您不需要知道字节,请使用 64 位整数,一次可以得到 8 个字节。实际上,一旦将其隔离为一组 8 个字节,您就可以找出错误的字节。

使用BinaryReader:

saveTime  = binReader.ReadInt32()

或者对于整数数组:

Dim count As Integer = binReader.Read(testArray, 0, 3)

【讨论】:

你能进一步解释一下吗? 使用 int 数组或字节数组。 既然这些是文件中的字节数组,我该如何将它们变成你所说的这种被阻止的格式? 要么将文件作为二进制文件一次读取一个 64 位 int,要么在读取 8 个字节后,使用位移位和按位或将它们放入 64 位 int。 @Middletone:检查链接,并使用 BinaryReader。【参考方案4】:

更好的方法...如果您只是想看看两者是否不同,那么不必遍历整个字节数组并将每个字节数组的哈希生成为字符串并比较字符串,从而节省一些时间。 MD5 应该可以正常工作并且非常高效。

【讨论】:

这是非常可笑的事情。任何加密函数都应该扫描每个数组并计算两者的哈希值......所以它的成本远远超过简单地执行每个字节的比较。【参考方案5】:

我看到两件事可能会有所帮助:

首先,不要总是以 item.Bytes 的形式访问第二个数组,而是使用一个局部变量直接指向该数组。也就是说,在开始循环之前,请执行以下操作:

 array2 = item.Bytes

这将节省每次您需要一个字节时从对象取消引用的开销。这在 Visual Basic 中可能会很昂贵,尤其是在该属性上有 Getter 方法的情况下。

另外,使用“确定循环”而不是“for each”。您已经知道数组的长度,因此只需使用该值对循环进行编码。这将避免将数组视为集合的开销。循环看起来像这样:

For i = 1 to max Step 1
   If (array1(i) <> array2(i)) 
       Exit For
   EndIf 
Next

【讨论】:

【参考方案6】:

与比较算法不严格相关:

您确定您的瓶颈与可用内存和加载字节数组所用的时间无关吗?加载两个 2 GB 字节数组只是为了比较它们可能会使大多数机器瘫痪。如果程序设计允许,请尝试使用流来读取更小的块。

【讨论】:

以上是关于比较两个字节数组的最快方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在opencv中使用imshow显示字节数组的最快方法是啥?

从 C++ 中的字节数组中提取非零索引的最快方法是啥

将现有数组归零的最快方法是啥?

查看固定长度数组之间有多少字节相等的最快方法

检查字节数组是不是全为零的最快方法

C语言中计算数组长度的方法是啥