【问题标题】:What is the fastest way to check if files are identical?检查文件是否相同的最快方法是什么?
【发布时间】:2023-03-16 18:49:01
【问题描述】:

如果您有 1,000,0000 个源文件,您怀疑它们都是相同的,并且您想比较它们目前比较这些文件的禁食方法是什么?假设它们是 Java 文件,并且进行比较的平台并不重要。 cksum 让我哭了。当我的意思是相同时,我的意思是所有相同。

更新:我知道生成校验和。 diff 是可笑的......我想要速度。

更新:不要拘泥于它们是源文件这一事实。例如,假设您运行了一百万次输出非常规范的程序。您想证明输出的所有 1,000,000 个版本都是相同的。

更新:读取块数而不是字节数?立即扔掉那些?这比查找字节数更快吗?

更新:这与比较两个文件的最快方法有什么不同吗?

【问题讨论】:

  • diff 之所以可笑,只是因为它是错误的工具——它为您提供了计算得出的差异。如果您只想知道两个文件是否相同,请使用 cmp。
  • 1,000,0000 个源文件,比较一下,我无法想象 100 万个源文件在哪里使用。
  • 您提到它们是 Java 文件。您是否需要一个可以忽略空格和格式差异的工具?
  • 假设您运行一个程序一百万次...绝对需要相同...您想比较百万个不同的输出...
  • @ojblass,使用线程... zoundsss。

标签: language-agnostic file comparison


【解决方案1】:

我会选择类似于cmp 程序所采用的方法:打开两个文件(比如文件 1 和文件 2),从每个文件中读取一个块,然后逐字节比较它们。如果它们匹配,则从每个块中读取下一个块,逐字节比较它们,等等。如果您到达两个文件的末尾而没有检测到任何差异,则查找文件 1 的开头,关闭文件 2 并打开文件 3在它的位置,并重复,直到您检查了所有文件。我认为没有任何方法可以避免读取所有文件的所有字节,如果它们实际上都是相同的,但我认为这种方法是(或接近于)检测可能存在的任何差异的最快方法。

OP修改:提升Mark Bessey的重要评论

“如果预计文件大部分相同,并且如果它们相对较小,则另一个明显的优化是将其中一个文件完全保留在内存中。这减少了尝试一次读取两个文件时的颠簸。 "

【讨论】:

  • 我认为 compare 有一个设置放弃一个发现差异。
  • 我想知道如果使用 ZFS 之类的文件系统是否可以为您提供廉价的校验和。
  • 另一个明显的优化,如果预计文件大部分相同,如果它们相对较小,则将其中一个文件完全保留在内存中。这减少了尝试一次读取两个文件时的抖动。
  • 没有比逐个字节比较它们更快的方法来证明两个文件相同(duh),但是对于典型文件,有可能更快的方法来证明它们是不 一样。如果文件大部分相同,但只有一个部分(通常是开头或结尾)不同,则首先对文件的开头和结尾进行采样会更快地发现差异。
  • @endolith 你很了解快速失败。早在 2009 年,我就提供了一个链接,指向解决这个确切问题的 Knuth-Morris-Pratt 方法(是的,Donald Knuth),但显然人们只阅读了最热门的答案,因为这个明显更好的答案今天得到了第一次支持。
【解决方案2】:

大多数人在他们的回答中都忽略了必须反复比较文件这一事实。因此校验和更快,因为校验和计算一次并存储在内存中(而不是顺序读取文件 n 次)。

【讨论】:

  • @Doug,没有意义,我们仍然需要计算所有一百万个文件的校验和。计算校验和将比直接比较花费更多的时间。
  • OP 只询问是否所有文件都相同。因此,您可以选择一个可以比较所有 n-1 个文件的文件。因此,您最多只需要阅读所有 n 个文件,如果它们实际上都是相同的。如果 OP 询问哪些文件是相同的,我会计算校验和并将它们排序到具有相同校验和的文件组中。
【解决方案3】:

假设期望文件是相同的(听起来就是这种情况),那么处理校验和/哈希是浪费时间 - 它们很可能是相同的,你会有重新读取文件以获得最终证明(我还假设因为你想“证明......它们是相同的”,让它们散列到相同的值是不够的)。

