Commit 3d0731da authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

fuse: reduce LatencyMap to an interface, move implementation to benchmark/

parent 5ee141ce
package fuse
package benchmark
import (
"sync"
"time"
)
type latencyMapEntry struct {
count int
ns int64
dur time.Duration
}
type LatencyMap struct {
......@@ -20,28 +21,23 @@ func NewLatencyMap() *LatencyMap {
return m
}
func (m *LatencyMap) Get(name string) (count int, dtNs int64) {
func (m *LatencyMap) Get(name string) (count int, dt time.Duration) {
m.Mutex.Lock()
l := m.stats[name]
m.Mutex.Unlock()
return l.count, l.ns
return l.count, l.dur
}
func (m *LatencyMap) Add(name string, dtNs int64) {
func (m *LatencyMap) Add(name string, dt time.Duration) {
m.Mutex.Lock()
m.add(name, dtNs)
m.Mutex.Unlock()
}
func (m *LatencyMap) add(name string, dtNs int64) {
e := m.stats[name]
if e == nil {
e = new(latencyMapEntry)
m.stats[name] = e
}
e.count++
e.ns += dtNs
e.dur += dt
m.Mutex.Unlock()
}
func (m *LatencyMap) Counts() map[string]int {
......@@ -55,17 +51,3 @@ func (m *LatencyMap) Counts() map[string]int {
return r
}
// Latencies returns a map. Use 1e-3 for unit to get ms
// results.
func (m *LatencyMap) Latencies(unit float64) map[string]float64 {
r := make(map[string]float64)
m.Mutex.Lock()
mult := 1 / (1e9 * unit)
for key, ent := range m.stats {
lat := mult * float64(ent.ns) / float64(ent.count)
r[key] = lat
}
m.Mutex.Unlock()
return r
}
package benchmark
import (
"testing"
"time"
)
func TestLatencyMap(t *testing.T) {
m := NewLatencyMap()
m.Add("foo", 100 * time.Millisecond)
m.Add("foo", 200 * time.Millisecond)
c, d := m.Get("foo")
if c != 2 || d != 300 * time.Millisecond {
t.Errorf("got %v, %d, want 2, 150ms", c, d)
}
}
......@@ -2,7 +2,6 @@ package benchmark
import (
"fmt"
"github.com/hanwen/go-fuse/fuse"
"io/ioutil"
"log"
"os"
......@@ -12,6 +11,9 @@ import (
"sort"
"testing"
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/pathfs"
)
func setupFs(fs pathfs.FileSystem) (string, func()) {
......@@ -26,16 +28,18 @@ func setupFs(fs pathfs.FileSystem) (string, func()) {
if err != nil {
panic(fmt.Sprintf("cannot mount %v", err)) // ugh - benchmark has no error methods.
}
state.SetRecordStatistics(true)
lmap := NewLatencyMap()
state.RecordLatencies(lmap)
// state.SetDebug(true)
go state.Loop()
return mountPoint, func() {
lc, lns := state.Latencies().Get("LOOKUP")
gc, gns := state.Latencies().Get("GETATTR")
fmt.Printf("GETATTR %dus/call n=%d, LOOKUP %dus/call n=%d\n",
gns/int64(1000*lc), gc,
lns/int64(1000*lc), lc)
lc, lns := lmap.Get("LOOKUP")
gc, gns := lmap.Get("GETATTR")
fmt.Printf("GETATTR %v/call n=%d, LOOKUP %v/call n=%d\n",
gns/time.Duration(gc), gc,
lns/time.Duration(lc), lc)
err := state.Unmount()
if err != nil {
......
//
// +build !cgo
// g++ -Wall `pkg-config fuse --cflags --libs` statfs.cc -o statfs
#include <unordered_map>
......
......@@ -12,7 +12,7 @@ import (
var delay = 0 * time.Microsecond
type StatFs struct {
pathfs.DefaultFileSystem
pathfs.FileSystem
entries map[string]*fuse.Attr
dirs map[string][]fuse.DirEntry
delay time.Duration
......@@ -62,6 +62,7 @@ func (me *StatFs) OpenDir(name string, context *fuse.Context) (stream []fuse.Dir
func NewStatFs() *StatFs {
return &StatFs{
FileSystem: pathfs.NewDefaultFileSystem(),
entries: make(map[string]*fuse.Attr),
dirs: make(map[string][]fuse.DirEntry),
}
......
package fuse
import (
"fmt"
"testing"
)
var _ = fmt.Println
func TestLatencyMap(t *testing.T) {
m := NewLatencyMap()
m.Add("foo", 0.1e9)
m.Add("foo", 0.2e9)
l := m.Latencies(1e-3)
if l["foo"] != 150 {
t.Error("unexpected latency", l)
}
}
......@@ -32,7 +32,7 @@ type MountState struct {
// Dump debug info onto stdout.
debug bool
latencies *LatencyMap
latencies LatencyMap
opts *MountOptions
......@@ -153,12 +153,14 @@ func (ms *MountState) Mount(mountPoint string, opts *MountOptions) error {
return nil
}
func (ms *MountState) SetRecordStatistics(record bool) {
if record {
ms.latencies = NewLatencyMap()
} else {
ms.latencies = nil
}
// This type may be provided for recording latencies of each FUSE
// operation.
type LatencyMap interface {
Add(name string, dt time.Duration)
}
func (ms *MountState) RecordLatencies(l LatencyMap) {
ms.latencies = l
}
func (ms *MountState) Unmount() (err error) {
......@@ -195,10 +197,6 @@ func NewMountState(fs RawFileSystem) *MountState {
return ms
}
func (ms *MountState) Latencies() *LatencyMap {
return ms.latencies
}
func (ms *MountState) BufferPoolStats() string {
s := ms.opts.Buffers.String()
......@@ -254,7 +252,7 @@ func (ms *MountState) readRequest(exitIdle bool) (req *request, code Status) {
}
if ms.latencies != nil {
req.startNs = time.Now().UnixNano()
req.startTime = time.Now()
}
gobbled := req.setInput(dest[:n])
......@@ -295,7 +293,7 @@ func (ms *MountState) returnRequest(req *request) {
func (ms *MountState) recordStats(req *request) {
if ms.latencies != nil {
dt := time.Now().UnixNano() - req.startNs
dt := time.Now().Sub(req.startTime)
opname := operationName(req.inHeader.Opcode)
ms.latencies.Add(opname, dt)
}
......
......@@ -5,6 +5,7 @@ import (
"fmt"
"log"
"strings"
"time"
"unsafe"
"github.com/hanwen/go-fuse/raw"
......@@ -34,8 +35,7 @@ type request struct {
readResult ReadResult
// Start timestamp for timing info.
startNs int64
preWriteNs int64
startTime time.Time
// All information pertaining to opcode of this request.
handler *operationHandler
......@@ -67,8 +67,7 @@ func (r *request) clear() {
r.status = OK
r.flatData = nil
r.fdData = nil
r.preWriteNs = 0
r.startNs = 0
r.startTime = time.Time{}
r.handler = nil
r.readResult = nil
}
......
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