32

約1万個のファイルがあるフォルダーをウォークスルーする必要があります。私の古いvbscriptは、これを処理するのが非常に遅いです。それ以来、RubyとPythonを使い始めたので、3つのスクリプト言語のベンチマークを作成して、どちらがこの仕事に最適かを確認しました。

共有ネットワーク上の4500ファイルのサブセットに対する以下のテストの結果は次のとおりです。

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

Vbscriptが最も遅くなるのは当然のことですが、RubyとPythonの違いを説明することはできません。Pythonのテストは最適ではありませんか?Pythonでこれを行うより速い方法はありますか?

thumbs.dbのテストはテスト用であり、実際にはさらに多くのテストを実行する必要があります。

パス上のすべてのファイルをチェックし、タイミングを乱さないように出力を生成しすぎないものが必要でした。結果は実行ごとに少し異なりますが、それほどではありません。

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

編集:印刷によって遅延が発生したと思われるため、4500ファイルすべてを印刷し、何も印刷せずにスクリプトをテストしたところ、違いは残っています。前者の場合はR:5 P:107、後者の場合はR:4.5 P:107です。

EDIT2:ここでの回答とコメントに基づいて、場合によってはフォルダーをスキップすることでより高速に実行できるPythonバージョン

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
4

2 に答える 2

9

のRuby実装はC(このドキュメントによるとDirファイル)です。ただし、Pythonに相当するものはPythonに実装されています。dir.c

PythonのパフォーマンスがCよりも低いことは驚くことではありませんが、Pythonで使用されるアプローチにより、柔軟性が少し向上します。たとえば'.svn'、ディレクトリ階層をトラバースするときに'.git'、などの名前のサブツリー全体をスキップできます。'.hg'

ほとんどの場合、Pythonの実装は十分に高速です。

更新:ファイル/サブディレクトリのスキップはトラバーサルにまったく影響しませんが、メインツリーの潜在的に大きなサブツリーをトラバースする必要がないため、ディレクトリツリーの処理にかかる全体的な時間を確実に短縮できます。もちろん、節約される時間はスキップする量に比例します。画像のフォルダのように見えるあなたの場合、多くの時間を節約することはまずありません(画像がリビジョン管理下にある場合を除いて、リビジョン管理システムが所有するサブツリーをスキップすると影響が出る可能性があります)。

追加の更新:フォルダーのスキップは、所定のdirs値を変更することで実行されます。

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.
于 2012-10-30T11:45:49.783 に答える
2

ローカルで次のディレクトリ構造を設定します。

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

これにより、1〜99レベルの深さのパスを持つ99個の​​ファイルが作成され、ディレクトリ構造のルートに4401個のファイルが作成されます。

次の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)}

次の結果が得られました。

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

os.walkを使用して次のPythonスクリプトを使用します。

#!/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))

次の結果が得られました。

    0.250478029251

ですから、ルビーのパフォーマンスはまだ良くなっているようです。これがネットワーク共有上のファイルセットでどのように機能するかを見るのは興味深いでしょう。

このスクリプトがpython3とjythonで実行され、おそらくpypyでも実行されるのを見るのもおそらく興味深いでしょう。

于 2012-10-31T06:59:41.290 に答える