如果是这种情况,我认为the solution proposed by David 非常接近您需要做的事情。可以做一些事情来优化比较,增加复杂性:

  • 在进行比较之前检查文件大小是否相同
  • 尽可能使用最快的 memcmp()(比较字而不是字节 - 大多数 C 运行时应该已经这样做了)
  • 使用多个线程进行内存块比较(取决于系统上可用的处理器数量,超过会导致您的线程相互争用)
  • 使用重叠/异步 I/O 以使 I/O 通道尽可能繁忙,但也要仔细分析,以便尽可能少地在文件之间颠簸(如果文件在几个不同的磁盘和 I/O 之间划分)端口,更好)

【讨论】:

  • 首先检查文件大小是否相同是一种显而易见的事情,很容易忘记去做——谢谢!
【解决方案4】:

更新:不要拘泥于它们是源文件的事实。例如,假设您运行了一百万次输出非常规范的程序。您想证明所有 1,000,000 个版本的输出都是相同的。

如果您可以控制输出,则让创建文件/输出的程序即时创建一个 md5 并将其嵌入文件或输出流中,甚至通过一个在此过程中创建 md5 并存储的程序通过管道传输输出它以某种方式与数据一起,重点是当字节已经在内存中时进行计算。

如果你不能做到这一点,那么就像其他人所说的那样,检查文件大小,然后对相同大小的文件进行逐字节的直接比较,我看不出任何类型的二进制除法或 md5 计算如何更好与直接比较相比,您必须触摸每个字节以证明相等,以任何方式切割它,因此您不妨减少每个字节所需的计算量,并获得在发现不匹配时立即切断的能力。

如果您打算稍后再将这些与新输出进行比较,md5 计算将很有用,但您基本上回到了我尽快计算 md5 的第一点

