5

我目前的做法是这样的:

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()

在 i5 @ 1.7 GHz 上计算 842MB iso 文件的 md5sum 大约需要 3.5 秒。我尝试了不同的读取文件的方法,但所有这些方法都会产生较慢的结果。也许有更快的解决方案?

编辑:我将2**16(内部f.read())替换为1024*func.block_size,因为block_sizehashlib 支持的大多数散列函数的默认值是64(除了 '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()

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

4

1 回答 1

3

使用md5openssl 工具需要 2 秒的 874 MiB 随机数据文件,我能够提高速度如下。

  • 使用第一种方法需要 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))

人类时间就是我上面提到的。而所有这些的处理器时间与 IO 阻塞的差异大致相同。

这里的关键决定因素是要有足够大的缓冲区大小以减少磁盘延迟,但又要足够小以避免 VM 页面交换。对于我的特定机器,64 KiB 似乎是最佳的。

于 2014-05-09T17:19:57.310 回答