【问题标题】:A Faster way of Directory walking instead of os.listdir?一种更快的目录遍历方式而不是 os.listdir?
【发布时间】:2011-03-10 20:56:58
【问题描述】:

我正在尝试提高 elfinder 的性能,这是一个基于 ajax 的文件管理器 (elRTE.ru)。

它在递归中使用 os.listdir 以递归方式遍历所有目录并影响性能(例如列出包含 3000 + 个文件的目录需要 7 秒)..

我正在努力提高它的性能,这是它的步行功能:

        for d in os.listdir(path):
            pd = os.path.join(path, d)
            if os.path.isdir(pd) and not os.path.islink(pd) and self.__isAccepted(d):
                tree['dirs'].append(self.__tree(pd))

我的问题是:

  1. 如果我更改 os.walk 而不是 os.listdir ,会提高性能吗?
  2. 使用 dircache.listdir() 怎么样?在初始请求时缓存整个目录/子目录内容并返回缓存结果,如果没有上传新文件或文件没有更改?
  3. 还有其他更快的目录遍历方法吗?
  4. 任何其他用python快速编写的服务器端文件浏览器(但我更喜欢让这个更快)?

【问题讨论】:

  • 您将这些数据用于什么目的?如果你有能力懒惰地进行递归(仅在需要内容时调用os.listdir(),而不是在找到新目录时自动调用),那么你可以分摊大量请求的成本。这就是大多数文件管理器在实践中的工作方式。
  • 此数据用于基于 ajax 的文件管理器,名为 elfinder,来自 elrte.ru。它很好,但由于我粘贴的功能,问题太慢了。你的看起来很实用,我会把它改成寻找每个目录而不是整个递归。
  • os.walk() 不会比您的步行功能更快,因为它们的功能基本相同。 os.walk()使用os.listdir()os.pathisdir()等。查看os.walk()的代码就知道了!
  • 2017 年更新:现在这里很多信息已经过时了。即os.walk不再使用listdir,现在它使用更快的scandir
  • @wim 它开始使用哪个版本?在 2.7 中不可用吗?

标签: python file-io directory performance


【解决方案1】:

我一直在寻找一种解决方案来列出文件夹中的图像数量,但 Colab 在运行几分钟后出现os.listdir() 超时。快速的方法是使用 scandir 创建迭代器,然后将文件名填充到单独的列表中。在几秒钟内工作。

Answer 和其他人差不多,但是把代码放在了替代品上,说 Colab 处理大文件有问题。

img_files = data_folder 
obj = os.scandir(img_files)
 
# List all files and directories
# in the specified path
print("Files and Directories in '% s':" % img_files)

img_files = []
for entry in obj :
    if entry.is_dir() or entry.is_file():
        img_files.append(entry.name)

len(img_files)

【讨论】:

  • os.lisdir() 正在阻塞操作。它会阻塞线程。在 google colab 上运行它是一个非常糟糕的主意。最好的方法是在单独的线程中运行它。 AIOFiles 也不支持列出目录。 os.scandir 也是阻塞操作。看这里:stackoverflow.com/questions/23894515/…
  • 哇,谢谢@PhyoArkarLwin
【解决方案2】:

我知道这是一个旧线程,但我现在不得不做出同样的决定,所以发布结果。随着对 Python 3.5+ 的所有更新,与 os.listdir()os.scandir() 相比,os.walk() 是最快的方法。

我在两个主文件夹中收集文件,每个主文件夹中大约有 30 个文件夹。

files_list = [os.path.join(dir_, root, f)
              for dir_ in folder_list
              for root, dirs, files in os.walk(dir_)
              for f in files
              if (os.path.basename(f).startswith(prefix) and f.endswith(ext))]

我的测试结果:
os.scandir():10,949 个文件,35.579052 秒
os.listdir():10,949 个文件,35.197001 秒
os.walk():10,949 个文件,01.544174 秒