【讨论】:

    【解决方案5】:

    先比较一下文件长度全部百万。如果您有一种便宜的方法,请从最大的文件开始。如果它们都通过了,则使用二进制除法模式比较每个文件;这将在相似但不相同的文件上更快地失败。有关此比较方法的信息,请参阅Knuth-Morris-Pratt method

    【讨论】:

      【解决方案6】:

      有许多程序通常会比较一组文件以找到相同的文件。 FDUPES 是一个不错的选择:Link。根据输入的确切性质,一百万个文件应该不是问题。我认为 FDUPES 需要 Linux,但其他平台也有其他此类程序。

      我尝试自己编写一个更快的程序,但除了特殊情况,FDUPES 更快。

      无论如何,一般的想法是从检查文件的大小开始。大小不同的文件不可能相等,因此您只需查看大小相同的文件组。如果您想要获得最佳性能,情况就会变得更加复杂:如果文件可能不同,您应该比较文件的一小部分,希望尽早发现差异,这样您就不必阅读其余部分。但是,如果文件可能相同,则读取每个文件以计算校验和会更快,因为这样您就可以从磁盘顺序读取,而不是在两个或多个文件之间来回跳转。 (这里假设普通磁盘,所以 SSD:s 可能不同。)

      在我的基准测试中,当我试图制作一个更快的程序时(令我惊讶的是)首先读取每个文件以计算校验和,然后如果校验和相等,则通过读取直接比较文件从每个文件中交替读取一个块,而不是在没有先前校验和计算的情况下交替读取块!原来,在计算校验和时,Linux 将两个文件缓存在主存中,顺序读取每个文件,然后第二次读取速度非常快。从交替读取开始时,文件不是(物理上)顺序读取的。

      编辑:

      有些人表示惊讶,甚至怀疑两次读取文件会比只读取一次更快。也许我没有设法非常清楚地解释我在做什么。我说的是缓存预加载,以便在以后以在物理磁盘驱动器上执行速度很慢的方式访问文件时将文件放在磁盘缓存中。 Here 是一个网页,我试图用图片、C 代码和测量进行更详细的解释。

      但是,这与原始问题(充其量)具有边际相关性。

      【讨论】:

      • 是的。实际上,我有点生气。
      • 即使文件被缓存,它必须花费更长的时间来读取所有字节以散列它们然后重新处理它们以进行比较,而不是仅仅读取并将它们比较从...开始。同样,比较可以在第一次不匹配时中止。因此,您的测试是错误的,或者您对所用时间的测量是错误的。
      • 给定两个 1,000,000 字节的文件,执行 1,000,000 x "if(chr1!=chr2)" 必须快于 1,000,000 x "hash.update(ch1)" 加上 1,000,000 x "hash.update(ch2 )",即使散列是通过使用带有数组或指针的 update() 函数进行优化的。
      • @Thomas:您在读取文件进行比较时使用了多大的缓冲区?
      • Software Monkey 上面的前两个 cmets:记住缓存。从物理磁盘顺序读取两个文件到内存中比并行读取它们更快,在它们之间交替读取(来回移动读取头)。您稍后执行的所有操作(将所有数据都缓存在内存中)相对要快得多。但是,是的,这取决于数据,这是一个平均值。开始时实际上确实不同的两个文件逐字节比较会更快。
      【解决方案7】:

      使用cksum 不如使用md5sum 之类的可靠。但我会选择最大可靠性,这意味着使用cmp 进行逐字节比较。

      对于所有检查方法,您必须读取两个文件中的每个字节,因此您不妨选择最可靠的方法。

      首先,您可以检查目录列表以查看大小是否不同。这是一种快速获得不同文件反馈的快捷方式。

      【讨论】:

      • 你真的需要读取每个文件的每个字节吗?如果一个文件失宠,你能停止阅读吗?
      • 是的,您可以在发现差异时停下来,这是 cmp 解决方案对任何 cksum/md5sum/any-checksum 解决方案的优势。对于相同的文件,您必须阅读很多内容(即使是 cmp)。
      【解决方案8】:

      最佳算法取决于重复文件的数量。

      假设一些是相同的,但大多数是不同的并且文件很大。

      使用简单的文件长度检查过滤掉明显不同的那些。

      从文件中选择随机字节,计算哈希并比较(最小化磁盘查找)

      使用完整的文件 SHA1.

      【讨论】:

      • 目录中的文件大小是个好主意,但随机字节是无用的,因为您仍然必须比较整个文件才能可靠。并且任何类型的校验和都将比缓冲的逐字节比较慢。
      • 看看所有一百万个字节的最后一个字节怎么样?
      • 如果你有大量相同大小但不同的文件,随机字节会更快,你可以使用随机字节来避免计算完整的哈希
      • 随机字节是一种不可靠的方法 - 您可以检查两个文件中除一个字节之外的所有字节,但仍不能 100% 确定它们相同。您必须检查每个字节,只要您必须这样做,缓冲顺序读取将优于随机访问。
      • 如果您在文件中发现差异,我相信您的来源是相同的提前退出策略。但是 cmp 解决方案已经具备了这一点。
      【解决方案9】:

      我认为散列不会比逐字节比较快。可以通过流水线读取和比较字节来优化逐字节比较,也可以在并行线程中比较文件的多个部分。它会是这样的:

      • 检查文件大小是否不同
      • 将文件块异步读入内存
      • 将它们交给工作线程进行比较

      或者只是并行运行 cmp(或您的操作系统的等效程序)。这可以很容易地编写脚本,并且您仍然可以从并行性中受益。

      【讨论】:

      • 磁盘总线不是这里的瓶颈吗?也许分散文件......你在这里做点什么......
      • 记住物理磁盘是瓶颈!当我尝试逐字节比较时,结果发现它比校验和慢,因为磁盘的物理特性。当您计算文件的校验和时,您会从磁盘顺序读取块(假设没有太多碎片)。当您直接比较文件并并行读取它们时,您必须读取整个磁盘的块。
      • “请记住,物理磁盘是瓶颈!当我尝试逐字节比较时......”您当然会一次读取相当大的文件块。小文件实际上会被完全吞食。将两组文件放在两个不同的磁盘上是理想的。
      • 这真的取决于你的磁盘是如何排列的。您可以将文件分布在不同的设备上,以便文件的 i/o 位于不同的主轴上,并且单个文件可能是连续的。您甚至可以将文件复制到 ramdisk 并在那里进行比较。
      • 重读问题后,如果你不断比较一个程序的输出,那么你只需要比较最后两次运行的输出即可。
      【解决方案10】:

      使用布隆过滤器的概念。 这里简单解释一下:http://crzyjcky.com/2013/01/03/the-magical-bloom-filter/

      它为您提供恒定的比较时间。但是这种方法不能单独使用。 Apache Cassandra 和 HBase 在内部使用这种技术。

      它基本上以非常快速的方式告诉你文件不相同。如果它说文件是相同的,你必须使用可靠的方法进行另一轮检查。

      【讨论】:

        【解决方案11】:

        我会运行这样的东西

        find -name \*.java -print0 | xargs -0 md5sum | sort
        

        然后查看哪些文件具有不同的 MD5 和。这将按校验和对文件进行分组。

        如果你愿意,你可以替换 md5sum which sha1sum 甚至 rmd160。

        【讨论】:

        • 而你还是需要手动比较这100万对md5
        • 第一步应该是按文件大小对文件进行分组!之后,MD5 对所有组的至少两个长度相等的文件可能会是最快的方法。 (并且不要手动比较校验和 - 你有一台电脑!)
        【解决方案12】:

        为什么要重新发明轮子?第三方应用怎么样?当然,它没有 API,但我不认为您经常将自己置于这种情况下。我喜欢这个应用程序doublekiller 只需在开始之前进行备份。 :) 它既快速又免费!

        【讨论】:

          【解决方案13】:

          除了比较之外,同步两个文件夹,超级快!我们每天都在使用它。

          【讨论】:

            【解决方案14】:

            我刚刚编写了一个 c# 应用程序,它可以执行与您想要的类似的操作。我的代码是这样做的。

            将每个文件的所有大小读入列表或数组中。

            使用 for 循环检查这些大小是否相同。 如果它们大小相同,则将一个文件的一个字节与另一个文件的一个字节进行比较。如果两个字节相同,则移动到下一个字节。如果发现差异,则返回文件不同。

            如果到达两个文件的末尾,并且最后两个字节相同,则文件必须相同。

            我已经尝试比较文件的 MD5 哈希值,而不是逐个字节地进行比较,我发现这种方法经常会遗漏相同的文件,但它的速度要快得多。

            【讨论】:

              【解决方案15】:

              在我看来,这是一个文件系统操作。因此,首先,请谨慎选择您的文件系统。接下来,去重。然后比较inode。喜欢:

              % find / -inum "$(ls -di "./test.file" | grep -E '^[0-9]*')"
              <list of identical files provided in a few seconds to a minute>
              

              【讨论】:

              • 你所说的“去重”到底是什么意思,你是怎么做的?
              【解决方案16】:

              如果要逐个比较文件,请使用 ExamDiff。

              【讨论】:

                【解决方案17】:

                我将首先创建一个数据库表,其中包含 pathname 列和 file_contents 的 sha_1,
                所有文件并存储路径名和 sha_1,
                然后在随后的存储中将其放入数据库中,
                sha_1 文件检查数据库中是否存在 sha_1,
                如果在数据库中,
                输出到该文件与路径名存在的日志,
                用它做任何事情大声笑创建一个符号链接。
                在文件上传后在您的验证中实施它,

                【讨论】:

                  【解决方案18】:

                  MD5 散列会比比较快,但比普通的 CRC 检查要慢。你必须弄清楚你想要比较的可靠性。

                  【讨论】:

                  • 为什么MD5会比比较快?他们都必须读取两个文件中的所有字节。
                  • 其实比较应该更快,因为一旦碰到不同的字节就可以中止。并先检查文件大小。
                  • 这(在第一个不同的字节后中止)正是我的想法,但结果证明是错误的,至少在处理大文件时是这样!看我的回答。
                  • @Thomas:您的测试一定有缺陷 - 请参阅我的 cmets 以了解您的答案。
                  • @Software Monkey:也许我们彼此误解了,意思不同,但我运行的测试很清楚:读取两个文件有时比读取一次更快!实际上,我现在重新运行了一个简化版本的测试,并且(在一台特定的计算机上,使用一对特定的文件)读取这两个文件两次的速度几乎是读取一次的两倍!
                  猜你喜欢
                  • 2010-11-24
                  • 2010-12-18
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多