Я ищу быстрый способ вычислить размер папки в Python в Windows. Это - то, что я имею до сих пор:
def get_dir_size(path):
total_size = 0
if platform.system() == 'Windows':
try:
items = win32file.FindFilesW(path + '\\*')
except Exception, err:
return 0
# Add the size or perform recursion on folders.
for item in items:
attr = item[0]
name = item[-2]
size = item[5]
if (attr & win32con.FILE_ATTRIBUTE_DIRECTORY) and \
not (attr & win32con.FILE_ATTRIBUTE_SYSTEM): # skip system dirs
if name not in DIR_EXCLUDES:
total_size += get_dir_size("%s\\%s" % (path, name))
total_size += size
return total_size
Это не достаточно хорошо, когда размер папки по 100G. Какие-либо идеи, как улучшить его?
На быстрой машине (2 ГГц + - 5G RAM), потребовалось 72 секунды для осмотра через 422 ГБ в 226 001 файле и 12 043 папках. Требуется 40 секунд с помощью опции свойств проводника.
Я знаю, что являюсь немного жадным, но я надеюсь на лучшее решение.
Laurent Luce
Быстрое профилирование вашего кода говорит о том, что более 90% времени занимает только вызов FindFilesW()
. Это означает, что любые улучшения путем подстройки кода на Python будут незначительными.
Крошечные подстройки (если вы будете придерживаться FindFilesW) могут включать в себя обеспечение того, что DIR_EXCLUDES является набором вместо списка, избегая повторных просмотров на других модулях, и индексирование в item[] лениво, а также перемещение проверки sys.platform снаружи. Это включает в себя эти и другие изменения, , но не даст более 1-2% ускорения.
DIR_EXCLUDES = set(['.', '..'])
MASK = win32con.FILE_ATTRIBUTE_DIRECTORY | win32con.FILE_ATTRIBUTE_SYSTEM
REQUIRED = win32con.FILE_ATTRIBUTE_DIRECTORY
FindFilesW = win32file.FindFilesW
def get_dir_size(path):
total_size = 0
try:
items = FindFilesW(path + r'\*')
except pywintypes.error, ex:
return total_size
for item in items:
total_size += item[5]
if (item[0] & MASK == REQUIRED):
name = item[8]
if name not in DIR_EXCLUDES:
total_size += get_dir_size(path + '\\' + name)
return total_size
Единственное значительное ускорение будет получено при использовании другого API, или другой техники. Вы упоминали об этом в комментарии, делая это в фоновом режиме, поэтому вы можете структурировать его так, чтобы он выполнял инкрементальное обновление, используя один из пакетов для мониторинга изменений в папках. Возможно FindFirstChangeNotification API или что-то в этом роде. Вы можете настроить мониторинг всего дерева, или, в зависимости от того, как эта рутина работает (я ее не использовал), возможно, лучше зарегистрировать несколько запросов на разных подмножествах полного дерева, если это уменьшает объем поиска (при уведомлении), чтобы выяснить, что на самом деле изменилось и каков его размер сейчас.
Edit: Я спросил в комментарии, принимаете ли вы во внимание кэширование метаданных тяжелой файловой системы, которое делает Windows XP и более поздние версии. Я просто проверил производительность вашего кода (и моего) против самой Windows, выбрав все элементы в моей папке C:\ и нажав Alt-Enter, чтобы открыть окно свойств. После того, как я сделал это один раз (используя ваш код) и получил 40 с истекшим временем, я получил 20 с истекшим временем от обоих методов . Другими словами, ваш код на самом деле работает так же быстро, как и сама Windows, по крайней мере, на моей машине.
. На данный момент у меня нет коробки Windows для тестирования, но в документации сказано, что
win32file.FindFilesIterator
"похож на win32file.FindFiles
, но избегайте создания списка для огромных каталогов". Помогает ли это?
Это верхушка дерева каталогов. Как говорили другие, я не уверен, что вы сможете ускорить его... не так, холодные данные w/o. А это значит...
Если вы можете кэшировать данные, как-то (не уверен, что это подразумевается на самом деле), то вы можете ускорить процесс (я думаю... как всегда, измеряйте, измеряйте, измеряйте).
Я не думаю, что я должен говорить вам, как делать кэширование, я думаю, вы кажетесь знающим человеком. И я бы все равно не знал без манжеты для Windows. ;-)
Это меня просто бесит:
try:
items = win32file.FindFilesW(path + '\\*')
except Exception, err:
return 0
Обработка исключений может добавить значительное время к вашему алгоритму. Если вы можете указать путь по-другому, так, как вы всегда знаете, что это безопасно, и таким образом предотвратить необходимость перехвата исключений (например, сначала проверить, является ли данный путь папкой, прежде чем найти файлы в этой папке), то вы можете обнаружить значительное ускорение.
Не нужно использовать рекурсивный алгоритм, если вы используете os.walk. Пожалуйста, проверьте этот вопрос .
Вам нужно время на оба подхода, но это должно быть намного быстрее:
import os
def get_dir_size(root):
size = 0
for path, dirs, files in os.walk(root):
for f in files:
size += os.path.getsize( os.path.join( path, f ) )
return size