在Python中为大文件创建校验和的最快方法
我需要在网络上传输大文件,并且需要每小时为它们生成校验和。所以,生成校验和的速度对我来说非常重要。
但是,我在64位的Windows XP专业版机器上,发现zlib.crc32和zlib.adler32在处理超过4GB的文件时无法正常工作。我怀疑我遇到了32位的限制?使用hashlib.md5我可以得到结果,但问题是速度太慢。生成一个4.8GB文件的md5大约需要5分钟。任务管理器显示这个过程只使用了一个核心。
我有几个问题:
- 有没有办法让crc在大文件上工作?我更喜欢使用crc而不是md5。
- 如果不行,有没有办法加快md5.hexdigest()/md5.digest的速度?或者说其他hashlib的hexdigest/digest?也许可以把它分成多线程处理?我该怎么做?
附注:我正在做一个类似“资产管理”的系统,有点像svn,但资产是由大型压缩图像文件组成。这些文件有微小的增量变化。生成哈希/校验和是为了检测变化和错误。
6 个回答
你可能遇到了XP系统对文件大小的限制。64位系统可以让你使用更多的地址空间(这意味着每个应用程序不再受限于大约2GB的地址空间),但这对文件大小的问题可能没有什么帮助。
首先,任何CRC算法本身并没有限制它们不能处理任意长度的数据(不过,某些具体的实现可能会设定一个限制)。
不过,在文件同步的应用中,这个限制可能并不重要,因为当文件变得很大的时候,你可能不想对整个文件进行哈希处理,而是只处理一些小块。如果你对整个文件进行哈希,如果两端的哈希值不一样,你就得复制整个文件。如果你对固定大小的小块进行哈希,那么你只需要复制那些哈希值发生变化的小块。如果文件的大部分变化都是局部的(比如数据库),那么这样做通常会需要更少的复制工作(而且在多个核心之间分散计算也更容易)。
至于哈希算法本身,基本的权衡是速度和碰撞的可能性(两个不同的数据块产生相同的哈希值)。CRC-32速度很快,但它只有2^32个唯一值,所以可能会出现碰撞。MD5速度慢得多,但它有2^128个唯一值,因此几乎不会出现碰撞(但理论上还是可能的)。更大的哈希算法(比如SHA1、SHA256等)有更多的唯一值,但速度也更慢:我怀疑你需要这些,因为你担心的是意外的碰撞,而不是像数字签名应用那样担心故意制造的碰撞。
听起来你想做的事情和rsync工具非常相似。你能直接使用rsync吗?
这是一个算法选择问题,而不是库或语言选择问题!
主要有两个要考虑的点:
- 磁盘读写(disk I/O)对整体性能的影响有多大?
- 错误检测功能的可靠性预期是什么?
显然,第二个问题的答案大概是“允许一些假阴性”,因为对于一个4Gb的信息,任何32位的哈希值在一个稍微嘈杂的环境下,其可靠性都不会是绝对的。
假设通过多线程可以改善读写性能,我们可以选择一种不需要对整个信息进行顺序扫描的哈希算法。我们可以并行处理文件,分别对每个部分进行哈希计算,然后将这些哈希值组合起来,或者直接拼接,形成一个更长、更可靠的错误检测工具。
下一步可以将文件处理形式化为有序的部分,并以这种方式进行传输(在接收端再重新组合)。这种方法,加上关于文件生成方式的额外信息(例如,它们可能只通过追加的方式进行修改,就像日志文件一样),甚至可以减少所需的哈希计算量。不过,这种方法的复杂性需要与快速的CRC计算需求进行权衡。
附带说明:Alder32并不局限于特定大小以下的消息。这可能只是zlib API的限制。(顺便说一下,我找到的关于zlib.adler32的参考资料使用了缓冲区,而在处理我们的大消息时,这种方法应该避免,应该采用流式处理:从文件中读取一点,计算,然后重复……)