web-dev-qa-db-fra.com

obtenir très rapidement la taille totale du dossier

Je veux trouver rapidement la taille totale de n'importe quel dossier en utilisant 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)

C'est le script simple que j'ai écrit pour obtenir la taille totale du dossier, cela a pris environ 60 secondes (+ -5 secondes). En utilisant le multitraitement, je l'ai réduit à 23 secondes sur une machine quadricœur.

En utilisant l'explorateur de fichiers Windows, cela ne prend que ~ 3 secondes (clic droit -> propriétés pour voir par vous-même). Existe-t-il un moyen plus rapide de trouver la taille totale d'un dossier proche de la vitesse à laquelle Windows peut le faire?

Windows 7, python 2.6 (A fait des recherches mais la plupart du temps les gens utilisaient une méthode très similaire à la mienne) Merci d'avance.

49
user202459

Vous êtes désavantagé.

L'Explorateur Windows utilise presque certainement FindFirstFile / FindNextFile pour parcourir la structure du répertoire et collecter la taille (via lpFindFileData) en un seul passage, ce qui constitue essentiellement un seul appel système par fichier.

Python n'est malheureusement pas votre ami dans ce cas. Donc,

  1. os.walk premiers appels os.listdir (qui appelle en interne FindFirstFile/FindNextFile)
    • tout appel système supplémentaire effectué à partir de ce point ne peut que vous ralentir par rapport à l'Explorateur Windows
  2. os.walk appelle ensuite isdir pour chaque fichier renvoyé par os.listdir (Qui appelle en interne GetFileAttributesEx - ou, avant Win2k, un combo GetFileAttributes + FindFirstFile) pour déterminer à nouveau s'il faut récursif ou non
  3. os.walk Et os.listdir Effectueront une allocation de mémoire supplémentaire , des opérations sur les chaînes et les tableaux, etc. pour remplir leur valeur de retour
  4. vous puis appelez getsize pour chaque fichier renvoyé par os.walk (qui appelle à nouveau - GetFileAttributesEx )

Cela représente 3 fois plus d'appels système par fichier que l'Explorateur Windows, plus l'allocation de mémoire et la surcharge de manipulation.

Vous pouvez soit utiliser la solution d'Anurag, soit essayer d'appeler FindFirstFile/FindNextFile directement et récursivement (ce qui devrait être comparable aux performances d'un cygwin ou autre port win32du -s some_directory.)

Reportez-vous à os.py pour l'implémentation de os.walk, posixmodule.c pour l'implémentation de listdir et win32_stat (Invoqué à la fois par isdir et getsize.)

Notez que Python os.walk Est sous-optimal sur toutes les plateformes (Windows et * nices), jusqu'à Python3.1 inclus. Sous Windows et * nices, os.walk Pouvait réaliser la traversée en une seule passe sans appeler isdir puisque FindFirst/FindNext (Windows) et opendir/readdir (* nix) renvoie déjà le type de fichier via lpFindFileData->dwFileAttributes (Windows) et dirent::d_type (* nix).

Peut-être contre-intuitivement, sur la plupart des configurations modernes (par exemple Win7 et NTFS, et même certaines SMB) GetFileAttributesEx est deux fois aussi lent comme FindFirstFile d'un seul fichier (peut-être même plus lent que l'itération sur un répertoire avec FindNextFile.)

Mise à jour: Python 3.5 inclut le nouveau PEP 471os.scandir() fonction qui résout ce problème en renvoyant des attributs de fichier avec le nom de fichier. Cette nouvelle fonction est utilisée pour accélérer le os.walk() intégré (sous Windows et Linux). Vous pouvez utiliser le module scandir sur PyPI pour obtenir ce comportement pour les anciennes versions Python, y compris 2.x.

74
vladr

Si vous voulez la même vitesse qu'Explorer, pourquoi ne pas utiliser les scripts Windows pour accéder aux mêmes fonctionnalités en utilisant pythoncom, par exemple.

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))

Il fonctionnera de la même manière que l'Explorateur, vous pouvez en savoir plus sur l'exécution du script sur http://msdn.Microsoft.com/en-us/library/bstcxhf7 (VS.85) .aspx .

21
Anurag Uniyal

J'ai comparé les performances du code Python à une arborescence de répertoires de 15k contenant 190k fichiers et je l'ai comparée à la commande du(1) qui va probablement aussi vite que le système d'exploitation. Le = Python a pris 3,3 secondes par rapport à du qui a pris 0,8 secondes. C'était sous Linux.

Je ne suis pas sûr qu'il y ait beaucoup à tirer du code Python. Notez également que la première exécution de du a pris 45 secondes, ce qui était évidemment avant que les nœuds i pertinents ne se trouvent dans le cache de bloc ; par conséquent, ces performances dépendent fortement de la façon dont le système gère son magasin. Cela ne me surprendrait pas si l'un ou les deux:

  1. os.path.getsize n'est pas optimal sous Windows
  2. Windows met en cache la taille du contenu du répertoire une fois calculée
5
msw