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

Switch on permanent caching for readonly branches.

Speed-up: around 40% on ls -lR.

Add benchmark.
parent e90d3868
#!/bin/sh
# Benchmark to test speedup for caching in the r/o layer.
export GOMAXPROCS=$(grep ^processor /proc/cpuinfo|wc -l)
set -eux
fusermount -u /tmp/zipunion || true
fusermount -u /tmp/zipbench || true
gomake -C example/unionfs
gomake -C example/zipfs
mkdir -p /tmp/zipbench
./example/zipfs/zipfs /tmp/zipbench /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0/src.zip &
sleep 1
mkdir -p /tmp/ziprw /tmp/zipunion
./example/unionfs/unionfs /tmp/zipunion /tmp/ziprw /tmp/zipbench &
sleep 1
wc /tmp/zipunion/javax/lang/model/element/UnknownAnnotationValueException.java
echo hello >> /tmp/zipunion/javax/lang/model/element/UnknownAnnotationValueException.java
# Heat caches.
time ls -lR /tmp/zipbench/ > /dev/null
sleep 1s
time ls -lR /tmp/zipunion/ > /dev/null
sleep 5s
time ls -lR /tmp/zipunion/ > /dev/null
...@@ -15,9 +15,8 @@ type cacheEntry struct { ...@@ -15,9 +15,8 @@ type cacheEntry struct {
expiryNs int64 expiryNs int64
} }
// TimedIntCache caches the result of fetch() for some time. // TimedIntCache caches the result of fetch() for some time. It is
// // thread-safe.
// Oh, how I wish we had generics.
type TimedCache struct { type TimedCache struct {
fetch func(name string) interface{} fetch func(name string) interface{}
...@@ -32,6 +31,8 @@ type TimedCache struct { ...@@ -32,6 +31,8 @@ type TimedCache struct {
const layerCacheTimeoutNs = 1e9 const layerCacheTimeoutNs = 1e9
// Creates a new cache with the given TTL. If TTL <= 0, the caching is
// indefinite.
func NewTimedCache(fetcher func(name string) interface{}, ttlNs int64) *TimedCache { func NewTimedCache(fetcher func(name string) interface{}, ttlNs int64) *TimedCache {
l := new(TimedCache) l := new(TimedCache)
l.ttlNs = ttlNs l.ttlNs = ttlNs
...@@ -45,8 +46,8 @@ func (me *TimedCache) Get(name string) interface{} { ...@@ -45,8 +46,8 @@ func (me *TimedCache) Get(name string) interface{} {
info, ok := me.cacheMap[name] info, ok := me.cacheMap[name]
me.cacheMapMutex.RUnlock() me.cacheMapMutex.RUnlock()
now := time.Nanoseconds() valid := ok && (me.ttlNs <= 0 || info.expiryNs > time.Nanoseconds())
if ok && info.expiryNs > now { if valid {
return info.data return info.data
} }
return me.GetFresh(name) return me.GetFresh(name)
...@@ -93,8 +94,12 @@ func (me *TimedCache) Purge() { ...@@ -93,8 +94,12 @@ func (me *TimedCache) Purge() {
} }
func (me *TimedCache) RecurringPurge() { func (me *TimedCache) RecurringPurge() {
if (me.ttlNs <= 0) {
return
}
me.Purge() me.Purge()
me.PurgeTimer = time.AfterFunc(5*me.ttlNs, me.PurgeTimer = time.AfterFunc(me.ttlNs * 5,
func() { me.RecurringPurge() }) func() { me.RecurringPurge() })
} }
......
...@@ -69,6 +69,8 @@ type UnionFs struct { ...@@ -69,6 +69,8 @@ type UnionFs struct {
// The same, but as interfaces. // The same, but as interfaces.
fileSystems []fuse.FileSystem fileSystems []fuse.FileSystem
cachingFileSystems []*CachingFileSystem
// A file-existence cache. // A file-existence cache.
deletionCache *DirCache deletionCache *DirCache
...@@ -93,12 +95,19 @@ func NewUnionFs(roots []string, options UnionFsOptions) *UnionFs { ...@@ -93,12 +95,19 @@ func NewUnionFs(roots []string, options UnionFsOptions) *UnionFs {
g.roots = make([]string, len(roots)) g.roots = make([]string, len(roots))
copy(g.roots, roots) copy(g.roots, roots)
g.options = &options g.options = &options
for _, r := range roots { for i, r := range roots {
var fs fuse.FileSystem
pt := fuse.NewLoopbackFileSystem(r) pt := fuse.NewLoopbackFileSystem(r)
g.branches = append(g.branches, pt) g.branches = append(g.branches, pt)
// We could use some sort of caching file system here. fs = pt
g.fileSystems = append(g.fileSystems, fuse.FileSystem(pt)) if i > 0 {
cfs := NewCachingFileSystem(pt, 0)
g.cachingFileSystems = append(g.cachingFileSystems, cfs)
fs = cfs
}
g.fileSystems = append(g.fileSystems, fs)
} }
deletionDir := g.deletionDir() deletionDir := g.deletionDir()
...@@ -542,8 +551,11 @@ func (me *UnionFs) GetAttr(name string) (a *os.FileInfo, s fuse.Status) { ...@@ -542,8 +551,11 @@ func (me *UnionFs) GetAttr(name string) (a *os.FileInfo, s fuse.Status) {
} }
if name == _DROP_CACHE { if name == _DROP_CACHE {
log.Println("Forced cache drop on", me.roots) log.Println("Forced cache drop on", me.roots)
me.branchCache.Purge() me.branchCache.DropAll()
me.deletionCache.DropCache() me.deletionCache.DropCache()
for _, fs := range me.cachingFileSystems {
fs.DropCache()
}
return nil, fuse.ENOENT return nil, fuse.ENOENT
} }
if name == me.options.DeletionDirName { if name == me.options.DeletionDirName {
......
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