30 votes

benchmarks: python a-t-il un moyen plus rapide de parcourir un dossier réseau?

J'ai besoin de marcher à travers un dossier avec près de dix mille fichiers. Mon vieux vbscript est très lent dans le traitement de ce. Depuis que j'ai commencé à l'aide de Ruby et Python depuis, j'ai fait un benchmark entre les trois langages de script pour voir ce qui serait le mieux adapté pour ce travail.

Les résultats des tests ci-dessous sur un sous-ensemble de 4500 fichiers sur un réseau partagé sont

Python: 106 seconds
Ruby: 5 seconds
Vbscript: 124 seconds

Que Vbscript serait plus lent n'était pas une surprise, mais je ne peux pas expliquer la différence entre Ruby et Python. Est mon test pour Python n'est pas optimal? Est-il un moyen plus rapide de le faire en Python?

Le test pour les pouces.db est juste pour le test, en réalité il n'y a plus de tests à faire.

J'ai besoin de quelque chose qui vérifie tous les fichiers sur le chemin et ne pas produire trop de sortie pour ne pas perturber le calendrier. Les résultats sont un peu différents, chaque terme, mais pas par beaucoup.

#python2.7.0
import os

def recurse(path):
  for (path, dirs, files) in os.walk(path):
    for file in files:
      if file.lower() == "thumbs.db":
        print (path+'/'+file)

if __name__ == '__main__':
  import timeit
  path = '//server/share/folder/'
  print(timeit.timeit('recurse("'+path+'")', setup="from __main__ import recurse", number=1))
'vbscript5.7
set oFso = CreateObject("Scripting.FileSystemObject")
const path = "\\server\share\folder"
start = Timer
myLCfilename="thumbs.db"

sub recurse(folder)
  for each file in folder.Files
    if lCase(file.name) = myLCfilename then
      wscript.echo file
    end if
  next
  for each subfolder in folder.SubFolders
    call Recurse(subfolder)
  next
end Sub

set folder = oFso.getFolder(path)
recurse(folder)
wscript.echo Timer-start
#ruby1.9.3
require 'benchmark'

def recursive(path, bench)
  bench.report(path) do
    Dir["#{path}/**/**"].each{|file| puts file if File.basename(file).downcase == "thumbs.db"}
  end
end

path = '//server/share/folder/'
Benchmark.bm {|bench| recursive(path, bench)}

EDIT: depuis que j'ai soupçonné l'impression causé un retard j'ai testé les scripts avec l'impression de tous les 4500 fichiers et d'impression aucune, la différence demeure, R:5 P:107 dans le premier cas et R:4.5 P:107 dans le dernier

EDIT2: sur la base des réponses et des commentaires ici une version de Python qui dans certains cas pourrait courir plus vite en sautant les dossiers

import os

def recurse(path):
  for (path, dirs, files) in os.walk(path):
    for file in files:
      if file.lower() == "thumbs.db":
        print (path+'/'+file)

def recurse2(path):
    for (path, dirs, files) in os.walk(path):
        for dir in dirs:
            if dir in ('comics'):
                dirs.remove(dir)
        for file in files:
            if file.lower() == "thumbs.db":
                print (path+'/'+file)


if __name__ == '__main__':
  import timeit
  path = 'f:/'
  print(timeit.timeit('recurse("'+path+'")', setup="from __main__ import recurse", number=1)) 
#6.20102692
  print(timeit.timeit('recurse2("'+path+'")', setup="from __main__ import recurse2", number=1)) 
#2.73848228
#ruby 5.7

8voto

Vinay Sajip Points 41286

Le Rubis de la mise en œuvre de Dir est en C (le fichier dir.c, selon cette documentation). Cependant, le Python équivalent est mis en œuvre en Python.

Il n'est pas surprenant que Python est moins performant que le C, mais l'approche utilisée dans Python donne un peu plus de flexibilité (par exemple, vous pouvez passer toute la sous-arborescences nommé par exemple, '.svn', '.git', '.hg' en traversant une hiérarchie de répertoire.

La plupart du temps, le Python de la mise en œuvre est assez rapide.

Mise à jour: Le saut de dossiers/sous-dossiers n'affecte pas la traversée de taux , mais l'ensemble de la durée de traitement d'un répertoire de l'arborescence pourrait certainement être réduite en raison de vous éviter d'avoir à traverser potentiellement lourdes de sous-arbres de l'arbre principal. Le gain de temps est bien sûr proportionnel à la quantité que vous ignorer. Dans votre cas, qui ressemble à des dossiers d'images, il est peu probable que vous permettrait d'économiser beaucoup de temps (sauf si les images étaient sous contrôle de version, en sautant des sous-arborescences détenue par la révision du système de contrôle pourrait avoir un certain impact).

Mise à jour supplémentaire: Sauter des dossiers se fait en changeant l' dirs de la valeur à la place:

for root, dirs, files in os.walk(path):
    for skip in ('.hg', '.git', '.svn', '.bzr'):
        if skip in dirs:
            dirs.remove(skip)
        # Now process other stuff at this level, i.e.
        # in directory "root". The skipped folders
        # won't be recursed into.

2voto

Warren T. Points 142

Je le répertoire d'installation de la structure avec les suivants au niveau local:

for i in $(seq 1 4500); do
    if [[ $i -lt 100 ]]; then
        dir="$(for j in $(seq 1 $i); do echo -n $i/;done)"
        mkdir -p "$dir"
        touch ${dir}$i
    else
        touch $i
    fi
done

Cela crée 99 fichiers avec des chemins qui sont de 1 à 99 niveaux de profondeur et 4401 fichiers dans la racine de la structure de répertoire.

J'ai utilisé le suivant script ruby:

#!/usr/bin/env ruby
require 'benchmark'

def recursive(path, bench)
  bench.report(path) do
    Dir["#{path}/**/**"]
  end
end

path = 'files'
Benchmark.bm {|bench| recursive(path, bench)}

J'ai obtenu le résultat suivant:

           user     system      total        real
    files/  0.030000   0.090000   0.120000 (  0.108562)

J'ai utiliser le script python ci à l'aide de l'os.à pied:

#!/usr/bin/env python

import os
import timeit

def path_recurse(path):
    for (path, dirs, files) in os.walk(path):
      for folder in dirs:
          yield '{}/{}'.format(path, folder)
      for filename in files:
          yield '{}/{}'.format(path, filename)

if __name__ == '__main__':
    path = 'files'
    print(timeit.timeit('[i for i in path_recurse("'+path+'")]', setup="from __main__ import path_recurse", number=1))

J'ai obtenu le résultat suivant:

    0.250478029251

Ainsi, il ressemble à ruby est encore mieux. Il serait intéressant de voir comment celui-ci fonctionne sur votre fileset sur le partage réseau.

Il serait sans doute également être intéressant de voir ce script s'exécute sur python3 et avec jython et peut-être même avec pypy.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X