Commit 03081e9d authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Again: use multiple loops rather than goroutine spawning. This allows

the request to be reused, thus reducing garbage generation.
    
Back to old speed (10% slower than libfuse).
parent 1b34b381
package fuse package fuse
import ( import (
"fmt"
"log" "log"
"os" "os"
"strings" "strings"
...@@ -42,6 +43,8 @@ type MountState struct { ...@@ -42,6 +43,8 @@ type MountState struct {
readPool [][]byte readPool [][]byte
reqReaders int reqReaders int
outstandingReadBufs int outstandingReadBufs int
loops sync.WaitGroup
} }
func (ms *MountState) KernelSettings() raw.InitIn { func (ms *MountState) KernelSettings() raw.InitIn {
...@@ -120,6 +123,8 @@ func (ms *MountState) Unmount() (err error) { ...@@ -120,6 +123,8 @@ func (ms *MountState) Unmount() (err error) {
delay = 2*delay + 5*time.Millisecond delay = 2*delay + 5*time.Millisecond
time.Sleep(delay) time.Sleep(delay)
} }
// Wait for event loops to exit.
ms.loops.Wait()
ms.mountPoint = "" ms.mountPoint = ""
return err return err
} }
...@@ -147,14 +152,31 @@ func (ms *MountState) OperationCounts() map[string]int { ...@@ -147,14 +152,31 @@ func (ms *MountState) OperationCounts() map[string]int {
} }
func (ms *MountState) BufferPoolStats() string { func (ms *MountState) BufferPoolStats() string {
return ms.buffers.String() s := ms.buffers.String()
var r int
ms.reqMu.Lock()
r = len(ms.readPool) + ms.reqReaders
ms.reqMu.Unlock()
s += fmt.Sprintf("read buffers: %d (sz %d )",
r, ms.opts.MaxWrite/PAGESIZE + 1)
return s
} }
const _MAX_READERS = 10 // What is a good number? Maybe the number of CPUs?
func (ms *MountState) readRequest() (req *request, code Status) { const _MAX_READERS = 2
// Returns a new request, or error. In case exitIdle is given, returns
// nil, OK if we have too many readers already.
func (ms *MountState) readRequest(exitIdle bool) (req *request, code Status) {
var dest []byte var dest []byte
ms.reqMu.Lock() ms.reqMu.Lock()
if ms.reqReaders > _MAX_READERS {
ms.reqMu.Unlock()
return nil, OK
}
l := len(ms.reqPool) l := len(ms.reqPool)
if l > 0 { if l > 0 {
req = ms.reqPool[l-1] req = ms.reqPool[l-1]
...@@ -167,7 +189,7 @@ func (ms *MountState) readRequest() (req *request, code Status) { ...@@ -167,7 +189,7 @@ func (ms *MountState) readRequest() (req *request, code Status) {
dest = ms.readPool[l-1] dest = ms.readPool[l-1]
ms.readPool = ms.readPool[:l-1] ms.readPool = ms.readPool[:l-1]
} else { } else {
dest = make([]byte, ms.opts.MaxWrite + 4096) dest = make([]byte, ms.opts.MaxWrite + PAGESIZE)
} }
ms.outstandingReadBufs++ ms.outstandingReadBufs++
ms.reqReaders++ ms.reqReaders++
...@@ -192,6 +214,10 @@ func (ms *MountState) readRequest() (req *request, code Status) { ...@@ -192,6 +214,10 @@ func (ms *MountState) readRequest() (req *request, code Status) {
dest = nil dest = nil
} }
ms.reqReaders-- ms.reqReaders--
if ms.reqReaders <= 0 {
ms.loops.Add(1)
go ms.loop(true)
}
ms.reqMu.Unlock() ms.reqMu.Unlock()
return req, OK return req, OK
...@@ -235,16 +261,22 @@ func (ms *MountState) recordStats(req *request) { ...@@ -235,16 +261,22 @@ func (ms *MountState) recordStats(req *request) {
// //
// Each filesystem operation executes in a separate goroutine. // Each filesystem operation executes in a separate goroutine.
func (ms *MountState) Loop() { func (ms *MountState) Loop() {
ms.loop() ms.loops.Add(1)
ms.loop(false)
ms.loops.Wait()
ms.mountFile.Close() ms.mountFile.Close()
} }
func (ms *MountState) loop() { func (ms *MountState) loop(exitIdle bool) {
defer ms.loops.Done()
exit: exit:
for { for {
req, errNo := ms.readRequest() req, errNo := ms.readRequest(exitIdle)
switch errNo { switch errNo {
case OK: case OK:
if req == nil {
break exit
}
case ENOENT: case ENOENT:
continue continue
case ENODEV: case ENODEV:
...@@ -252,13 +284,13 @@ func (ms *MountState) loop() { ...@@ -252,13 +284,13 @@ func (ms *MountState) loop() {
break exit break exit
default: // some other error? default: // some other error?
log.Printf("Failed to read from fuse conn: %v", errNo) log.Printf("Failed to read from fuse conn: %v", errNo)
break break exit
} }
if ms.latencies != nil { if ms.latencies != nil {
req.startNs = time.Now().UnixNano() req.startNs = time.Now().UnixNano()
} }
go ms.handleRequest(req) ms.handleRequest(req)
} }
} }
......
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