Commit c1a117cc authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Optimize pathInode.GetPath().

* Hand code a reverse string join.
* Do not use defer.

Speed up: 7%
parent fff4a3ab
...@@ -4,9 +4,8 @@ GO-FUSE: native bindings for the FUSE kernel module. ...@@ -4,9 +4,8 @@ GO-FUSE: native bindings for the FUSE kernel module.
HIGHLIGHTS HIGHLIGHTS
* High speed: about 50% slower than libfuse, using the gc * High speed: as fast as libfuse using the gc compiler for single
compiler. For most real world applications, the difference will be threaded loads.
negligible.
* Supports in-process mounting of different FileSystems onto * Supports in-process mounting of different FileSystems onto
subdirectories of the FUSE mount. subdirectories of the FUSE mount.
...@@ -80,9 +79,9 @@ interface, all kernel caching turned off, median stat time: ...@@ -80,9 +79,9 @@ interface, all kernel caching turned off, median stat time:
platform libfuse Go-FUSE difference (%) platform libfuse Go-FUSE difference (%)
Lenovo T60/Fedora17 (1cpu) 349us 379us 9% slower Lenovo T60/Fedora17 (1cpu) 349us 355us 2% slower
Lenovo T400/Lucid (1cpu) 133us 140us 5% slower //Lenovo T400/Lucid (1cpu) 133us 140us 5% slower
Dell T3500/Lucid (1cpu) 78us 78us 0% //Dell T3500/Lucid (1cpu) 78us 78us 0%
CREDITS CREDITS
......
...@@ -8,7 +8,6 @@ import ( ...@@ -8,7 +8,6 @@ import (
"log" "log"
"os" "os"
"reflect" "reflect"
"strings"
"syscall" "syscall"
"time" "time"
"unsafe" "unsafe"
...@@ -88,14 +87,6 @@ func Version() string { ...@@ -88,14 +87,6 @@ func Version() string {
return "unknown" return "unknown"
} }
func ReverseJoin(rev_components []string, sep string) string {
components := make([]string, len(rev_components))
for i, v := range rev_components {
components[len(rev_components)-i-1] = v
}
return strings.Join(components, sep)
}
func CurrentOwner() *Owner { func CurrentOwner() *Owner {
return &Owner{ return &Owner{
Uid: uint32(os.Getuid()), Uid: uint32(os.Getuid()),
......
...@@ -49,3 +49,4 @@ func TestLinkAt(t *testing.T) { ...@@ -49,3 +49,4 @@ func TestLinkAt(t *testing.T) {
t.Fatal("Ino mismatch", s1, s2) t.Fatal("Ino mismatch", s1, s2)
} }
} }
...@@ -219,18 +219,42 @@ func (n *pathInode) RLockTree() func() { ...@@ -219,18 +219,42 @@ func (n *pathInode) RLockTree() func() {
// GetPath returns the path relative to the mount governing this // GetPath returns the path relative to the mount governing this
// inode. It returns nil for mount if the file was deleted or the // inode. It returns nil for mount if the file was deleted or the
// filesystem unmounted. // filesystem unmounted.
func (n *pathInode) GetPath() (path string) { func (n *pathInode) GetPath() string {
defer n.RLockTree()() if n == n.pathFs.root {
return ""
}
pathLen := 0
rev_components := make([]string, 0, 10) // The simple solution is to collect names, and reverse join
// them, them, but since this is a hot path, we take some
// effort to avoid allocations.
n.pathFs.pathLock.RLock()
p := n p := n
for ; p.Parent != nil; p = p.Parent { for ; p.Parent != nil; p = p.Parent {
rev_components = append(rev_components, p.Name) pathLen += len(p.Name) + 1
} }
pathLen--
if p != p.pathFs.root { if p != p.pathFs.root {
n.pathFs.pathLock.RUnlock()
return ".deleted" return ".deleted"
} }
path = ReverseJoin(rev_components, "/")
pathBytes := make([]byte, pathLen)
end := len(pathBytes)
for p = n; p.Parent != nil; p = p.Parent {
l := len(p.Name)
copy(pathBytes[end - l:], p.Name)
end -= len(p.Name) + 1
if end > 0 {
pathBytes[end] = '/'
}
}
n.pathFs.pathLock.RUnlock()
path := string(pathBytes)
if n.pathFs.Debug { if n.pathFs.Debug {
log.Printf("Inode %d = %q (%s)", n.Inode().nodeId, path, n.fs.String()) log.Printf("Inode %d = %q (%s)", n.Inode().nodeId, path, n.fs.String())
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment