2017-01-01 52 views
6

os.File.Readdir kullanarak çok sayıda dosya içeren bir üst dizinden tüm alt dizinleri bulan bir program yazıyorum, ancak sistem çağrısı sayısının tüm dosyalarda lstat() sürümünü kullanabildiğini görmek için bir strace çalıştırıyor Üst dizinde/dizinleri bulunur. (Aşağıdaki parçacığı olmadan) program üzerindegolang os * File.Readdir. Optimize edilebilir mi?

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:

Git kodunu (şimdilik /usr/bin dizin olan bu test ediyorum)

% 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 

Ben C'ler readdir() ile aynı test Bu davranışı görmeden.

C kodu: program üzerinde

#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 

Ben POSIX.1'e tarafından görevlendirilmiş olan dirent yapısında sadece alanlar d_name ve d_ino farkındayım ama Bunu belirli bir dosya sistemi için yazıyorum.

bir lstat kullanabilir ve tüm dosya ve dizinleri bir listesini verir, ancak döndürülen dize bir dosya ya da dizin tekrar lstat yapmak eninde sonunda olacak olmadığını görmek için etmeyen, *File.Readdirnames() çalıştı.

  • un-ille tüm dosyalar üzerinde lstat() önlemek için bir şekilde yeniden yazmak go programı mümkün olup olmadığını merak ediyorum. C programı aşağıdaki syscalls kullandığını görebiliyordum. 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
  • Bu, endişelenmemesi gereken, erken bir optimizasyon gibi bir şey midir? Bu soruyu sordum çünkü izlenen dizindeki dosyaların sayısı çok sayıda küçük arşivlenmiş dosyaya sahip olacak ve systemcalls'daki fark, diske vuracak olan C ve GO sürümlerinin neredeyse iki katıdır.
+1

[Bu paket] (https://godoc.org/github.com/EricLagergren/go-gnulib/dirent) görünüyor: Aşağıda Go yazılmış C örneğidir. –

+0

Teşekkür ederiz @TimCooper. Eğer bir cevap olarak koyabilirsen, kabul edeceğim. – nohup

cevap

4

dirent paketi, aradığınız şeyi başardığı gibi görünüyor. istenilen davranışı sağlamalıdır gibi

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) 
     } 
    } 
}