[英]Reasonably faster way to traverse a directory tree in Python?
假設給定的目錄樹大小合理:比如像Twisted或Python這樣的開源項目,那么遍歷和遍歷該目錄中所有文件/目錄的絕對路徑的最快方法是什么?
我想在Python中做到這一點。 os.path.walk很慢。 所以我嘗試了ls -lR和tree -fi 。 對於包含大約8337個文件的項目(包括tmp,pyc,test,.svn文件):
$ time tree -fi > /dev/null
real 0m0.170s
user 0m0.044s
sys 0m0.123s
$ time ls -lR > /dev/null
real 0m0.292s
user 0m0.138s
sys 0m0.152s
$ time find . > /dev/null
real 0m0.074s
user 0m0.017s
sys 0m0.056s
$
tree
似乎比ls -lR
快(雖然ls -R
比tree
快,但它不提供完整路徑)。 find
是最快的。
任何人都可以想到更快和/或更好的方法嗎? 在Windows上,如果需要,我可能只是發送一個32位二進制tree.exe或ls.exe。
更新1 :添加了find
更新2 :為什么我要這樣做? ...我正在嘗試巧妙地替換cd,pushd等...以及依賴於傳遞路徑的其他命令的包裝命令(更少,更多,cat,vim,tail)。 程序將偶爾使用文件遍歷來執行此操作(例如:鍵入“cd sr grai pat lxml”將自動轉換為“cd src / pypm / grail / patches / lxml”)。 如果這個替換CD需要半秒才能運行,我將不會感到滿意。 請參閱http://github.com/srid/pf
即使os.path.walk
沒有花時間,你在pf中的方法也會無可救葯地緩慢。 在所有現存路徑上進行包含3個無界閉包的正則表達式匹配將會在那里殺死你。 以下是我引用的Kernighan和Pike的代碼,這是一個適合該任務的算法:
/* spname: return correctly spelled filename */
/*
* spname(oldname, newname) char *oldname, *newname;
* returns -1 if no reasonable match to oldname,
* 0 if exact match,
* 1 if corrected.
* stores corrected name in newname.
*/
#include <sys/types.h>
#include <sys/dir.h>
spname(oldname, newname)
char *oldname, *newname;
{
char *p, guess[DIRSIZ+1], best[DIRSIZ+1];
char *new = newname, *old = oldname;
for (;;) {
while (*old == '/') /* skip slashes */
*new++ = *old++;
*new = '\0';
if (*old == '\0') /* exact or corrected */
return strcmp(oldname,newname) != 0;
p = guess; /* copy next component into guess */
for ( ; *old != '/' && *old != '\0'; old++)
if (p < guess+DIRSIZ)
*p++ = *old;
*p = '\0';
if (mindist(newname, guess, best) >= 3)
return -1; /* hopeless */
for (p = best; *new = *p++; ) /* add to end */
new++; /* of newname */
}
}
mindist(dir, guess, best) /* search dir for guess */
char *dir, *guess, *best;
{
/* set best, return distance 0..3 */
int d, nd, fd;
struct {
ino_t ino;
char name[DIRSIZ+1]; /* 1 more than in dir.h */
} nbuf;
nbuf.name[DIRSIZ] = '\0'; /* +1 for terminal '\0' */
if (dir[0] == '\0') /* current directory */
dir = ".";
d = 3; /* minimum distance */
if ((fd=open(dir, 0)) == -1)
return d;
while (read(fd,(char *) &nbuf,sizeof(struct direct)) > 0)
if (nbuf.ino) {
nd = spdist(nbuf.name, guess);
if (nd <= d && nd != 3) {
strcpy(best, nbuf.name);
d = nd;
if (d == 0) /* exact match */
break;
}
}
close(fd);
return d;
}
/* spdist: return distance between two names */
/*
* very rough spelling metric:
* 0 if the strings are identical
* 1 if two chars are transposed
* 2 if one char wrong, added or deleted
* 3 otherwise
*/
#define EQ(s,t) (strcmp(s,t) == 0)
spdist(s, t)
char *s, *t;
{
while (*s++ == *t)
if (*t++ == '\0')
return 0; /* exact match */
if (*--s) {
if (*t) {
if (s[1] && t[1] && *s == t[1]
&& *t == s[1] && EQ(s+2, t+2))
return 1; /* transposition */
if (EQ(s+1, t+1))
return 2; /* 1 char mismatch */
}
if (EQ(s+1, t))
return 2; /* extra character */
}
if (*t && EQ(s, t+1))
return 2; /* missing character */
return 3;
}
注意 :此代碼是在ANSI C,ISO C或POSIX之前編寫的,當一個讀取目錄文件為raw時,甚至可以想象。 代碼的方法遠比所有指針吊索更有用。
在性能方面find
它會好得多,但問題是速度有多快,為什么你需要這么快? 你聲稱os.path.walk很慢,實際上,它在我的機器上比16k目錄的樹慢約3倍。 但話說回來,我們正在討論Python的0.68秒和1.9秒之間的差異。
如果設定一個速度記錄是你的目標,你不能擊敗硬編碼C,這完全是75%的系統調用限制,你不能讓操作系統更快。 也就是說,25%的Python時間花在了系統調用上。 你想用遍歷的路徑做什么?
你沒有提到的一個解決方案是'os.walk'。 我不確定它會比os.path.walk更快,但它客觀上更好。
當你擁有目錄列表時,你還沒有說明你要做什么,所以很難給出更具體的建議。
雖然我懷疑你有多個讀頭,但這里是你如何遍歷幾百萬個文件(我們在幾分鍾內完成了10M +)。
https://github.com/hpc/purger/blob/master/src/treewalk/treewalk.c
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.