У меня есть каталоги, содержащие от 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
. Я читаю с жесткого диска. Я читал о чтении с отображением памяти, но не мог понять, применимо ли оно в этой ситуации или нет.
Мой вопрос: есть ли способ оптимизировать эту операцию чтения?
Почему вы отключили буферизацию (buffering=0
)?
Чем поможет пул, если только один поток одновременно может читать с диска? Буферизация отключена без особой причины. Просто немного осталось после операции копирования-вставки.
Вы можете попытаться распараллелить код вычисления хэша, как показано ниже. Однако производительность зависит от того, сколько параллельных запросов ввода-вывода может обработать диск, а также от того, сколько ядер имеет ваш ЦП. Но вы можете попробовать.
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)
Я думал, что распараллеливание кода при привязке к вводу-выводу мало что дало, а перемещение считывающей головки для этого было проблемой?
Также возможно, что проблема связана с количеством файлов в каталоге. Производительность некоторых файловых систем сильно снижается, когда в одном каталоге размещаются тысячи файлов. Если у вас есть 100 КБ или более файлов в одном каталоге, файловой системе требуется значительное время только для того, чтобы найти файл перед его открытием и чтением.
Тем не менее, давайте немного подумаем об этом. Если я правильно понимаю ваши выходные данные, ваша программа выполнила примерно 102 КБ из 335 КБ файлов за четыре часа и 42 минуты. В круглых числах это около 6 файлов в секунду. Таким образом, на создание всех 335-килобайтных файлов уйдет около 15,5 часов.
Если это одноразовая задача, просто настройте ее на выполнение на ночь, и она будет выполнена, когда вы вернетесь на работу утром. Если вам нужно проиндексировать миллион файлов, начните процесс в пятницу вечером, и он будет готов, когда вы вернетесь в офис в понедельник.
Если это не разовая задача, то у вас другие проблемы. . .
Файлы находятся не в одном каталоге, а в древовидной структуре, где каждая «листовая папка» содержит около 10 000 изображений. Не уверены, что это все еще будет иметь значение? Отметка времени на самом деле в мин: сек. Из-за кеша ЦП потребовалось 4 секунды, чтобы сделать первые 31%, и всего ~ 50 минут для всех ~ 300 000 изображений. Так что ничего страшного.
10 КБ изображений в одной папке определенно были проблемой для NTFS некоторое время назад. Не знаю, есть ли еще. Но если вы можете сделать 300 тыс. изображений менее чем за час, и это разовая (или, по крайней мере, нечастая) задача, то, вероятно, у вас все в порядке. Если это достаточно быстро, оставьте его в покое. :)
Вам следует подумать о том, чтобы попробовать asyncio или пул потоков, если вы привязаны к io.