查找字节数组数组是否包含另一个字节数组的最快方法是什么?

时间:2009-06-09 21:44:21

标签: c# comparison reference-type bytearray

我有一些非常慢的代码。我知道它会是,现在是。基本上,我正在从一堆目录中读取文件。文件名会更改,但数据不会更改。为了确定我是否已经读取了该文件,我正在对其字节进行哈希并将其与已处理文件的哈希列表进行比较。每个目录中大约有1000个文件,并且确定每个目录中的新内容需要大约一分钟左右(然后处理开始)。这是基本代码:

public static class ProgramExtensions
{
    public static byte[] ToSHA256Hash(this FileInfo file)
    {
        using (FileStream fs = new FileStream(file.FullName, FileMode.Open))
        {
            using (SHA256 hasher = new SHA256Managed())
            {
                return hasher.ComputeHash(fs);
            }
        }
    }
    public static string ToHexString(this byte[] p)
    {

        char[] c = new char[p.Length * 2 + 2];

        byte b;

        c[0] = '0'; c[1] = 'x';

        for (int y = 0, x = 2; y < p.Length; ++y, ++x)
        {
            b = ((byte)(p[y] >> 4));

            c[x] = (char)(b > 9 ? b + 0x37 : b + 0x30);

            b = ((byte)(p[y] & 0xF));

            c[++x] = (char)(b > 9 ? b + 0x37 : b + 0x30);
        }

        return new string(c);

    }
}

class Program
{
    static void Main(string[] args)
    {
        var allFiles = new DirectoryInfo("c:\\temp").GetFiles("*.*");

        List<string> readFileHashes = GetReadFileHashes();

        List<FileInfo> filesToRead = new List<FileInfo>();

        foreach (var file in allFiles)
        {
            if (readFileHashes.Contains(file.ToSHA256Hash().ToHexString()))
                filesToRead.Add(file);
        }

        //read new files
    }
}

无论如何我可以加快速度吗?

7 个答案:

答案 0 :(得分:8)

我相信你可以通过简单地首先检查文件大小来存档最显着的性能改进,如果文件大小不匹配,你可以跳过整个文件,甚至不打开它。

除了保存已知哈希列表之外,您还可以保留已知文件大小的列表,并且只在文件大小匹配时进行内容比较。当filesize不匹配时,您甚至可以避免查看文件内容。

根据文件的一般大小,进一步改进是值得的:

  • 当第一个字节不同时,要么与早期中止进行二进制比较(保存读取整个文件,如果文件通常很大,这可能是一个非常显着的改进,任何哈希算法都会读取整个文件。第一个字节不同,使您无法读取文件的其余部分)。如果您的查找文件列表可能包含许多相同大小的文件,那么您可能不得不对几个文件进行二进制比较,而是考虑:

  • 以每个1MB的块进行散列。首先仅针对查找中预先计算的第一个块哈希检查第一个块。如果第一个块相同,则仅比较第二个块,在大多数情况下,对于不同的文件,将读数保存在第一个块之外当文件很大时,这两个选项都非常值得。

我怀疑更改散列算法本身(例如,首先检查按建议执行CRC)会产生任何显着差异。您的瓶颈可能是磁盘IO,而不是CPU,因此避免磁盘IO会给您带来最大的改进。但是,与性能一样,衡量。

然后,如果这仍然不够(并且只有那时),请尝试使用异步IO(请记住,顺序读取通常比随机访问更快,因此过多的随机异步读取会损害您的性能)

答案 1 :(得分:1)

  • 创建文件列表
  • 按文件大小排序列表
  • 从列表中删除具有唯一大小的文件
  • 现在进行散列(快速散列也可以提高性能)

答案 2 :(得分:1)

  • 为readFileHashes商店使用具有高效搜索功能(散列或二进制搜索)的数据结构。我认为HashSet或TreeSet会在这里为您提供更好的服务。

  • 使用适当的校验和(哈希和)函数。 SHA256是一个加密哈希,可能有点过分。 CRC的计算成本较低,最初用于捕获无意/随机的变化(传输错误),但是对于被设计/意图隐藏的变化是可接受的。什么适合您正在扫描的文件之间的差异?

    请参阅http://en.wikipedia.org/wiki/List_of_checksum_algorithms#Computational_costs_of_CRCs_vs_Hashes

    通过采样(例如校验和=(前10个字节和后10个字节))真正简单的校验和是否有效?

答案 3 :(得分:0)

我先做一个快速的CRC哈希检查,因为它比较便宜。 如果CRC不匹配,继续进行更“可靠”的哈希测试,例如SHA

答案 4 :(得分:0)

您对问题的描述仍然不够明确。

最大的问题是你正在做一堆哈希。这保证很慢。

您可能想尝试搜索修改时间,如果文件名已更改,则修改时间不会改变:

http://msdn.microsoft.com/en-us/library/ms724320(VS.85,loband).aspx

或者您可能希望监视文件夹以查找任何新文件:

http://www.codeguru.com/forum/showthread.php?t=436716

答案 5 :(得分:0)

首先按文件大小对文件进行分组 - 这将为您留下较小的文件组。现在它取决于组大小和文件大小。您可以开始并行读取所有文件,直到找到差异为止。如果存在差异,请将组拆分为在当前位置具有相同值的较小组。如果您有关于文件如何不同的信息,您可以使用此信息 - 最后开始阅读,如果更大的群集更改,或者您对文件的了解,请不要逐字节读取和比较。如果您必须并行读取许多文件导致随机光盘访问,此解决方案可能会引入I / O性能问题。

您还可以计算每个组中所有文件的哈希值并进行比较。您不能一次处理整个文件 - 只需计算一些(可能是4kiB集群或任何适合您的文件大小)字节的散列,并检查是否存在所有已有的差异。如果不是,则计算接下来几个字节的哈希值。这将使您可以处理每个文件的较大块,而无需为内存中的组中的每个文件保留一个这样的大块。

所以关于时间内存(光盘I / O内存)的权衡。你必须在将一组中的所有文件读入内存并逐字节地比较它们之间找到方法(高内存要求,快速顺序访问,但可以读取大量数据)并逐字节读取文件并仅比较最后一个字节读取(低内存要求,慢速随机访问,只读取所需数据)。此外,如果组非常大,逐字节比较文件将变得更慢 - 比较n个文件中的一个字节是O(n)操作 - 并且首先计算哈希值然后仅比较哈希值可能更有效值。

答案 6 :(得分:0)

更新:绝对不要只检查文件大小。如果您的操作系统版本允许使用FileInfo.LastWriteTime

我为内部项目编译器/打包器实现了类似的功能。我们有超过8k的文件,因此我们将最后修改的日期和哈希数据存储到sql数据库中。然后在后续运行中,我们首先查询任何特定文件上的修改日期,然后才查询哈希数据......这样我们只计算那些看似被修改的文件的新哈希数据......

.net有一种方法可以检查FileInfo类中的最后修改日期..我建议你检查一下。 编辑:这里是链接LastWriteTime

我们的打包器大约需要20秒才能找出修改过的文件。