【问题标题】:How to list directories faster?如何更快地列出目录?
【发布时间】:2010-10-07 12:56:41
【问题描述】:

我有一些需要递归列出文件的情况,但我的实现速度很慢。我有一个包含 92784 个文件的目录结构。 find 在不到 0.5 秒的时间内列出文件,但我的 Haskell 实现要慢很多。

我的第一个实现需要 9 多秒才能完成,下一个版本需要 5 多秒,而我目前只用了不到 2 秒。

listFilesR :: FilePath -> IO [FilePath]
listFilesR path = let
    isDODD "." = False
    isDODD ".." = False
    isDODD _ = True

    in do
        allfiles <- getDirectoryContents path
    dirs <- forM allfiles $ \d ->
      if isDODD d then
        do let p = path </> d
           isDir <- doesDirectoryExist p
           if isDir then listFilesR p else return [d]
        else return []
    return $ concat dirs

测试占用大约 100 兆内存 (+RTS -s),程序在 GC 上花费了大约 40%。

我正在考虑在 WriterT monad 中使用 Sequence 作为 monoid 进行列表,以防止 concats 和列表创建。这可能有帮助吗?我还应该做什么?

编辑:我已经编辑了函数以使用 readDirStream,它有助于减少内存。仍有一些分配发生,但现在的生产率 > 95% 并且运行时间不到一秒。

这是当前版本:

list path = do
  de <- openDirStream path
  readDirStream de >>= go de
  closeDirStream de
  where
    go d [] = return ()
    go d "." = readDirStream d >>= go d
    go d ".." = readDirStream d >>= go d
    go d x = let newpath = path </> x
         in do
          e <- doesDirectoryExist newpath
          if e 
        then
          list newpath >> readDirStream d >>= go d
        else putStrLn newpath >> readDirStream d >>= go d 

【问题讨论】:

    标签: optimization haskell file-io io


    【解决方案1】:

    我认为System.Directory.getDirectoryContents 构造了一个完整的列表,因此会占用大量内存。使用System.Posix.Directory 怎么样? System.Posix.Directory.readDirStream 一个一个返回一个条目。

    另外,FileManip library 可能有用,尽管我从未使用过。

    【讨论】:

    • 我使用 System.Posix.Directory 和 iteratees 制作了一个版本,如果有更好的话,它并没有做太多。我发现的一件奇怪的事情是 System.Posix.Directory 似乎没有提供我期望的功能。 “readdir”返回一个指向“struct dirent”的指针,但似乎您可以从 DirectoryStream 获得的唯一东西是文件名 - 这意味着您必须进行另一个调用(可能是通过 dosDirectoryExist 调用 stat())来确定是否这是一个目录。这也可能是问题的一部分 - find 不需要进行另一个系统调用来发现它是否是目录。
    • @mokus:感谢您提供的信息。在 Posix 系统中,通过readdir 读取目录不会返回返回的条目是否是目录,因此您需要单独的系统调用(通常是 stat 或 lstat)来确定它是否是目录。因此,您描述的 System.Posix.Directory 的行为并不奇怪。 find 命令的一些实现使用硬链接计数技巧来省略对 stat 的不必要调用,从而加快遍历速度。
    • 在我的系统(Mac OS)上,“struct dirent”有一个字段“d_type”,其中一个可能的值是“DT_DIR”。 Wikipedia 暗示这在 POSIX 规范中是可选的,但是 DirectoryStream 提供一个“isDir”或“fileType”操作肯定是一个强有力的案例,如果可用,它将使用该信息,否则调用 stat。即使它不是必需的标准,如果他的平台有它,如果 find 没有使用它,我会感到震惊。
    • @mokus:哇。我不知道 d_type 字段,但至少 LinuxFreeBSD 也有。看来这是一个事实上的标准。
    • @Tsuyoshi Ito 你如何在没有统计数据的情况下获得硬链接计数?
    【解决方案2】:

    分析您的代码显示大部分 CPU 时间都用于getDirectoryContentsdoesDirectoryExist&lt;/&gt;。这意味着仅更改数据结构不会有太大帮助。如果你想匹配 find 的性能,你应该使用较低级别的函数来访问文件系统,可能是 Tsuyoshi 指出的那些。

    【讨论】:

      【解决方案3】:

      一个问题是它必须构建完整的目录内容列表,然后程序才能对它们做任何事情。 Lazy IO 通常不受欢迎,但在这里使用 unsafeInterleaveIO 会显着减少内存使用。

      listFilesR :: FilePath -> IO [FilePath]
      listFilesR path = 
        let
          isDODD "." = False
          isDODD ".." = False
          isDODD _ = True
        in unsafeInterleaveIO $ do
          allfiles <- getDirectoryContents path
          dirs <- forM allfiles $ \d ->
            if isDODD d then
              do let p = path </> d
                 isDir <- doesDirectoryExist p
                 if isDir then listFilesR p else return [d]
              else return []
          return $ concat dirs
      

      【讨论】:

      • 这减少了大约 0.4 秒和 20 兆字节。所以稍微好一点
      【解决方案4】:

      是否可以选择将某种缓存系统与读取结合使用?我在考虑一个异步索引服务/线程,它在后台保持这个缓存是最新的,也许你可以把缓存作为一个简单的 SQL-DB 来做,然后在对它进行查询时给你一些很好的性能?

      您能否详细说明您的“项目/想法”,以便我们提出替代方案?

      我自己不会选择“完整索引”,因为我主要构建基于 Web 的服务,而“resposnetime”对我来说很重要,另一方面,如果它是启动新服务器的初始方式,我确信客户不会介意第一次等待。我只是将结果存储在数据库中以供以后查找。

      【讨论】:

      • 我总是乐于接受新想法。我正在为 Hyper Estraier(一个全文搜索引擎)编写一个包装器,供桌面使用。我是一个重度命令行用户,所以我正在考虑做一个本地收集器和搜索器。目前我已经将我的 bash-script 转换为 Haskell,但它仍然使用 estcmd 命令进行收集和搜索,并且系统进程调用很丑陋。对于本地收集器,我需要至少在第一遍解析每个文件。但是我想不出一种方法来仅列出自上次以来添加或修改的文件。
      • 好的 - 你正在为什么样的操作系统构建?例如。 Windows 具有用于新文件、重命名等的“目录事件”。如果您有某种“根”文件夹,则可以使用递归触发放置“根事件处理程序”。我自己还没有尝试过,但我第一次索引目录后会朝那个方向看。
      • Linux 有一个全局文件缓存,所以你不必写一个,它在应用程序之间共享。它也有目录事件。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      • 2017-05-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多