очень быстро добирающийся общий размер папки

Я хочу быстро найти общий размер любой папки с помощью 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 7, python 2.6 (Сделал поиски, но большую часть времени люди привыкли очень похожий метод для моего собственного), Заранее спасибо.

51
задан AnneTheAgile 18 December 2018 в 13:16
поделиться

3 ответа

Вы находитесь в невыгодном положении.

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

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

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

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

Вы можете использовать решение Анурага или попробовать вызвать 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.

76
ответ дан 7 November 2019 в 10:04
поделиться

Если вам нужна такая же скорость, как у проводника, почему бы не использовать сценарии 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 .

22
ответ дан 7 November 2019 в 10:04
поделиться

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

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

  1. os.path.getsize неоптимален в Windows
  2. Windows кэширует размер содержимого каталога после вычисления
5
ответ дан 7 November 2019 в 10:04
поделиться
Другие вопросы по тегам:

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