我有几种情况需要递归列出文件,但是我的实现速度很慢。我有一个包含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和list的创建。这可能有助于吗?我还应该做什么?
编辑:我编辑了使用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
我做了一个使用System.Posix.Directory的版本并进行迭代,如果有更好的方法,它没有太多的工作。我发现一件奇怪的事情是System.Posix.Directory似乎没有提供我期望的功能。“readdir”返回一个指向“struct dirent”的指针,但它似乎是唯一可以从DirectoryStream获得的是文件名 - 这意味着你必须再次调用(可能是通过使用doesDirectoryExist的stat())来查找是否这是一个目录。这也可能是问题的一部分 - 找到并不需要另一个系统调用来发现它是否是一个目录。 – mokus 2010-10-07 23:09:49
@mokus:感谢您的信息。在Posix系统中,由[readdir](http://www.opengroup.org/onlinepubs/009695399/functions/readdir.html)读取目录不会返回返回的条目是否是目录,因此您需要单独系统调用(通常是stat或lstat)来决定它是否是一个目录。因此,您描述的System.Posix.Directory的行为并不奇怪。 find命令的一些实现使用硬链接计数技巧来省略对stat的不必要调用,这使得遍历速度更快。 – 2010-10-08 00:52:21
在我的系统(Mac OS)上,“struct dirent”有一个字段“d_type”,其中一个可能的值是“DT_DIR”。维基百科提示这在POSIX规范中是可选的,但它肯定会是DirectoryStream提供'isDir'或'fileType'操作的一个强有力的例子,如果可用则使用该信息,否则调用stat。即使它不是必需的标准,如果他的平台有它,如果发现不使用它,我会感到震惊。 – mokus 2010-10-08 01:01:54