быстрое вычисление размера папки в Python в Windows

Я ищу быстрый способ вычислить размер папки в 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

6
задан Laurent Luce 31 December 2009 в 23:30
поделиться

5 ответов

Быстрое профилирование вашего кода говорит о том, что более 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, по крайней мере, на моей машине.

.
6
ответ дан 9 December 2019 в 22:35
поделиться

На данный момент у меня нет коробки Windows для тестирования, но в документации сказано, что win32file.FindFilesIterator "похож на win32file.FindFiles, но избегайте создания списка для огромных каталогов". Помогает ли это?

.
1
ответ дан 9 December 2019 в 22:35
поделиться

Это верхушка дерева каталогов. Как говорили другие, я не уверен, что вы сможете ускорить его... не так, холодные данные w/o. А это значит...

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

Я не думаю, что я должен говорить вам, как делать кэширование, я думаю, вы кажетесь знающим человеком. И я бы все равно не знал без манжеты для Windows. ;-)

1
ответ дан 9 December 2019 в 22:35
поделиться

Это меня просто бесит:

try:
  items = win32file.FindFilesW(path + '\\*')
except Exception, err:
  return 0

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

1
ответ дан 9 December 2019 в 22:35
поделиться

Не нужно использовать рекурсивный алгоритм, если вы используете 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
2
ответ дан 9 December 2019 в 22:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: