2017-01-01 59 views
6

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?

đang

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 CGO, sẽ được truy cập vào đĩa.
+1

[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. –

+0

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

Trả lời

4

Gói dirent có vẻ như nó hoàn thành những gì bạn đang tìm kiếm. Dưới đây là ví dụ C của bạn được viết bằng chữ:

package main 

import (
    "bytes" 
    "fmt" 
    "io" 

    "github.com/EricLagergren/go-gnulib/dirent" 
    "golang.org/x/sys/unix" 
) 

func int8ToString(s []int8) string { 
    var buff bytes.Buffer 
    for _, chr := range s { 
     if chr == 0x00 { 
      break 
     } 
     buff.WriteByte(byte(chr)) 
    } 
    return buff.String() 
} 

func main() { 
    stream, err := dirent.Open("/usr/bin") 
    if err != nil { 
     panic(err) 
    } 
    defer stream.Close() 
    for { 
     entry, err := stream.Read() 
     if err != nil { 
      if err == io.EOF { 
       break 
      } 
      panic(err) 
     } 

     name := int8ToString(entry.Name[:]) 
     if entry.Type == unix.DT_DIR { 
      fmt.Printf("%s is a directory\n", name) 
     } else { 
      fmt.Printf("%s is not a directory\n", name) 
     } 
    } 
} 
Các vấn đề liên quan