在Python中,计算文件哈希(使用hashlib)是否有比这个更快的方法?

6 投票
1 回答
5780 浏览
提问于 2025-04-18 00:27

我现在的做法是这样的:

def get_hash(path=PATH, hash_type='md5'):
    func = getattr(hashlib, hash_type)()
    with open(path, 'rb') as f:
         for block in iter(lambda: f.read(1024*func.block_size, b''):
             func.update(block)
    return func.hexdigest()

在一台1.7 GHz的i5电脑上,计算一个842MB的iso文件的md5值大约需要3.5秒。我尝试了不同的读取文件的方法,但结果都比较慢。有没有可能有更快的解决方案呢?

补充:我把2**16(在f.read()里面)换成了1024*func.block_size,因为大多数由hashlib支持的哈希函数的默认block_size64(除了'sha384'和'sha512',它们的默认block_size128)。所以,块大小还是一样的(65536位)。

补充(2):我做错了。现在需要8.4秒,而不是3.5秒。 :(

补充(3):显然,当我再次运行这个函数时,Windows的磁盘使用率超过了80%。实际上只需要3.5秒。呼。

另一个解决方案(大约快了0.5秒)是使用os.open():

def get_hash(path=PATH, hash_type='md5'):
    func = getattr(hashlib, hash_type)()
    f = os.open(path, (os.O_RDWR | os.O_BINARY))
    for block in iter(lambda: os.read(f, 2048*func.block_size), b''):
        func.update(block)
    os.close(f)
    return func.hexdigest()

请注意,这些结果并不是最终的。

1 个回答

4

我使用了一个874 MiB的随机数据文件,使用md5的openssl工具处理这个文件需要2秒钟。然后我尝试了一些方法来提高速度,结果如下:

  • 用你提到的第一种方法,处理这个文件需要21秒。
  • 先把整个文件读入(花了21秒),然后再更新需要2秒。
  • 使用下面这个函数,缓冲区大小为8096,处理时间是17秒。
  • 使用下面这个函数,缓冲区大小为32767,处理时间是11秒。
  • 使用下面这个函数,缓冲区大小为65536,处理时间是8秒。
  • 使用下面这个函数,缓冲区大小为131072,处理时间也是8秒。
  • 使用下面这个函数,缓冲区大小为1048576,处理时间是12秒。
def md5_speedcheck(path, size):
    pts = time.process_time()
    ats = time.time()
    m = hashlib.md5()
    with open(path, 'rb') as f:
        b = f.read(size)
        while len(b) > 0:
            m.update(b)
            b = f.read(size)
    print("{0:.3f} s".format(time.process_time() - pts))
    print("{0:.3f} s".format(time.time() - ats))

上面提到的时间是人类的处理时间。而所有这些操作的处理器时间差不多,主要的差别在于输入输出的阻塞时间。

这里的关键是选择一个合适的缓冲区大小,既要足够大以减少磁盘延迟,又要小到不会导致虚拟内存的页面交换。对我这台机器来说,64 KiB的缓冲区大小似乎是最优的。

撰写回答