Я пишу скрипт, чтобы найти все дубликаты файлов в двух разных файлах. Скрипт отлично работает, за исключением того, что он слишком медленный, чтобы практично использовать большое количество файлов (> 1000). Профилирование моего скрипта cProfile показало, что одна строка в моем коде отвечает за почти все время выполнения.Python os.system call slow ~ 1/2 second
Линия является вызовом os.system():
cmpout = os.system("cmp -s -n 10MiB %s %s" % (callA, callB));
Этот вызов внутри для цикла, который вызывается примерно N раз, если у меня есть N идентичных файлов. Среднее время выполнения составляет 0,53 секунды.
ncalls tottime percall cumtime percall filename:lineno(function)
563 301.540 0.536 301.540 0.536 {built-in method system}
Это, конечно, быстро добавляет более тысячи файлов. Я попытался ускоряя его, заменив его вызов из модуля подпроцесса:
cmpout = call("cmp -s -n 10MiB %s %s" % (callA, callB), shell=True);
Но это имеет почти одинаковое время выполнения. Я также попытался уменьшить ограничение байта на команду cmp, но это только экономит очень мало времени.
Есть ли способ ускорить это?
Полнофункциональный Я использую:
def dirintersect(dirA, dirB):
intersectionAB = []
filesA = listfiles(dirA);
filesB = listfiles(dirB);
for (pathB, filenameB) in filesB:
for (pathA, filenameA) in filesA:
if filenameA == filenameB:
callA = shlex.quote(os.path.join(pathA, filenameA));
callB = shlex.quote(os.path.join(pathB, filenameB));
cmpout = os.system("cmp -s -n 10MiB %s %s" % (callA, callB));
#cmpout = call("cmp -s -n 10MiB %s %s" % (callA, callB), shell=True);
if cmpout is 0:
intersectionAB.append((filenameB, pathB, pathA))
return intersectionAB
Update: Спасибо за обратную связь! Я попытаюсь рассмотреть большинство ваших комментариев и дать дополнительную информацию. @Iarsmans. Вы абсолютно правы, что мои вложенные в петлевые масштабы с n² я уже выяснил, что могу сделать то же самое, используя словарь или набор и выполнять операции. Но даже накладные расходы на этот «плохой» алгоритм незначителен для времени, затрачиваемого на запуск os.system. Фактическое предложение if запускается примерно один раз для каждого имени файла (я ожидаю, что для каждого имени файла будет только один дубликат). Таким образом, os.system получает только N раз, а не N² раз, но даже для этого линейного времени это не достаточно быстро.
@ Iarsman и @Alex Reynolds: Причина, по которой я не выбрал решение хэширования, как вы предлагаете, заключается в том, что в случае использования я предполагаю, что сравниваю меньшее дерево каталогов с большим и хешируя все файлы в для большего дерева потребуется очень много времени (так как это могут быть все файлы в целом разделе), в то время как мне требуется только фактическое сравнение на небольшой части файлов.
@abarnert: Причина, по которой я использую shell = True в команде вызова, - это просто потому, что я начал с os.system, а затем прочитал, что лучше использовать subprocess.call, и это был способ преобразования между ними. Если есть лучший способ запустить команду cmp, я хотел бы знать. Причина, по которой я аргументирую аргументы, состоит в том, что у меня были проблемы с пробелами в именах файлов, когда я просто передал результат os.path.join в команде.
Спасибо за ваше предложение, я изменю его if cmpout == 0
@Gabe: Я не знаю, как ко времени команды Баша, но я считаю, что это работает намного быстрее, чем половина второго, когда я просто запустить команда.
Я сказал, что ограничение байтов не имеет большого значения, потому что, когда я изменил его только на 10Kib, он изменил общее время выполнения моего тестового прогона до 290 секунд вместо около 300 секунд. Причина, по которой я соблюдала ограничение, - это не сравнивать сравнение действительно больших файлов (например, видеофайлов 1GiB).
Update 2: Я следовал @abarnert предложение «s и изменил вызов:
cmpout = call(["cmp", '-s', '-n', '10MiB', callA, callB])
Время выполнения для моего тестового сценария в настоящее время снизилась до 270S от 300seconds. Пока недостаточно, но это начало.
Нерест подпроцесс всегда будет довольно медленным. Повторное выполнение 'cmp' в Python может быть быстрее. Другая оптимизация, которую вы можете сделать, - проверка файлов; если они разные, то вы знаете, что файлы разные. –
Зачем вы используете оболочку для этого? Вы ничего не используете, и вы, очевидно, тратите время на запуск и выход из оболочки поверх программы 'cmp'. И, конечно, это усложняет ситуацию (вы должны «указывать» аргументы). Я не знаю, если это ваша проблема, но зачем это делать в первую очередь? Просто 'call (['cmp', '-s', '-n', '10MiB', os.path.join (pathA, filenameA), os.path.join (pathB, filenameB)])'. – abarnert
Что произойдет, если у вас есть N * отдельные * файлы? Вы выполняете парные сравнения O (N^2)? – user2357112