Skip to content

Commit

Permalink
fastwalk: improve darwin perf by ~3x
Browse files Browse the repository at this point in the history
```
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
charlievieth committed Feb 24, 2022
1 parent 3f3c1d6 commit 91f1444
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 25 deletions.
101 changes: 81 additions & 20 deletions fastwalk/fastwalk_darwin.go
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
}
}
}
5 changes: 2 additions & 3 deletions fastwalk/fastwalk_dirent_ino.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build (linux || darwin) && !appengine
// +build linux darwin
// +build !appengine
//go:build linux && !appengine
// +build linux,!appengine

package fastwalk

Expand Down
4 changes: 2 additions & 2 deletions fastwalk/fastwalk_dirent_namlen_bsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build darwin || freebsd || openbsd || netbsd
// +build darwin freebsd openbsd netbsd
//go:build freebsd || openbsd || netbsd
// +build freebsd openbsd netbsd

package fastwalk

Expand Down
56 changes: 56 additions & 0 deletions fastwalk/zsyscall_darwin.go
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"
28 changes: 28 additions & 0 deletions fastwalk/zsyscall_darwin_amd64.1_13.s
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)
28 changes: 28 additions & 0 deletions fastwalk/zsyscall_darwin_arm64.1_13.s
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)

0 comments on commit 91f1444

Please sign in to comment.