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

时间:2009-03-09 19:51:12

标签: vb.net arrays compare

我试图比较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

我需要能够尽可能快地比较可能数百兆字节甚至可能是1千兆字节或2千兆字节的文件。任何可以更快地完成此任务的建议或算法?

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

[更新]

我将数组转换为Byte()的局部变量,然后进行相同的比较,相同的代码并在一秒钟内运行(我必须对其进行基准测试并将其与其他人进行比较),但是如果你这样做的话使用局部变量并使用通用数组它变得非常慢。我不确定为什么,但它为我提出了更多关于阵列使用的问题。

6 个答案:

答案 0 :(得分:16)

_Bytes(I)来电是做什么的?它不是每次都加载文件,是吗?即使有缓冲,这也是坏消息!

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

我建议你将比较代码提取到一个带有两个字节数组的独立函数中。这样你知道你不会做任何奇怪的事情。在这种情况下,我还会使用简单的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

答案 1 :(得分:3)

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

使用BinaryReader

saveTime  = binReader.ReadInt32()

或者对于整数数组:

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

答案 2 :(得分:3)

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

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

    }
}

答案 3 :(得分:0)

我看到两件可能有用的事情:

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

 array2 = item.Bytes

每次需要一个字节时,这将节省从对象解除引用的开销。在Visual Basic中这可能很昂贵,特别是如果该属性上有Getter方法。

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

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

答案 4 :(得分:0)

与比较算法没有严格关系:

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

答案 5 :(得分:0)

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