2010-04-22 27 views
2

假設給定的目錄樹具有合理的大小:說一個像Twisted或Python這樣的開源項目,遍歷和遍歷該目錄內所有文件/目錄的絕對路徑的最快方法是什麼?在Python中遍歷目錄樹的方法合理得快?

我想在Python中做到這一點。 os.path.walk很慢。所以我嘗試了ls -lR樹-fi。對於具有約8337文件的項目(包括TMP,PYC,測試的.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 -Rtree快,但它沒有給出完整路徑) 。 find是最快的。

任何人都可以想到更快和/或更好的方法嗎?在Windows上,如果需要,我可以直接發送32位二進制tree.exe或ls.exe。

更新1:新增find

更新2:爲什麼我要這麼做? ...我試圖做一個聰明的替代cd,pushd等。和包裝命令的其他命令依賴於傳遞路徑(更少,更多,貓,vim,尾巴)。該程序偶爾會使用文件遍歷來做到這一點(例如:鍵入「cd sr grai pat lxml」會自動轉換爲「cd src/pypm/grail/patches/lxml」)。如果這個CD更換花費了半秒鐘的時間,我將不會滿意。見http://github.com/srid/pf

+0

這似乎是一個非常奇怪的事情來優化。你在做什麼,這是一個性能至關重要的操作?你分析和研究你的算法,並確定這是問題?即使像Twisted這樣的「大」目錄樹也不大。 – 2010-04-22 23:34:20

+0

@Mike:背景:我試圖爲cd,pushd等做一個聰明的替代品,並依靠傳遞路徑(更少,更多,貓,vim)的其他命令的包裝。該程序偶爾會使用文件遍歷來做到這一點(例如:鍵入「cd sr grai pat lxml」會自動轉換爲「cd src/pypm/grail/patches/lxml」)。如果這個'cd'替代品需要運行半秒鐘,我將不會滿意。 http://github.com/srid/pf – 2010-04-22 23:47:45

+1

然後我們回到算法;路徑修正最好採用分段而不是「整棵樹並與之相匹配」,因爲在每個路徑組件上設置的搜索都很小。 Kernighan和Pike在Unix編程環境中有一個經典的交互路徑校正器 - 尋找spdist()http://netlib.bell-labs.com/cm/cs/upe/ – msw 2010-04-22 23:53:13

回答

0

你沒有提到的一個解決方案是'os.walk'。我不確定它會比os.path.walk更快,但客觀上更好。

你還沒有說當你擁有目錄時你要做什麼,所以很難給出更具體的建議。

+0

我對它進行了計時,os.walk在我的系統上慢了15%,可能是因爲它產生了所有文件而不是目錄。但是,正如我在答案中指出的那樣,2.3秒並不是所有那麼大,相比之下0.7秒。 – msw 2010-04-22 23:48:22

+0

我將如何處理已過濾的路徑列表?我會做一個正則表達式匹配。看到我對上面的Mike的回覆。也http://github.com/srid/pf/blob/master/src/pf/__init__.py – 2010-04-22 23:49:52

1

在性能上很難比find好得多,但問題是速度要快多少,爲什麼你需要這麼快?你聲稱os.path.walk很慢,實際上,在我的機器上,它比16k目錄的樹慢了3倍。但是,我們再次談論Python的0.68秒和1.9秒之間的差異。

如果設置的速度記錄是你的目標,你不能打硬編碼ç這是完全75%的系統調用的約束,你不能讓操作系統走得更快。也就是說,25%的Python時間花費在系統調用中。你想用遍歷路徑做什麼?

+0

「爲什麼我需要它如此之快?」 - 我已經更新了這個問題。 – 2010-04-22 23:51:29

3

你在PF做法將是無可救藥緩慢,即使os.path.walk沒有采取任何時候都。在所有現存路徑中進行包含3個無界閉包的正則表達式匹配會在那裏殺死你。下面是從Kernighan and 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。代碼的方法比所有的指針索引要有用得多。

+0

非常感謝,我會在這個週末看看這個問題,並回答問題,如果有的話。 – 2010-04-23 00:13:13