Hashlib: оптимальный размер кусков для использования в md5.update ()

Это относится к Получение MD5-хэша больших файлов в Python и Hashlib в Windows и Linux

В ответах на оба этих вопроса рекомендуется использовать большие фрагменты данных в функции md5.update () для повышения производительности.

Все проведенные мной тесты показывают, что использование меньших фрагментов дает наилучшую производительность.

Рассмотрим следующий код:

def test(factor):
    filehash = hashlib.md5()
    blk_size_to_read = filehash.block_size * (2**factor)
    with open(largetestfile, 'rb') as f:
        read_data = f.read(blk_size_to_read)
        filehash.update(read_data)
    filehash.digest()

if __name__ == '__main__':
    for ctr in xrange(0, 12):
        funcstr = "test({})".format(str(ctr))
        timetaken = timeit.timeit(funcstr, setup="from __main__ import test", number = 5000)
        print "Factor: {} Time: {}".format(str(ctr), str(timetaken))

Все проведенные мною тесты показывают, что наилучшая производительность достигается при использовании factor 0 или 1 (то есть 64 или 128 байтов).

По какой причине я вижу результаты, отличные от тех, которые указаны в процитированных вопросах?

Я пробовал двоичные и простые текстовые файлы размером от 700 МБ до 1,2 ГБ и использую Python 2.7.3 на Ubuntu 12.04.

Второй вопрос: я использую timeit так, как должно быть?


person Verma    schedule 18.07.2013    source источник


Ответы (1)


Нашли ошибку! Я read проглотил только один кусок, а потом ничего не делал!

Измененный

with open(largetestfile, 'rb') as f:
    read_data = f.read(blk_size_to_read)
    filehash.update(read_data)

to

with open(testfile, 'rb') as f:
    while (True):
        read_data = f.read(blk_size_to_read)
        if not read_data:
            break
        filehash.update(read_data)

чтобы исправить проблему.

ОБНОВИТЬ:

Я запустил слегка измененную версию указанной выше программы, чтобы установить наилучший размер буфера, который будет использоваться при постепенном использовании update () для нахождения хэша данного файла. Я также хотел установить, есть ли какие-либо преимущества в инкрементном хешировании вместо вычисления хеша файла за один раз (кроме ограничений памяти).

Я создал для этого 20 файлов (со случайными данными) размером от 4096 байт до 2,1 ГБ. Хэш md5 для каждого из этих файлов был рассчитан с использованием размеров буфера, начиная с 2**6 байтов (64 байта - размер блока) до 2**20 байтов. Используя timeit, каждый из них был запущен 100 раз, и время выполнения было получено с наименьшим временем выполнения. Также было записано время выполнения хеш-вычисления всего файла за один раз.

Результаты приведены ниже...

FileName           Filesize       Chunksize      Chunked Time   Complete Time       %diff
file5.txt                 4096           4096      0.0014789      0.0014701         -0.60%
file6.txt                 8192         524288      0.0021310      0.0021060         -1.19%
file7.txt                16384          16384      0.0033200      0.0033162         -0.12%
file8.txt                32768          65536      0.0061381      0.0057440         -6.86%
file9.txt                65536          65536      0.0106990      0.0112500          4.90%
file10.txt              131072         131072      0.0203800      0.0206621          1.37%
file11.txt              262144         524288      0.0396681      0.0401120          1.11%
file12.txt              524288        1048576      0.0780780      0.0787551          0.86%
file13.txt             1048576        1048576      0.1552539      0.1564729          0.78%
file14.txt             2097152         262144      0.3101590      0.3167789          2.09%
file15.txt             4194304          65536      0.6295781      0.6477270          2.80%
file16.txt             8388608         524288      1.2633710      1.3030031          3.04%
file17.txt            16777216         524288      2.5265670      2.5925691          2.55%
file18.txt            33554432          65536      5.0558681      5.8452392         13.50%
file19.txt            67108864          65536     10.1133211     11.6993010         13.56%
file20.txt           134217728         524288     20.2226040     23.3923230         13.55%
file21.txt           268435456          65536     40.4060180     46.6972852         13.47%
file22.txt           536870912          65536     80.9403431     93.4165111         13.36%
file23.txt          1073741824         524288    161.8108051    187.1303582         13.53%
file24.txt          2147483648          65536    323.4812710    374.3899529         13.60%

Chunked Time - это время выполнения, когда файл разбивается на фрагменты и постепенно обрабатывается; Complete Time - это время выполнения, когда весь файл хешируется за один раз. %diff - это процентная разница между временем фрагментации и «временем завершения».

Наблюдения:

  1. Для файлов меньшего размера размер блока почти всегда равен размеру файла, и, похоже, нет никаких преимуществ в принятии любого из подходов.
  2. Для файлов большего размера (33554432 (2**25) байт и выше), по-видимому, наблюдается значительный выигрыш в производительности (меньшее время) при использовании инкрементного подхода вместо хеширования всего файла за один раз.
  3. Для больших файлов лучший размер блока / буфера составляет 65536 (2**16) байт.

Примечания: python 2.7.3; Ubuntu 12.06 64 бит; 8 ГБ ОЗУ. Используемый для этого код доступен здесь ... http://pastebin.com/VxH7bL2X

person Verma    schedule 19.07.2013
comment
Ради любопытства, можете ли вы назвать оптимальный размер блока? - person 2rs2ts; 19.07.2013
comment
Производительность будет асимптотически увеличиваться до максимальной теоретической пропускной способности вашей системы, выполняющей код md5, по мере увеличения размера вашего блока. К тому времени, как вы буферизуете 1MiB, любое увеличение скорости уже не имеет значения. Если вы хотите выбрать произвольный размер буфера, я предлагаю 128 КБ. Это верно для всех хэш-функций. - person gps; 20.07.2013
comment
@ 2rs2ts оптимальный размер 65536 байт. См. Обновление моего ответа выше. - person Verma; 21.07.2013