ext2,3で、1ディレクトリに大量のファイルがあるとlsが遅い件の対処とか

遅いのは知ってた。で、対処をどうすべきかと考えていたが、自分はlsが遅いのはreaddirとかの関数が遅くて、しょうがないのだと思っていたが、どうもそうではなかった。

lsが遅いのは、それぞれのファイルについて、その名前以外の情報をいつもstatしてたからなのだった。「lsすら遅い」のではなくて「lsするから遅い」のであった。
だから、ファイルの一覧が必要ならば、readdirすればいいだけなのであった。たとえば↓のような、manpageから引っこ抜いただけのようないい加減なコードでも、だいたい動く(SEGVとかするので、さすがにそこは直した)

#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    DIR *dirp;
    struct dirent *dp;

    if (argc != 2 ) {
        printf("coundn't open dir\n");
        return;
    }


    if ( (dirp = opendir(argv[1]) ) == NULL) {
        printf("coundn't open dir\n");
        return;
    }

    do {
        errno = 0;
        if ( (dp = readdir(dirp) ) != NULL) {
            (void) printf("%s\n", dp->d_name);
        }
    } while (dp != NULL);

    if (errno != 0)
        perror("error reading directory ");

    (void) closedir(dirp);
    return(0);
}

しかし、本当にreaddirは遅くないのか?というと、1ディレクトリに数万個のファイルが有った場合には、それなりに時間がかかるもののようだ。(CPU時間で0.3秒くらい?)
Kernelは2.6.9-42.ELsmp #1 SMPでCentOS4.4 1CPUbogomipsは 4800程度で、strace -c の結果はこんな感じ

 % time     seconds  usecs/call     calls    errors syscall
 ------ ----------- ----------- --------- --------- ----------------
 96.08    0.275670        2484       111           getdents64
  3.53    0.010142          16       620           write
  0.16    0.000473         158         3           open
  0.12    0.000339         339         1           execve
  0.03    0.000075          15         5           old_mmap
  0.02    0.000045          11         4           brk
  0.01    0.000038          10         4           fstat64
  0.01    0.000030          30         1           read
  0.01    0.000024           8         3           close
  0.01    0.000022          11         2           mprotect
  0.01    0.000017          17         1           munmap
  0.01    0.000016          16         1         1 access
  0.00    0.000012          12         1           uname
  0.00    0.000007           7         1           mmap2
  0.00    0.000005           5         1           fcntl64
  0.00    0.000005           5         1           set_thread_area
 ------ ----------- ----------- --------- --------- ----------------
 100.00    0.286920                   760         1 total

極普通のディレクトリでこれを実行しても、getdents64は2,3回がせいぜい。

ちょっと訂正というか、追加情報。
上記の結果は、NFSでマウントしているディレクトリだった。
直付けの場合、何度かreaddirを実行してると、kernelのキャッシュだの、ディスク自体のキャッシュが比較的良く効いて来るようだ。たとえば、1ディレクトリに100万個ぐらいファイルがあっても、getdentsの処理時間が十数マイクロ秒ということもある。(Kernel 2.6.9-42.ELsmp、 1CPUのBogomipsは5500程度)

# strace -c readdir . > /dev/null
% time     seconds  usecs/call     calls    errors syscall
 ------ ----------- ----------- --------- --------- ----------------
 80.44    0.442220          14     30821           getdents64
 19.48    0.107076           8     13018           write
  0.03    0.000149         149         1           execve
  0.01    0.000054          11         5           old_mmap
  0.01    0.000038          13         3           open
  0.01    0.000035          35         1           read
  0.01    0.000031           8         4           fstat64
  0.01    0.000030           8         4           brk
  0.00    0.000022          11         2           mprotect
  0.00    0.000014           5         3           close
  0.00    0.000013          13         1           munmap
  0.00    0.000011          11         1         1 access
  0.00    0.000008           8         1           mmap2
  0.00    0.000008           8         1           fcntl64
  0.00    0.000007           7         1         1 ioctl
  0.00    0.000007           7         1           uname
  0.00    0.000003           3         1           set_thread_area
 ------ ----------- ----------- --------- --------- ----------------
100.00    0.549726                 43869         2 total