2

尝试#2:

人们似乎并不理解我想要做什么。让我看看我是否可以更清楚地说明它:

1)读取文件列表比遍历目录要快得多。

2)所以让我们有一个遍历目录并将结果列表写入文件的函数。现在,将来,如果我们想获取该目录中的所有文件,我们可以只读取该文件而不是遍历目录。我将此文件称为索引。

3) 显然,随着文件系统的变化,索引文件会失去同步。为了克服这个问题,我们有一个单独的程序连接到操作系统中,以监控文件系统的变化。它将这些更改写入一个称为监控日志的文件。在我们读取特定目录的索引文件后,我们立即使用监控日志将各种更改应用于索引,以便它反映目录的当前状态。

因为读取文件比遍历目录要便宜得多,所以这应该比在第一次调用之后遍历所有调用要快得多。

原帖:

我想要一个函数,它将递归地获取任何给定目录中的所有文件并根据各种参数过滤它们。而且我希望它快——比如,比简单地走目录快一个数量级。我更喜欢用 Python 来做。跨平台更可取,但 Windows 最重要。

这是我关于如何解决这个问题的想法:

我有一个名为 all_files 的函数:

def all_files(dir_path, ...parms...):
    ...

我第一次调用这个函数时,它将使用 os.walk 构建所有文件的列表,以及有关文件的信息,例如它们是否被隐藏、符号链接等。我将把这些数据写入文件在目录中称为“.index”。在随后对 all_files 的调用中,将检测到 .index 文件,我将读取该文件而不是遍历目录。

这会导致在添加和删除文件时索引不同步的问题。为此,我将有第二个程序在启动时运行,检测对整个文件系统的所有更改,并将它们写入一个名为“mod_log.txt”的文件。它通过 Windows 信号检测变化,就像这里描述的方法一样。该文件每行包含一个事件,每个事件由受影响的路径、事件类型(创建、删除等)和时间戳组成。.index 文件也将有一个时间戳,以及它最后一次更新的时间。在我读取 all_files 中的 .index 文件后,我将跟踪 mod_log.txt 并查找在 .index 文件中的时间戳之后发生的任何事件。它将获取这些最近的事件,找到适用于当前目录的任何事件,并相应地更新 .index。

最后,我将所有文件的列表,根据各种参数过滤,并返回结果。

你觉得我的做法怎么样?有一个更好的方法吗?

编辑:

检查此代码。通过递归遍历读取缓存列表,我看到了显着的加速。

import os
from os.path import join, exists
import cProfile, pstats

dir_name = "temp_dir"
index_path = ".index"

def create_test_files():
    os.mkdir(dir_name)
    index_file = open(index_path, 'w')
    for i in range(10):
        print "creating dir: ", i
        sub_dir = join(dir_name, str(i))
        os.mkdir(sub_dir)
        for i in range(100):
            file_path = join(sub_dir, str(i))
            open(file_path, 'w').close() 
            index_file.write(file_path + "\n")
    index_file.close()
#

#  0.238 seconds
def test_walk():            
    for info in os.walk("temp_dir"):
        pass

#  0.001 seconds
def test_read():
    open(index_path).readlines()

if not exists("temp_dir"):
    create_test_files()

def profile(s):
    cProfile.run(s, 'profile_results.txt')
    p = pstats.Stats('profile_results.txt')
    p.strip_dirs().sort_stats('cumulative').print_stats(10)

profile("test_walk()")
profile("test_read()")
4

6 回答 6

7

不要试图复制文件系统已经完成的工作。你不会比现在做得更好。

你的方案在很多方面都有缺陷,它不会给你带来数量级的改进。

缺陷和潜在问题:

您将始终使用文件系统的快照。你永远不会肯定地知道它与现实没有明显脱节。如果这在您的应用程序的工作参数范围内,那就不用担心了。

文件系统监控程序仍然需要递归遍历文件系统,所以工作还在进行中。

为了提高缓存的准确性,您必须提高文件系统监视器的运行频率。它运行得越多,您节省的实际时间就越少。

您的客户端应用程序可能无法在文件系统监控程序更新索引文件时读取它,因此您将在客户端等待索引可读时浪费时间。

我可以继续。

事实上,如果您不关心使用可能与现实非常脱节的文件系统快照,我认为您最好将索引保存在内存中并从应用程序本身进行更新。这将清除任何否则会出现的文件争用问题。

于 2010-01-13T20:20:40.457 回答
3

最佳答案来自Michał Marczyk,位于第一个问题的评论列表底部。他指出,我所描述的非常接近 UNIX 定位程序。我在这里找到了一个 Windows 版本:http: //locate32.net/index.php。它解决了我的问题。

