C# 如果一个字节数组数组包含另一个字节数组,最快的方法是什么?

C# 如果一个字节数组数组包含另一个字节数组,最快的方法是什么?,c#,comparison,reference-type,bytearray,C#,Comparison,Reference Type,Bytearray,我有一些非常慢的代码。我知道会的,现在是了。基本上,我从一堆目录中读取文件。文件名会更改,但数据不会更改。为了确定是否已读取该文件,我对其字节进行散列,并将其与已处理文件的散列列表进行比较。每个目录中大约有1000个文件,弄清楚每个目录中有什么新内容需要一分钟左右的时间(然后处理开始)。以下是基本代码: public static class ProgramExtensions { public static byte[] ToSHA256Hash(this FileInfo file)

我有一些非常慢的代码。我知道会的,现在是了。基本上,我从一堆目录中读取文件。文件名会更改,但数据不会更改。为了确定是否已读取该文件,我对其字节进行散列,并将其与已处理文件的散列列表进行比较。每个目录中大约有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
    }
}
公共静态类程序扩展
{
公共静态字节[]tosha256散列(此FileInfo文件)
{
使用(FileStream fs=newfilestream(file.FullName,FileMode.Open))
{
使用(SHA256 hasher=new SHA256Managed())
{
返回hasher.ComputeHash(fs);
}
}
}
公共静态字符串到HexString(此字节[]p)
{
char[]c=新字符[p.Length*2+2];
字节b;
c[0]=“0”;c[1]=“x”;
对于(int y=0,x=2;y>4));
c[x]=(字符)(b>9?b+0x37:b+0x30);
b=((字节)(p[y]&0xF));
c[++x]=(字符)(b>9?b+0x37:b+0x30);
}
返回新字符串(c);
}
}
班级计划
{
静态void Main(字符串[]参数)
{
var allFiles=new DirectoryInfo(“c:\\temp”).GetFiles(“*”);
List readFileHashes=GetReadFileHashes();
List filesToRead=新列表();
foreach(所有文件中的var文件)
{
if(readFileHashes.Contains(file.tosha256 hash().ToHexString()))
添加(文件);
}
//读取新文件
}
}

有什么方法可以加快速度吗?

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

我相信您可以通过首先检查文件大小来归档最显著的性能改进,如果文件大小不匹配,您可以跳过整个文件,甚至不打开它

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

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

  • 当第一个字节不同时,执行二进制比较或提前中止(节省读取整个文件的时间,这可能是一个非常显著的改进,如果您的文件通常较大,任何哈希算法都会读取整个文件。检测到第一个字节不同,则节省读取文件其余部分的时间)。如果您的查找文件列表可能包含许多大小相同的文件,因此您可能需要对多个文件进行二进制比较,请考虑:

  • 以块为单位进行散列,每个块为1MB。首先,仅根据查找中预先计算的第一个块哈希值检查第一个块。如果第一个块相同,则仅比较第二个块,在大多数情况下,对于不同的文件,在第一个块之外保存读取。只有当文件很大时,这两个选项才真正值得付出努力

我怀疑更改散列算法本身(例如,按照建议进行CRC的第一次检查)是否会产生任何显著差异。您的瓶颈可能是磁盘IO,而不是CPU,因此避免磁盘IO将给您带来最大的改进。但与以往一样,在绩效方面,要进行衡量

然后,如果这还不够(而且只有到那时),尝试异步IO(请记住,顺序读取通常比随机访问快,因此过多的随机异步读取可能会影响性能)

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

    • 您对问题的描述仍然不够清楚

      最大的问题是,您正在进行大量的哈希运算。这肯定是缓慢的

      您可能希望尝试搜索修改时间,如果文件名更改,修改时间不会更改:

      或者,您可能希望监视文件夹中的任何新文件更改:

      • 为readFileHashes存储使用具有高效搜索功能(哈希或二进制搜索)的数据结构。我想HashSet或TreeSet在这里更适合你

      • 使用适当的校验和(哈希和)函数。SHA256是一个加密散列,可能是杀伤力过大。CRC的计算成本较低,最初用于捕获无意/随机更改(传输错误),但可接受设计/打算隐藏的更改。您正在扫描的文件之间的差异是什么

        通过采样实现的简单校验和(例如校验和=(前10个字节和后10个字节))有效吗


      首先按文件大小对文件进行分组-这将为您留下更小的文件组。现在它取决于组大小和文件大小。您可以开始并行读取所有文件,直到找到差异。如果存在差异,请将组拆分为在当前位置具有相同值的较小组。如果您有关于文件差异的信息,您可以使用这些信息—从末尾开始读取,如果较大的集群发生变化,不要逐字节读取和比较,或者您知道的关于文件的任何信息。如果您必须并行读取多个文件,从而导致随机磁盘访问,则此解决方案可能会带来I/O性能问题

      您还可以计算每个组中所有文件的哈希值并进行比较。你不必做公关