私はos.File.Readdir
を使用して膨大な数のファイルを含む親ディレクトリからすべてのサブディレクトリを検索するプログラムを作成していますが、strace
を実行してシステムコールの数を確認すると、goバージョンはすべてのファイルに対してlstat()
/ディレクトリが親ディレクトリにあります。golang os *すべてのファイルに対してlstatを使用したFile.Readdir。それは最適化できますか?
囲碁コードを(私は今の/usr/bin
ディレクトリでこれをテストしてい):
package main
import (
"fmt"
"os"
)
func main() {
x, err := os.Open("/usr/bin")
if err != nil {
panic(err)
}
y, err := x.Readdir(0)
if err != nil {
panic(err)
}
for _, i := range y {
fmt.Println(i)
}
}
straceの(次のスレッドなし)プログラム上:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
93.62 0.004110 2 2466 write
3.46 0.000152 7 22 getdents64
2.92 0.000128 0 2466 lstat // this increases with increase in no. of files.
0.00 0.000000 0 11 mmap
0.00 0.000000 0 1 munmap
0.00 0.000000 0 114 rt_sigaction
0.00 0.000000 0 8 rt_sigprocmask
0.00 0.000000 0 1 sched_yield
0.00 0.000000 0 3 clone
0.00 0.000000 0 1 execve
0.00 0.000000 0 2 sigaltstack
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 gettid
0.00 0.000000 0 57 futex
0.00 0.000000 0 1 sched_getaffinity
0.00 0.000000 0 1 openat
------ ----------- ----------- --------- --------- ----------------
100.00 0.004390 5156 total
私はCのreaddir()
と同じことをテストしましたこの動作を見ることなく。
Cコード:
#include <stdio.h>
#include <dirent.h>
int main (void) {
DIR* dir_p;
struct dirent* dir_ent;
dir_p = opendir ("/usr/bin");
if (dir_p != NULL) {
// The readdir() function returns a pointer to a dirent structure representing the next
// directory entry in the directory stream pointed to by dirp.
// It returns NULL on reaching the end of the directory stream or if an error occurred.
while ((dir_ent = readdir (dir_p)) != NULL) {
// printf("%s", dir_ent->d_name);
// printf("%d", dir_ent->d_type);
if (dir_ent->d_type == DT_DIR) {
printf("%s is a directory", dir_ent->d_name);
} else {
printf("%s is not a directory", dir_ent->d_name);
}
printf("\n");
}
(void) closedir(dir_p);
}
else
perror ("Couldn't open the directory");
return 0;
}
straceのプログラム上:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.000128 0 2468 write
0.00 0.000000 0 1 read
0.00 0.000000 0 3 open
0.00 0.000000 0 3 close
0.00 0.000000 0 4 fstat
0.00 0.000000 0 8 mmap
0.00 0.000000 0 3 mprotect
0.00 0.000000 0 1 munmap
0.00 0.000000 0 3 brk
0.00 0.000000 0 3 3 access
0.00 0.000000 0 1 execve
0.00 0.000000 0 4 getdents
0.00 0.000000 0 1 arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00 0.000128 2503 3 total
私はPOSIX.1で義務付けられているdirent構造体の唯一のフィールドがd_nameとd_inoであることを承知していますが、私はこれを特定のファイルシステム用に書いています。 を使用せず、すべてのファイルとディレクトリのリストを返しますが、返された文字列がファイルかディレクトリかを確認するために、最終的にlstat
を実行します。
- すべてのファイルの
lstat()
が必ずしも必要ないように、goプログラムを書き直すことが可能かどうかは疑問でした。 Cプログラムが以下のシステムコールを使用しているのがわかりました。open("/usr/bin", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFDIR|0755, st_size=69632, ...}) = 0 brk(NULL) = 0x1098000 brk(0x10c1000) = 0x10c1000 getdents(3, /* 986 entries */, 32768) = 32752
- これは早すぎる最適化のようなものですが、私は心配しないでください。私はこの問題を提起しました。監視されているディレクトリ内のファイルの数には、膨大な数の小さなアーカイブファイルがあるため、システムコールの違いはディスクに当たる
C
とGO
バージョンの間のほぼ2倍です。
[このパッケージ](https://godoc.org/github.com/EricLagergren/go-gnulib/dirent)がそうです。 –
ありがとう@TimCooper。あなたが答えとしてそれを置くことができれば、私はそれを受け入れます。 – nohup