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 {
expiryNs int64
}
// TimedIntCache caches the result of fetch() for some time.
//
// Oh, how I wish we had generics.
// TimedIntCache caches the result of fetch() for some time. It is
// thread-safe.
type TimedCache struct {
fetch func(name string) interface{}
......@@ -32,6 +31,8 @@ type TimedCache struct {
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 {
l := new(TimedCache)
l.ttlNs = ttlNs
......@@ -45,8 +46,8 @@ func (me *TimedCache) Get(name string) interface{} {
info, ok := me.cacheMap[name]
me.cacheMapMutex.RUnlock()
now := time.Nanoseconds()
if ok && info.expiryNs > now {
valid := ok && (me.ttlNs <= 0 || info.expiryNs > time.Nanoseconds())
if valid {
return info.data
}
return me.GetFresh(name)
......@@ -93,8 +94,12 @@ func (me *TimedCache) Purge() {
}
func (me *TimedCache) RecurringPurge() {
if (me.ttlNs <= 0) {
return
}
me.Purge()
me.PurgeTimer = time.AfterFunc(5*me.ttlNs,
me.PurgeTimer = time.AfterFunc(me.ttlNs * 5,
func() { me.RecurringPurge() })
}
......
......@@ -69,6 +69,8 @@ type UnionFs struct {
// The same, but as interfaces.
fileSystems []fuse.FileSystem
cachingFileSystems []*CachingFileSystem
// A file-existence cache.
deletionCache *DirCache
......@@ -93,12 +95,19 @@ func NewUnionFs(roots []string, options UnionFsOptions) *UnionFs {
g.roots = make([]string, len(roots))
copy(g.roots, roots)
g.options = &options
for _, r := range roots {
for i, r := range roots {
var fs fuse.FileSystem
pt := fuse.NewLoopbackFileSystem(r)
g.branches = append(g.branches, pt)
// We could use some sort of caching file system here.
g.fileSystems = append(g.fileSystems, fuse.FileSystem(pt))
fs = 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()
......@@ -542,8 +551,11 @@ func (me *UnionFs) GetAttr(name string) (a *os.FileInfo, s fuse.Status) {
}
if name == _DROP_CACHE {
log.Println("Forced cache drop on", me.roots)
me.branchCache.Purge()
me.branchCache.DropAll()
me.deletionCache.DropCache()
for _, fs := range me.cachingFileSystems {
fs.DropCache()
}
return nil, fuse.ENOENT
}
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