有没有比os.listdir更快的目录遍历方法?
我正在尝试提高 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))
我有几个问题:
- 如果我把 os.listdir 换成 os.walk,性能会提高吗?
- 使用 dircache.listdir() 呢?可以在初始请求时缓存整个目录/子目录的内容,然后返回缓存的结果,如果没有新文件上传或者文件没有变化的话?
- 有没有其他更快的目录遍历方法?
- 有没有其他用 Python 写的快速服务器端文件浏览器(不过我更希望能让这个变快)?
10 个回答
你应该直接在你感兴趣的机器上(比如操作系统、文件系统和缓存等)进行测量。比如说,os.walk
是否比 os.listdir
在某台完全不同的机器、操作系统或文件系统上更快,这对你自己机器的性能了解帮助不大。
我不太明白你说的 cachedir.listdir
是什么意思,因为没有这个名字的标准库模块或函数。listdir
本身就会一次性读取整个目录(因为它需要对结果进行排序),os.walk
也是这样(因为它需要把子目录和文件分开)。如果根据你的平台,你有一种快速的方式来接收文件或目录变化的通知,那么可能值得先建立一次树形结构,然后随着变化通知的到来逐步进行编辑……但这又取决于变化的频率和请求的频率,这些都是完全依赖于你具体应用的情况。
你有没有看看 scandir(之前叫 betterwalk)?我自己没试过,但这里有一个关于它的 讨论,还有 另一个讨论。它声称在MacOSX/Linux上速度提升了3到10倍,在Windows上提升了7到50倍,因为它避免了多余的os.stat()调用。现在它已经包含在Python 3.5的标准库中了。
Python自带的os.walk()速度比实际需要的慢很多,因为它除了在每个目录上调用listdir()外,还会对每个文件调用stat()来判断文件名是否是目录。但在Windows上,FindFirstFile / FindNextFile和在Linux/OS X上的readdir已经能告诉你返回的文件是否是目录,所以不需要再额外调用stat。简单来说,你可以把系统调用的次数从大约2N减少到N,其中N是树中所有文件和目录的总数。
实际上,去掉这些多余的系统调用让os.walk()在Windows上快了7到50倍,在Linux和Mac OS X上快了3到10倍。
来自 项目的自述文件。
我正在尝试找出如何加快在一个比较大的文件系统上使用os.walk的速度(这个文件系统里有350,000个文件,分布在大约50,000个目录中)。我在一台使用ext3文件系统的Linux电脑上。然后我发现有一种方法可以针对我的情况加快速度。
具体来说,我使用的是从上到下的遍历方式,每当os.walk返回多个目录时,我就用os.stat来获取每个目录的inode号码,并根据inode号码对目录列表进行排序。这样,遍历时大部分会按照inode的顺序访问子目录,这样可以减少磁盘的寻址次数。
对于我的使用情况,这样一来,我的完整目录遍历时间从18分钟缩短到了13分钟……