очень быстро получить общий размер папки - PullRequest
49 голосов
/ 21 марта 2010

Я хочу быстро найти общий размер любой папки, использующей python.

import os
from os.path import join, getsize, isfile, isdir, splitext
def GetFolderSize(path):
    TotalSize = 0
    for item in os.walk(path):
        for file in item[2]:
            try:
                TotalSize = TotalSize + getsize(join(item[0], file))
            except:
                print("error with file:  " + join(item[0], file))
    return TotalSize

print(float(GetFolderSize("C:\\")) /1024 /1024 /1024)

Это простой скрипт, который я написал, чтобы получить общий размер папки, он занял около 60 секунд (+ -5 секунд). Используя многопроцессорность, я сократил до 23 секунд на четырехъядерном компьютере.

Использование файлового менеджера Windows занимает всего ~ 3 секунды (щелкните правой кнопкой мыши -> Свойства, чтобы убедиться в этом). Так есть ли более быстрый способ найти общий размер папки, близкий к скорости, с которой Windows может это сделать?

Windows 7, python 2.6 (делал поиски, но большую часть времени люди использовали метод, очень похожий на мой) Заранее спасибо.

Ответы [ 3 ]

74 голосов
/ 21 марта 2010

Вы в невыгодном положении.

Проводник Windows почти наверняка использует FindFirstFile / FindNextFile для обхода структуры каталогов и для сбора информации о размере (через lpFindFileData) в один проход, делая, по сути, один системный вызов на файл.

В этом случае, к сожалению, Python не ваш друг. Таким образом,

  1. os.walk первые звонки os.listdir (которые внутренне звонят FindFirstFile / FindNextFile)
    • любые дополнительные системные вызовы, сделанные с этого момента, могут сделать вас медленнее , чем Windows Explorer
  2. os.walk затем вызывает isdir для каждого файла, возвращаемого os.listdir (который внутренне вызывает GetFileAttributesEx - или, до Win2k, GetFileAttributes + FindFirstFile комбо), чтобы определить, рекурсировать или нет
  3. os.walk и os.listdir будут выполнять дополнительное выделение памяти , операции со строками и массивами и т. Д., Чтобы заполнить их возвращаемое значение
  4. вы затем вызываете getsize для каждого файла, возвращаемого os.walk (который снова вызывает GetFileAttributesEx)

Это в 3 раза больше системных вызовов на файл, чем в Windows Explorer, плюс выделение памяти и накладные расходы на манипуляции.

Вы можете использовать решение Anurag или попытаться вызвать FindFirstFile / FindNextFile напрямую и рекурсивно (что должно быть сопоставимо с производительностью cygwin или другого порта win32 du -s some_directory.)

Обратитесь к os.py для реализации os.walk, posixmodule.c для реализации listdir и win32_stat (вызывается как isdir и getsize.)

Обратите внимание, что Python os.walk неоптимален на всех платформах (Windows и * nices), вплоть до Python3.1. И в Windows, и в * nices os.walk можно было пройти обход за один проход без вызова isdir, так как FindFirst / FindNext (Windows) и opendir / readdir (* nix) уже возвращают тип файла через lpFindFileData->dwFileAttributes (Windows) и dirent::d_type (* nix).

Возможно, нелогично, в большинстве современных конфигураций (например, Win7 и NTFS и даже в некоторых реализациях SMB) GetFileAttributesEx в в два раза медленнее, чем FindFirstFile в одном файле (возможно, даже медленнее, чем итерация по каталог с FindNextFile.)

Обновление: Python 3.5 включает новую функцию PEP 471 os.scandir(), которая решает эту проблему, возвращая атрибуты файла вместе с именем файла. Эта новая функция используется для ускорения встроенного os.walk() (как в Windows, так и в Linux). Вы можете использовать модуль scandir на PyPI , чтобы получить такое поведение для старых версий Python, включая 2.x.

21 голосов
/ 21 марта 2010

Если вы хотите использовать ту же скорость, что и проводник, почему бы не использовать сценарии Windows для доступа к той же функциональности с помощью pythoncom, например

import win32com.client as com

folderPath = r"D:\Software\Downloads"
fso = com.Dispatch("Scripting.FileSystemObject")
folder = fso.GetFolder(folderPath)
MB = 1024 * 1024.0
print("%.2f MB" % (folder.Size / MB))

Он будет работать так же, как проводник, вы можете узнать больше о среде выполнения сценариев на http://msdn.microsoft.com/en-us/library/bstcxhf7(VS.85).aspx.

5 голосов
/ 21 марта 2010

Я сравнил производительность кода Python с деревом каталогов 15 КБ, содержащим 190 КБ файлов, и сравнил его с командой du(1), которая предположительно работает так же быстро, как и ОС. Код Python занял 3,3 секунды по сравнению с du, который занял 0,8 секунды. Это было в Linux.

Я не уверен, что из кода Python можно выжать слишком много. Также обратите внимание, что первый запуск du занял 45 секунд, что было очевидно до того, как соответствующие i-узлы были в кеше блоков; поэтому эта производительность сильно зависит от того, насколько хорошо система управляет своим хранилищем. Меня не удивит, если или то и другое:

  1. os.path.getsize является неоптимальным в Windows
  2. Размер содержимого каталога Windows кэшируется после расчета
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...