【讨论】:

    【解决方案3】:

    您查看过scandir(以前的betterwalk)吗?自己没试过,不过有discussion about it hereanother one here。它声称通过避免对 os.stat() 的冗余调用,在 MacOSX/Linux 上加速了 3~10 倍,在 Windows 上加速了 7~50 倍。它现在也包含在 Python 3.5 的标准库中。

    Python 的内置 os.walk() 比它需要的慢得多 是,因为 -- 除了在每个目录上调用 listdir() -- 它在每个文件上调用 stat() 以确定文件名是否为 目录与否。但是 Windows 上的 FindFirstFile / FindNextFile 和 Linux/OS X 上的 readdir 已经告诉你返回的文件是否是 目录与否,因此不需要进一步的 stat 系统调用。在 总之,可以将系统调用的次数从大约 2N 减少到 N, 其中 N 是树中文件和目录的总数。

    实际上,删除所有这些额外的系统调用会使 os.walk() 在 Windows 上大约是 7-50 倍,在 Windows 上大约是 3-10 倍 Linux 和 Mac OS X

    来自project's readme

    【讨论】:

    • 我去试试,很好。
    • scandir 包含在 Python 3.5 的 os 模块中。
    • 注意scandir 现在包含在 Python 中,it's actually used by os.walk。因此,如果您想尝试 scandir 作为更快的替代品,您可以忘记它!
    【解决方案4】:

    您正在寻找fsdir。它是用 C 语言编写的,可以与 python 一起使用。它比使用标准 python 库遍历树要快很多

    【讨论】:

      【解决方案5】:

      用 bash 来做怎么样?

      import subprocess
      command = 'ls .... or something else'
      subprocess.Popen([command] ,shell=True) 
      

      在我的情况下,它正在更改数千个文件的权限,这效果要好得多。

      【讨论】:

      • 解析命令行不是pythonic和hacky,我避免在通过python可用时调用命令行。而且它不可移植。在我的情况下,如果我需要 ssh 访问目标,我使用 paramiko,从不使用 ssh 客户端
      • 它不是便携式的 - 同意。我从来没有提到任何关于 ssh 的事情。如果调用 cammandlines 不是 pythonic 和 hacky,为什么 subprocess 模块包含在 python 中?无论如何,我建议使用原生 bash,它比任何 python 都更快地遍历目录树。
      • >native bash,它的遍历速度要快得多我想看看你的声明的性能基准。 >not pythonic and hacky' 为什么子进程模块包含在 python 中我相信你不熟悉 pythonic 的含义。
      • 我实际上只是在考虑将这个想法用于常规目录列表......而不是步行树。 find 可能是行走树木的快速解决方案。确实它不是 pythonic,但如果它更快,有时我们必须凑合。每当我需要 md5 某些东西时,我都会使用 subprocess,因为根据相同的原理,它比在原生 python 中运行要快得多。
      【解决方案6】:

      我只是想弄清楚如何在一个较大的文件系统上加速 os.walk(350,000 个文件分布在大约 50,000 个目录中)。我在使用 ext3 文件系统的 linux 机器上。我发现有一种方法可以加快我的情况。

      具体来说,使用自顶向下遍历,任何时候 os.walk 返回多个目录的列表,我使用 os.stat 获取每个目录的 inode 编号,并按 inode 编号对目录列表进行排序。这使得 walk 主要以 inode 顺序访问子目录,从而减少了磁盘查找。

      对于我的用例,它将我的完整目录步行时间从 18 分钟缩短到 13 分钟...

      【讨论】:

      • 非常感谢,这是一个非常有趣的想法!我会测试一下!
      • 其实这是最快的方式。谢谢,但已经选择了答案。
      • 好把戏 garlon4,没有你的暗示很难这样想。 @V3ss0n,我认为您仍然可以随时更改您选择的答案,如果您愿意的话。
      • 如果您的性能是关键,那么您不需要可移植性,因为您在 Linux 中,并且您的列表是“静态的”。您不会经常获得新文件。然后我会考虑使用像'find'这样的本机命令运行一个外部进程,或者甚至执行一些c脚本,比如:stackoverflow.com/questions/4204666/…,并将它们输出到一个文件中,然后从python读取这个文件。在给定的场景中,这是一个更快的解决方案。
      • 在我的用例中,我明确不知道文件和目录是否发生了变化(这就是我要遍历它们的原因)。
      【解决方案7】:

      os.path.walk可能提高你的表现,原因有二:

      1)如果你能在你走完所有东西之前停下来,那么它确实会比listdir更快,虽然只有在处理大树时才会注意到

      2) 如果您要列出 HUGE 目录,那么让 listdir 返回的列表可能会很昂贵。(不正确,请参阅下面 alex 的评论)

      但是,由于调用 visit 函数并执行所有额外的参数打包和解包可能会产生额外的开销,它可能不会产生影响,实际上可能会更慢。

      (真正回答这个问题的唯一方法就是自己测试一下——应该只需要几分钟)

      【讨论】:

      • 相对较新的 os.walk 和老旧的 os.path.walk 都必须完全读取每个目录,因为它们必须将其中的名称显示为一个或两个列表(os.path .walk 在文档中被指定为使用 os.listdir,但你认为 os.walk 是如何做到的?-)。所以(2)并不真正适用。
      • 好吧,呸。我仍然坚持我的劝告,即应该测试这些东西.. :-)
      • 所以这意味着没有性能差异。但至少 os.walk 不需要这样做: os.path.isdir(pd) 而不是 os.path.islink(pd)分别给出文件/目录对吗? Alrtie 我会测试它并告诉你!
      【解决方案8】:

      您应该直接在您感兴趣的机器(操作系统、文件系统及其缓存等)上进行测量——在特定且完全不同的机器/操作系统/FS 上,os.walk 是否比os.listdir 快很少告诉你你的的性能。

      不确定cachedir.listdir 是什么意思——没有该名称的标准库模块/函数。 listdir 已经一次性读取了所有目录(因为它必须对结果进行排序),os.walk 也是如此(因为它必须将子目录与文件分开)。如果根据您的平台,您可以快速获得有关文件/目录更改的通知,那么可能值得构建一次树并随着更改通知的到来逐步编辑它......但这取决于更改的相对频率vs 请求,这又完全取决于您的具体应用环境。

      【讨论】:

      • 抱歉,已修复,我的意思是 dircache.listdir
      • @V3ss0n, dircache 从来没有运行得特别好,最终自 Python 2.6 起被弃用,自 Python 3.0 起被删除——我绝对不建议这样做。
      • 好的,那就报废吧:)谢谢!
      【解决方案9】:

      按顺序:

      • 我怀疑你会在os.walkos.listdir 之间看到很多加速,因为两者都依赖于底层文件系统。事实上,我怀疑底层文件系统会对操作速度产生很大影响。

      • 任何缓存操作都将比访问文件系统快得多(至少对于第二次和后续检查而言)。

      • 您总是可以编写一些实用程序(或调用 shell 命令)来生成 Python 之外的目录列表,并通过 subprocess 模块调用它。但这有点复杂,只有当缓存对您不起作用时,我才会转向该解决方案。

      • 如果您在 the Cheeseshop 上没有找到文件浏览器,您可能找不到。

      【讨论】:

      • 我必须比较 listdir 与 shell 命令的性能。我怀疑他们会有区别..
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-26
      • 1970-01-01
      • 1970-01-01
      • 2018-12-29
      • 2021-04-01
      • 2013-10-31
      相关资源
      最近更新 更多