Tôi đang viết một chương trình tìm tất cả thư mục con từ thư mục mẹ chứa một số lượng lớn tệp bằng cách sử dụng os.File.Readdir
, nhưng chạy số strace
để xem số lượng hệ thống cho thấy phiên bản đang sử dụng lstat()
trên tất cả các tệp/thư mục có trong thư mục mẹ. (Tôi đang thử nghiệm này với /usr/bin
thư mục cho bây giờ)golang os * File.Readdir sử dụng lstat trên tất cả các tệp. Nó có thể được tối ưu hóa không?
Go:
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 trên chương trình (không có đề sau):
% 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
Tôi đã thử nghiệm tương tự với C readdir()
mà không thấy hành vi này.
C mã:
#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 về chương trình:
% 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
Tôi biết rằng các lĩnh vực duy nhất trong cơ cấu dirent được ủy quyền bởi POSIX.1 là d_name và d_ino, nhưng Tôi đang viết điều này cho một hệ thống tập tin cụ thể.
Đã thử *File.Readdirnames()
, không sử dụng lstat
và cung cấp danh sách tất cả các tệp và thư mục, nhưng để xem chuỗi được trả về có phải là tệp hoặc thư mục cuối cùng sẽ thực hiện lại một lần nữa hay không.
- Tôi đã tự hỏi nếu có thể viết lại chương trình đi theo cách để tránh
lstat()
trên tất cả các tệp không nhất thiết. Tôi có thể thấy chương trình C đang sử dụng các syscalls sau đây.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
- Đây có phải là một sự tối ưu hóa sớm mà tôi không nên lo lắng không? Tôi đưa ra câu hỏi này vì số lượng tệp trong thư mục đang được giám sát sẽ có số lượng lớn các tệp lưu trữ nhỏ và sự khác biệt về hệ thống gần như gấp đôi giữa phiên bản
C
vàGO
, sẽ được truy cập vào đĩa.
[Gói này] (https://godoc.org/github.com/EricLagergren/go-gnulib/dirent) có vẻ như cần cung cấp hành vi mong muốn. –
Cảm ơn bạn @TimCooper. Nếu bạn có thể đặt nó như một câu trả lời, tôi sẽ chấp nhận nó. – nohup