Подтвердить что ты не робот

Тесты: у python есть более быстрый способ перехода в сетевую папку?

Мне нужно пройти через папку с примерно десяти тысячами файлов. Мой старый vbscript очень медленно справляется с этим. Поскольку я начал использовать Ruby и Python с тех пор, я сделал тест между тремя языками сценариев, чтобы увидеть, какой из них лучше всего подходит для этой работы.

Результаты тестов ниже в подмножестве из 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)}

EDIT: поскольку я подозревал, что печать вызвала задержку, я протестировал скрипты с печатью всех 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
4b9b3361

Ответ 1

Реализация Ruby для Dir находится в C (файл dir.c, в соответствии с этой документацией). Однако эквивалент Python реализован в Python.

Не удивительно, что 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.

Ответ 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

Это создает 99 файлов с путями длиной 1-299 и 4401 файлами в корне структуры каталогов.

Я использовал следующий ruby ​​ script:

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

Я использую следующий python script с помощью os.walk:

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

Итак, похоже, что Ruby все еще работает лучше. Было бы интересно посмотреть, как это выполняется на вашем наборе файлов в общем сетевом ресурсе.

Было бы также интересно увидеть, что этот script работает на python3 и с jython и, возможно, даже с pypy.