-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fastwalk: improve darwin perf by ~3x
``` name old time/op new time/op delta FastWalk-10 48.4ms ± 3% 16.6ms ± 3% -65.77% (p=0.000 n=10+10) name old alloc/op new alloc/op delta FastWalk-10 1.30MB ± 0% 1.29MB ± 0% -0.48% (p=0.000 n=10+10) name old allocs/op new allocs/op delta FastWalk-10 28.2k ± 0% 25.5k ± 0% -9.34% (p=0.000 n=9+10) ```
- Loading branch information
1 parent
3f3c1d6
commit 91f1444
Showing
6 changed files
with
197 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,96 @@ | ||
// Copyright 2016 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
//go:build appengine || darwin | ||
// +build appengine darwin | ||
//go:build darwin | ||
// +build darwin | ||
|
||
package fastwalk | ||
|
||
import "os" | ||
import ( | ||
"os" | ||
"syscall" | ||
"unsafe" | ||
) | ||
|
||
// readDir calls fn for each directory entry in dirName. | ||
// It does not descend into directories or follow symlinks. | ||
// If fn returns a non-nil error, readDir returns with that error | ||
// immediately. | ||
func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { | ||
fis, err := os.ReadDir(dirName) | ||
fd, err := openDir(dirName) | ||
if err != nil { | ||
return err | ||
return &os.PathError{Op: "opendir", Path: dirName, Err: err} | ||
} | ||
defer closedir(fd) | ||
|
||
skipFiles := false | ||
for _, fi := range fis { | ||
if fi.Type().IsRegular() && skipFiles { | ||
var dirent syscall.Dirent | ||
var entptr *syscall.Dirent | ||
for { | ||
if errno := readdir_r(uintptr(fd), &dirent, &entptr); errno != 0 { | ||
if errno == syscall.EINTR { | ||
continue | ||
} | ||
return &os.PathError{Op: "readdir", Path: dirName, Err: errno} | ||
} | ||
if entptr == nil { // EOF | ||
break | ||
} | ||
if dirent.Ino == 0 { | ||
continue | ||
} | ||
if err := fn(dirName, fi.Name(), fi.Type()); err != nil { | ||
if err == ErrSkipFiles { | ||
skipFiles = true | ||
continue | ||
typ := dtToType(dirent.Type) | ||
if skipFiles && typ.IsRegular() { | ||
continue | ||
} | ||
name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:] | ||
for i, c := range name { | ||
if c == 0 { | ||
name = name[:i] | ||
break | ||
} | ||
return err | ||
} | ||
// Check for useless names before allocating a string. | ||
if string(name) == "." || string(name) == ".." { | ||
continue | ||
} | ||
nm := string(name) | ||
if err := fn(dirName, nm, typ); err != nil { | ||
if err != ErrSkipFiles { | ||
return err | ||
} | ||
skipFiles = true | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func dtToType(typ uint8) os.FileMode { | ||
switch typ { | ||
case syscall.DT_BLK: | ||
return os.ModeDevice | ||
case syscall.DT_CHR: | ||
return os.ModeDevice | os.ModeCharDevice | ||
case syscall.DT_DIR: | ||
return os.ModeDir | ||
case syscall.DT_FIFO: | ||
return os.ModeNamedPipe | ||
case syscall.DT_LNK: | ||
return os.ModeSymlink | ||
case syscall.DT_REG: | ||
return 0 | ||
case syscall.DT_SOCK: | ||
return os.ModeSocket | ||
} | ||
return ^os.FileMode(0) | ||
} | ||
|
||
// According to https://golang.org/doc/go1.14#runtime | ||
// A consequence of the implementation of preemption is that on Unix systems, | ||
// including Linux and macOS systems, programs built with Go 1.14 will receive | ||
// more signals than programs built with earlier releases. | ||
// | ||
// This causes opendir to sometimes fail with EINTR errors. | ||
// We need to retry in this case. | ||
func openDir(path string) (fd uintptr, err error) { | ||
for { | ||
fd, err := opendir(path) | ||
if err != syscall.EINTR { | ||
return fd, err | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
//go:build darwin && go1.13 | ||
// +build darwin,go1.13 | ||
|
||
package fastwalk | ||
|
||
import ( | ||
"syscall" | ||
"unsafe" | ||
) | ||
|
||
// Implemented in the runtime package (runtime/sys_darwin.go) | ||
func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) | ||
func syscall_syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) | ||
|
||
//go:linkname syscall_syscall syscall.syscall | ||
//go:linkname syscall_syscallPtr syscall.syscallPtr | ||
|
||
func closedir(dir uintptr) (err error) { | ||
_, _, e1 := syscall_syscall(libc_closedir_trampoline_addr, uintptr(dir), 0, 0) | ||
if e1 != 0 { | ||
err = e1 | ||
} | ||
return | ||
} | ||
|
||
var libc_closedir_trampoline_addr uintptr | ||
|
||
//go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib" | ||
|
||
func readdir_r(dir uintptr, entry *syscall.Dirent, result **syscall.Dirent) syscall.Errno { | ||
res, _, _ := syscall_syscall(libc_readdir_r_trampoline_addr, uintptr(dir), uintptr(unsafe.Pointer(entry)), uintptr(unsafe.Pointer(result))) | ||
return syscall.Errno(res) | ||
} | ||
|
||
var libc_readdir_r_trampoline_addr uintptr | ||
|
||
//go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib" | ||
|
||
func opendir(path string) (dir uintptr, err error) { | ||
// We implent opendir so that we don't have to open a file, duplicate | ||
// it's FD, then call fdopendir with it. | ||
p, err := syscall.BytePtrFromString(path) | ||
if err != nil { | ||
return 0, err | ||
} | ||
r0, _, e1 := syscall_syscallPtr(libc_opendir_trampoline_addr, uintptr(unsafe.Pointer(p)), 0, 0) | ||
dir = uintptr(r0) | ||
if e1 != 0 { | ||
err = e1 | ||
} | ||
return dir, err | ||
} | ||
|
||
var libc_opendir_trampoline_addr uintptr | ||
|
||
//go:cgo_import_dynamic libc_opendir opendir "/usr/lib/libSystem.B.dylib" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
//go:build go1.13 | ||
// +build go1.13 | ||
|
||
#include "textflag.h" | ||
|
||
TEXT libc_closedir_trampoline<>(SB),NOSPLIT,$0-0 | ||
JMP libc_closedir(SB) | ||
|
||
GLOBL ·libc_closedir_trampoline_addr(SB), RODATA, $8 | ||
DATA ·libc_closedir_trampoline_addr(SB)/8, $libc_closedir_trampoline<>(SB) | ||
|
||
TEXT libc_readdir_r_trampoline<>(SB),NOSPLIT,$0-0 | ||
JMP libc_readdir_r(SB) | ||
|
||
GLOBL ·libc_readdir_r_trampoline_addr(SB), RODATA, $8 | ||
DATA ·libc_readdir_r_trampoline_addr(SB)/8, $libc_readdir_r_trampoline<>(SB) | ||
|
||
TEXT libc_opendir_trampoline<>(SB),NOSPLIT,$0-0 | ||
JMP libc_opendir(SB) | ||
|
||
GLOBL ·libc_opendir_trampoline_addr(SB), RODATA, $8 | ||
DATA ·libc_opendir_trampoline_addr(SB)/8, $libc_opendir_trampoline<>(SB) | ||
|
||
TEXT libc___getdirentries64_trampoline<>(SB),NOSPLIT,$0-0 | ||
JMP libc___getdirentries64(SB) | ||
|
||
GLOBL ·libc___getdirentries64_trampoline_addr(SB), RODATA, $8 | ||
DATA ·libc___getdirentries64_trampoline_addr(SB)/8, $libc___getdirentries64_trampoline<>(SB) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
//go:build go1.13 | ||
// +build go1.13 | ||
|
||
#include "textflag.h" | ||
|
||
TEXT libc_closedir_trampoline<>(SB),NOSPLIT,$0-0 | ||
JMP libc_closedir(SB) | ||
|
||
GLOBL ·libc_closedir_trampoline_addr(SB), RODATA, $8 | ||
DATA ·libc_closedir_trampoline_addr(SB)/8, $libc_closedir_trampoline<>(SB) | ||
|
||
TEXT libc_readdir_r_trampoline<>(SB),NOSPLIT,$0-0 | ||
JMP libc_readdir_r(SB) | ||
|
||
GLOBL ·libc_readdir_r_trampoline_addr(SB), RODATA, $8 | ||
DATA ·libc_readdir_r_trampoline_addr(SB)/8, $libc_readdir_r_trampoline<>(SB) | ||
|
||
TEXT libc_opendir_trampoline<>(SB),NOSPLIT,$0-0 | ||
JMP libc_opendir(SB) | ||
|
||
GLOBL ·libc_opendir_trampoline_addr(SB), RODATA, $8 | ||
DATA ·libc_opendir_trampoline_addr(SB)/8, $libc_opendir_trampoline<>(SB) | ||
|
||
TEXT libc___getdirentries64_trampoline<>(SB),NOSPLIT,$0-0 | ||
JMP libc___getdirentries64(SB) | ||
|
||
GLOBL ·libc___getdirentries64_trampoline_addr(SB), RODATA, $8 | ||
DATA ·libc___getdirentries64_trampoline_addr(SB)/8, $libc___getdirentries64_trampoline<>(SB) |