6

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

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

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

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

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

[更新]

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

4

6 回答 6

16

电话在做什么_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
于 2009-03-09T19:57:26.870 回答
4

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

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

    }
}
于 2011-12-15T19:21:26.097 回答
3

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

使用BinaryReader

saveTime  = binReader.ReadInt32()

或者对于整数数组:

Dim count As Integer = binReader.Read(testArray, 0, 3)
于 2009-03-09T19:53:20.110 回答
0

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

首先,不要总是以 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
于 2009-03-09T20:12:33.993 回答
0

与比较算法不严格相关:

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

于 2009-03-09T20:38:46.007 回答
0

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

于 2014-09-06T15:44:36.610 回答