mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
reduce CPU usage upto 50% in readdir (#10466)
This commit is contained in:
parent
0104af6bcc
commit
b1c99e88ac
@ -119,7 +119,7 @@ func readAllFileInfo(ctx context.Context, disks []StorageAPI, bucket, object, ve
|
||||
metadataArray := make([]FileInfo, len(disks))
|
||||
|
||||
g := errgroup.WithNErrs(len(disks))
|
||||
// Read `xl.meta` parallelly across disks.
|
||||
// Read `xl.meta` in parallel across disks.
|
||||
for index := range disks {
|
||||
index := index
|
||||
g.Go(func() (err error) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
@ -42,14 +43,14 @@ var direntPool = sync.Pool{
|
||||
// value used to represent a syscall.DT_UNKNOWN Dirent.Type.
|
||||
const unexpectedFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice
|
||||
|
||||
func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode, err error) {
|
||||
func parseDirEnt(buf []byte) (consumed int, name []byte, typ os.FileMode, err error) {
|
||||
// golang.org/issue/15653
|
||||
dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[0]))
|
||||
if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v {
|
||||
return consumed, name, typ, fmt.Errorf("buf size of %d smaller than dirent header size %d", len(buf), v)
|
||||
return consumed, nil, typ, fmt.Errorf("buf size of %d smaller than dirent header size %d", len(buf), v)
|
||||
}
|
||||
if len(buf) < int(dirent.Reclen) {
|
||||
return consumed, name, typ, fmt.Errorf("buf size %d < record length %d", len(buf), dirent.Reclen)
|
||||
return consumed, nil, typ, fmt.Errorf("buf size %d < record length %d", len(buf), dirent.Reclen)
|
||||
}
|
||||
consumed = int(dirent.Reclen)
|
||||
if direntInode(dirent) == 0 { // File absent in directory.
|
||||
@ -72,11 +73,10 @@ func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode, err er
|
||||
nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
|
||||
nameLen, err := direntNamlen(dirent)
|
||||
if err != nil {
|
||||
return consumed, name, typ, err
|
||||
return consumed, nil, typ, err
|
||||
}
|
||||
|
||||
name = string(nameBuf[:nameLen])
|
||||
return consumed, name, typ, nil
|
||||
return consumed, nameBuf[:nameLen], typ, nil
|
||||
}
|
||||
|
||||
// Return all the entries at the directory dirPath.
|
||||
@ -116,13 +116,13 @@ func readDirFilterFn(dirPath string, filter func(name string, typ os.FileMode) e
|
||||
return err
|
||||
}
|
||||
boff += consumed
|
||||
if name == "" || name == "." || name == ".." {
|
||||
if len(name) == 0 || bytes.Equal(name, []byte{'.'}) || bytes.Equal(name, []byte{'.', '.'}) {
|
||||
continue
|
||||
}
|
||||
if typ&os.ModeSymlink == os.ModeSymlink {
|
||||
continue
|
||||
}
|
||||
if err = filter(name, typ); err == errDoneForNow {
|
||||
if err = filter(string(name), typ); err == errDoneForNow {
|
||||
// filtering requested to return by caller.
|
||||
return nil
|
||||
}
|
||||
@ -143,6 +143,10 @@ func readDirN(dirPath string, count int) (entries []string, err error) {
|
||||
bufp := direntPool.Get().(*[]byte)
|
||||
defer direntPool.Put(bufp)
|
||||
|
||||
nameTmp := direntPool.Get().(*[]byte)
|
||||
defer direntPool.Put(nameTmp)
|
||||
tmp := *nameTmp
|
||||
|
||||
boff := 0 // starting read position in buf
|
||||
nbuf := 0 // end valid data in buf
|
||||
|
||||
@ -165,14 +169,14 @@ func readDirN(dirPath string, count int) (entries []string, err error) {
|
||||
return nil, err
|
||||
}
|
||||
boff += consumed
|
||||
if name == "" || name == "." || name == ".." {
|
||||
if len(name) == 0 || bytes.Equal(name, []byte{'.'}) || bytes.Equal(name, []byte{'.', '.'}) {
|
||||
continue
|
||||
}
|
||||
// Fallback for filesystems (like old XFS) that don't
|
||||
// support Dirent.Type and have DT_UNKNOWN (0) there
|
||||
// instead.
|
||||
if typ == unexpectedFileMode {
|
||||
fi, err := os.Lstat(pathJoin(dirPath, name))
|
||||
fi, err := os.Lstat(pathJoin(dirPath, string(name)))
|
||||
if err != nil {
|
||||
// It got deleted in the meantime, not found
|
||||
// or returns too many symlinks ignore this
|
||||
@ -189,9 +193,13 @@ func readDirN(dirPath string, count int) (entries []string, err error) {
|
||||
continue
|
||||
}
|
||||
if typ.IsRegular() {
|
||||
entries = append(entries, name)
|
||||
entries = append(entries, string(name))
|
||||
} else if typ.IsDir() {
|
||||
entries = append(entries, name+SlashSeparator)
|
||||
// Use temp buffer to append a slash to avoid string concat.
|
||||
tmp = tmp[:len(name)+1]
|
||||
copy(tmp, name)
|
||||
tmp[len(tmp)-1] = '/' // SlashSeparator
|
||||
entries = append(entries, string(tmp))
|
||||
}
|
||||
count--
|
||||
}
|
||||
|
@ -29,29 +29,20 @@ type TreeWalkResult struct {
|
||||
}
|
||||
|
||||
// Return entries that have prefix prefixEntry.
|
||||
// Note: input entries are expected to be sorted.
|
||||
// The supplied entries are modified and the returned string is a subslice of entries.
|
||||
func filterMatchingPrefix(entries []string, prefixEntry string) []string {
|
||||
start := 0
|
||||
end := len(entries)
|
||||
for {
|
||||
if start == end {
|
||||
break
|
||||
}
|
||||
if HasPrefix(entries[start], prefixEntry) {
|
||||
break
|
||||
}
|
||||
start++
|
||||
if len(entries) == 0 || prefixEntry == "" {
|
||||
return entries
|
||||
}
|
||||
for {
|
||||
if start == end {
|
||||
break
|
||||
// Write to the beginning of entries.
|
||||
dst := entries[:0]
|
||||
for _, s := range entries {
|
||||
if !HasPrefix(s, prefixEntry) {
|
||||
continue
|
||||
}
|
||||
if HasPrefix(entries[end-1], prefixEntry) {
|
||||
break
|
||||
}
|
||||
end--
|
||||
dst = append(dst, s)
|
||||
}
|
||||
return entries[start:end]
|
||||
return dst
|
||||
}
|
||||
|
||||
// xl.ListDir returns entries with trailing "/" for directories. At the object layer
|
||||
@ -101,12 +92,12 @@ type IsLeafFunc func(string, string) bool
|
||||
type IsLeafDirFunc func(string, string) bool
|
||||
|
||||
func filterListEntries(bucket, prefixDir string, entries []string, prefixEntry string, isLeaf IsLeafFunc) ([]string, bool) {
|
||||
// Listing needs to be sorted.
|
||||
sort.Strings(entries)
|
||||
|
||||
// Filter entries that have the prefix prefixEntry.
|
||||
entries = filterMatchingPrefix(entries, prefixEntry)
|
||||
|
||||
// Listing needs to be sorted.
|
||||
sort.Strings(entries)
|
||||
|
||||
// Can isLeaf() check be delayed till when it has to be sent down the
|
||||
// TreeWalkResult channel?
|
||||
delayIsLeaf := delayIsLeafCheck(entries)
|
||||
@ -124,6 +115,7 @@ func filterListEntries(bucket, prefixDir string, entries []string, prefixEntry s
|
||||
// Sort again after removing trailing "/" for objects as the previous sort
|
||||
// does not hold good anymore.
|
||||
sort.Strings(entries)
|
||||
|
||||
return entries, false
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user