Ускорьте чтение/хеширование миллионов файлов/изображений

У меня есть каталоги, содержащие от 100 000 до 1 000 000 изображений. Я собираюсь создать хеш для каждого изображения, чтобы в будущем я мог найти точное совпадение на основе этих хэшей. Мой текущий подход:

def hash_test(images):      # images is a list of image paths
    hashes = []
    for image in images:
        with open(folder + image, 'rb', buffering=0) as f:
           hashes.append(hashlib.sha256(f.read()).hexdigest())
           # hashes.append(CityHash128(f.read()))
    return hashes
31%|███       | 102193/334887 [00:04<42:15, 112.02it/s]

Из того, что я могу сказать из своих экспериментов, операция file.read() является моим узким местом, а это означает, что я ограничен вводом-выводом. Это также подтверждается проверкой iotop . Я читаю с жесткого диска. Я читал о чтении с отображением памяти, но не мог понять, применимо ли оно в этой ситуации или нет.

Мой вопрос: есть ли способ оптимизировать эту операцию чтения?

Вам следует подумать о том, чтобы попробовать asyncio или пул потоков, если вы привязаны к io.

juanpa.arrivillaga 27.05.2019 11:59

Почему вы отключили буферизацию (buffering=0)?

President James K. Polk 29.05.2019 02:31

Чем поможет пул, если только один поток одновременно может читать с диска? Буферизация отключена без особой причины. Просто немного осталось после операции копирования-вставки.

Mathias Stensrud 29.05.2019 23:03
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
3
79
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете попытаться распараллелить код вычисления хэша, как показано ниже. Однако производительность зависит от того, сколько параллельных запросов ввода-вывода может обработать диск, а также от того, сколько ядер имеет ваш ЦП. Но вы можете попробовать.


from multiprocessing import Pool

# This function will return hashes as list
# Will wait for all parallel hash computation to complete

def parallel_hash(images):
    with Pool(5) as pool:
        return pool.map(hash_test, images)

def hash_test(image):      # images is a list of image paths
    with open(folder + image, 'rb', buffering=0) as f:
        return hashlib.sha256(f.read()).hexdigest()
        # hashes.append(CityHash128(f.read()))

parallel_hash(images)

Я думал, что распараллеливание кода при привязке к вводу-выводу мало что дало, а перемещение считывающей головки для этого было проблемой?

Mathias Stensrud 29.05.2019 23:02

Также возможно, что проблема связана с количеством файлов в каталоге. Производительность некоторых файловых систем сильно снижается, когда в одном каталоге размещаются тысячи файлов. Если у вас есть 100 КБ или более файлов в одном каталоге, файловой системе требуется значительное время только для того, чтобы найти файл перед его открытием и чтением.

Тем не менее, давайте немного подумаем об этом. Если я правильно понимаю ваши выходные данные, ваша программа выполнила примерно 102 КБ из 335 КБ файлов за четыре часа и 42 минуты. В круглых числах это около 6 файлов в секунду. Таким образом, на создание всех 335-килобайтных файлов уйдет около 15,5 часов.

Если это одноразовая задача, просто настройте ее на выполнение на ночь, и она будет выполнена, когда вы вернетесь на работу утром. Если вам нужно проиндексировать миллион файлов, начните процесс в пятницу вечером, и он будет готов, когда вы вернетесь в офис в понедельник.

Если это не разовая задача, то у вас другие проблемы. . .

Файлы находятся не в одном каталоге, а в древовидной структуре, где каждая «листовая папка» содержит около 10 000 изображений. Не уверены, что это все еще будет иметь значение? Отметка времени на самом деле в мин: сек. Из-за кеша ЦП потребовалось 4 секунды, чтобы сделать первые 31%, и всего ~ 50 минут для всех ~ 300 000 изображений. Так что ничего страшного.

Mathias Stensrud 29.05.2019 23:11

10 КБ изображений в одной папке определенно были проблемой для NTFS некоторое время назад. Не знаю, есть ли еще. Но если вы можете сделать 300 тыс. изображений менее чем за час, и это разовая (или, по крайней мере, нечастая) задача, то, вероятно, у вас все в порядке. Если это достаточно быстро, оставьте его в покое. :)

Jim Mischel 30.05.2019 02:34

Другие вопросы по теме