¿Hay una forma más rápida (que esta) de calcular el hash de un archivo (usando hashlib) en Python?

Mi enfoque actual es este:

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

Se tarda unos 3,5 segundos para calcular el md5sum de un archivo iso de 842MB en un i5 a 1.7 GHz. He intentado diferentes métodos de leer el archivo, pero todos ellos producen resultados más lentos. ¿Hay, quizás, una solución más rápida?

EDITAR: reemplacé 2**16 (dentro de f.read() ) con 1024*func.block_size , ya que el block_size predeterminado para la mayoría de las funciones de hashing soportadas por hashlib es 64 (excepto ‘sha384’ y ‘sha512’ – para ellos , el block_size predeterminado es 128 ). Por lo tanto, el tamaño del bloque sigue siendo el mismo (65536 bits).

EDIT (2): hice algo mal. Se tarda 8.4 segundos en lugar de 3.5. 🙁

EDITAR (3): Aparentemente, Windows estaba usando el disco al + 80% cuando ejecuté la función nuevamente. Realmente toma 3.5 segundos. Uf.

Otra solución (~ -0.5 seg, ligeramente más rápida) es usar 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() 

Tenga en cuenta que estos resultados no son finales.

Usando un archivo de datos aleatorios 874 MiB que requirió 2 segundos con la herramienta md5 openssl, pude mejorar la velocidad de la siguiente manera.

  • Usando su primer método requiere 21 segundos.
  • La lectura de todo el archivo (21 segundos) para almacenar en búfer y luego la actualización requiere 2 segundos.
  • El uso de la siguiente función con un tamaño de búfer de 8096 requirió 17 segundos.
  • El uso de la siguiente función con un tamaño de búfer de 32767 requirió 11 segundos.
  • El uso de la siguiente función con un tamaño de búfer de 65536 requirió 8 segundos.
  • El uso de la siguiente función con un tamaño de búfer de 131072 requirió 8 segundos.
  • El uso de la siguiente función con un tamaño de búfer de 1048576 requirió 12 segundos.

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

El tiempo humano es lo que noté arriba. Mientras que el tiempo del procesador para todos estos es aproximadamente el mismo, la diferencia se toma en el locking de IO.

El determinante clave aquí es tener un tamaño de búfer que sea lo suficientemente grande para mitigar la latencia del disco, pero lo suficientemente pequeño para evitar el intercambio de páginas de la máquina virtual. Para mi máquina en particular, parece que 64 KiB es óptimo.