编辑:实际上Everything搜索引擎看起来更好。显然,Windows 会记录文件系统的更改日志,而 Everything 使用它来保持数据库的最新状态。

于 2010-01-15T22:41:10.137 回答
2

Windows 桌面搜索不提供这样的索引作为副产品吗?在 Mac 上,可以查询 Spotlight 索引以获取如下文件名:mdfind -onlyin . -name '*'.

当然它比遍历目录要快得多。

于 2010-01-13T20:41:52.563 回答
1

最简洁的答案是不”。您将无法在 Python 中构建一个比文件系统快一个数量级的索引系统。

无论缓存实现如何,“索引”文件系统都是一项密集/缓慢的任务。避免构建文件系统索引的巨大开销的唯一现实方法是“随时索引”以避免大遍历。(毕竟,文件系统本身已经是一个数据索引器。)

有一些操作系统功能能够执行这种“随用随构建”的文件系统索引。它是 OSX 上的 Spotlight 和 Windows 桌面搜索等服务的基础。

为了获得比遍历目录更快的速度,您需要利用其中一种操作系统或文件系统级别的工具。

此外,尽量不要误导自己认为解决方案更快,因为您已将工作“移动”到不同的时间/过程。您的示例代码正是这样做的。您在构建相同文件并创建索引时遍历示例文件的目录结构,然后读取该文件。

这里有两节课。(a) 要创建正确的测试,必须将“设置”与“测试”分开。在这里,您的性能测试基本上是说:“哪个更快,遍历目录结构或读取已经预先创建的索引?” 显然,这不是苹果与橘子的比较。

但是,(b)您同时偶然发现了正确答案。如果使用已经存在的索引,则可以更快地获取文件列表。这是您需要利用诸如 Windows 桌面搜索或 Spotlight 索引之类的东西的地方。

毫无疑问,为了建立文件系统的索引,根据定义,您必须“访问”每个文件。如果您的文件存储在树中,那么递归遍历可能是您访问每个文件的最快方式。如果问题是“我是否可以编写 Python 代码来完全执行该os.walk操作,但要比它快一个数量级os.walk”,答案是肯定的“” 。如果问题是“我是否可以编写 Python 代码来索引系统上的每个文件,而无需花时间实际访问每个文件”,那么答案仍然是否定的。

编辑回应“我认为你不明白我想要做什么”

让我们在这里明确一点,几乎这里的每个人都了解您要做什么。似乎您正在采取“不,这不会像您希望的那样起作用”来表示我们不理解。

让我们从另一个角度来看这个。文件系统从一开始就是现代计算的重要组成部分。数据的分类、索引、存储和检索是计算机科学和计算机工程的重要组成部分,计算机科学领域的许多最杰出的人才都在不断努力。

您希望能够根据文件的属性/元数据/数据过滤/选择文件。这是计算中经常使用的极其常见的任务。即使在您现在正在使用的计算机上,它也可能每秒发生几次。

如果通过简单地保留文件名和属性的文本文件索引来将这个过程加速一个数量级(!)那么简单,您不认为现有的每个文件系统和操作系统都会这样做吗?

也就是说,缓存特定查询的结果当然可以为您带来一些小的性能提升。而且,正如预期的那样,文件系统和磁盘缓存是每个现代操作系统和文件系统的基本组成部分。

但是,正如您提出的那样,您的问题有一个明确的答案:。在一般情况下,重新实现的速度不会提高一个数量级os.walk。您可能能够通过缓存获得更好的摊销运行时,但如果您在分析中正确包含构建缓存的工作,您将不会被它打败一个数量级。

于 2010-01-14T02:15:28.843 回答
0

我是 Python 新手,但根据我读过的报告,我正在使用列表推导、迭代器和生成器的组合。

class DirectoryIterator:
    def __init__(self, start_dir, pattern):
        self.directory = start_dir
        self.pattern = pattern

 def __iter__(self):
     [([DirectoryIterator(dir, self.pattern) for dir in dirnames], [(yield os.path.join(dirpath, name)) for name in filenames if re.search(self.pattern, name) ]) for dirpath, dirnames, filenames in os.walk(self.directory)]

 ###########

 for file_name in DirectoryIterator(".", "\.py$"): print file_name
于 2010-01-13T22:21:11.037 回答
0

我想建议您为此使用os.walk(获取目录树)和os.stat(获取文件信息)的组合。使用 std-lib 将确保它适用于所有平台,并且它们可以很好地完成工作。并且不需要索引任何东西。

正如其他人所说,我真的不认为你会通过尝试索引和重新索引文件系统来购买很多东西,特别是如果你已经通过路径和参数限制了你的功能。

于 2010-01-13T20:33:16.043 回答