我有幾種情況需要遞歸列出文件,但是我的實現速度很慢。我有一個包含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