Avoid calling Logf on the hot path when logging is disabled.

The call to commonOp.Logf in sendBazilfuseResponse is extremely hot for
allocations, presumably because it needs to allocate an argument slice.
It is normally not needed at all, so why pay for it?
geesefs-0-30-9
Aaron Jacobs 2015-07-22 21:45:49 +10:00
parent 4b1e4b55f9
commit f004d3ff4a
4 changed files with 40 additions and 27 deletions

View File

@ -50,6 +50,8 @@ type Connection struct {
// Responsibility for closing the wrapped connection is transferred to the // Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually. // result. You must call c.close() eventually.
//
// The loggers may be nil.
func newConnection( func newConnection(
parentCtx context.Context, parentCtx context.Context,
debugLogger *log.Logger, debugLogger *log.Logger,
@ -73,6 +75,10 @@ func (c *Connection) debugLog(
calldepth int, calldepth int,
format string, format string,
v ...interface{}) { v ...interface{}) {
if c.debugLogger == nil {
return
}
// Get file:line info. // Get file:line info.
var file string var file string
var line int var line int
@ -239,8 +245,11 @@ func (c *Connection) ReadOp() (op fuseops.Op, err error) {
// Set up op dependencies. // Set up op dependencies.
opCtx := c.beginOp(bfReq) opCtx := c.beginOp(bfReq)
debugLogForOp := func(calldepth int, format string, v ...interface{}) { var debugLogForOp func(int, string, ...interface{})
c.debugLog(opID, calldepth+1, format, v...) if c.debugLogger != nil {
debugLogForOp = func(calldepth int, format string, v ...interface{}) {
c.debugLog(opID, calldepth+1, format, v...)
}
} }
finished := func(err error) { c.finishOp(bfReq) } finished := func(err error) { c.finishOp(bfReq) }

View File

@ -49,9 +49,13 @@ type commonOp struct {
// A function that can be used to log debug information about the op. The // A function that can be used to log debug information about the op. The
// first argument is a call depth. // first argument is a call depth.
//
// May be nil.
debugLog func(int, string, ...interface{}) debugLog func(int, string, ...interface{})
// A logger to be used for logging exceptional errors. // A logger to be used for logging exceptional errors.
//
// May be nil.
errorLogger *log.Logger errorLogger *log.Logger
// A function that is invoked with the error given to Respond, for use in // A function that is invoked with the error given to Respond, for use in
@ -116,6 +120,10 @@ func (o *commonOp) Context() context.Context {
} }
func (o *commonOp) Logf(format string, v ...interface{}) { func (o *commonOp) Logf(format string, v ...interface{}) {
if o.debugLog == nil {
return
}
const calldepth = 2 const calldepth = 2
o.debugLog(calldepth, format, v...) o.debugLog(calldepth, format, v...)
} }
@ -131,15 +139,19 @@ func (o *commonOp) Respond(err error) {
} }
// Log the error. // Log the error.
o.Logf( if o.debugLog != nil {
"-> (%s) error: %v", o.Logf(
o.op.ShortDesc(), "-> (%s) error: %v",
err) o.op.ShortDesc(),
err)
}
o.errorLogger.Printf( if o.errorLogger != nil {
"(%s) error: %v", o.errorLogger.Printf(
o.op.ShortDesc(), "(%s) error: %v",
err) o.op.ShortDesc(),
err)
}
// Send a response to the kernel. // Send a response to the kernel.
o.bazilReq.RespondError(err) o.bazilReq.RespondError(err)
@ -157,11 +169,15 @@ func (o *commonOp) sendBazilfuseResponse(resp interface{}) {
// Special case: handle successful ops with no response struct. // Special case: handle successful ops with no response struct.
if resp == nil { if resp == nil {
o.Logf("-> (%s) OK", o.op.ShortDesc()) o.Logf("-> (%s) OK", o.op.ShortDesc())
respond.Call([]reflect.Value{}) respond.Call([]reflect.Value{})
return return
} }
// Otherwise, send the response struct to the kernel. // Otherwise, send the response struct to the kernel.
o.Logf("-> %v", resp) if o.debugLog != nil {
o.Logf("-> %v", resp)
}
respond.Call([]reflect.Value{reflect.ValueOf(resp)}) respond.Call([]reflect.Value{reflect.ValueOf(resp)})
} }

View File

@ -32,6 +32,8 @@ import (
// //
// It is guaranteed that o != nil. If the op is unknown, a special unexported // It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used. // type will be used.
//
// The debug logging function and error logger may be nil.
func Convert( func Convert(
opCtx context.Context, opCtx context.Context,
r bazilfuse.Request, r bazilfuse.Request,

View File

@ -16,7 +16,6 @@ package fuse
import ( import (
"fmt" "fmt"
"io/ioutil"
"log" "log"
"runtime" "runtime"
@ -193,12 +192,6 @@ func Mount(
dir string, dir string,
server Server, server Server,
config *MountConfig) (mfs *MountedFileSystem, err error) { config *MountConfig) (mfs *MountedFileSystem, err error) {
// Arrange for a non-nil debug logger.
debugLogger := config.DebugLogger
if debugLogger == nil {
debugLogger = log.New(ioutil.Discard, "", 0)
}
// Initialize the struct. // Initialize the struct.
mfs = &MountedFileSystem{ mfs = &MountedFileSystem{
dir: dir, dir: dir,
@ -206,7 +199,6 @@ func Mount(
} }
// Open a bazilfuse connection. // Open a bazilfuse connection.
debugLogger.Println("Opening a bazilfuse connection.")
bfConn, err := bazilfuse.Mount(mfs.dir, config.bazilfuseOptions()...) bfConn, err := bazilfuse.Mount(mfs.dir, config.bazilfuseOptions()...)
if err != nil { if err != nil {
err = fmt.Errorf("bazilfuse.Mount: %v", err) err = fmt.Errorf("bazilfuse.Mount: %v", err)
@ -219,17 +211,11 @@ func Mount(
opContext = context.Background() opContext = context.Background()
} }
// Create a /dev/null error logger if necessary.
errorLogger := config.ErrorLogger
if errorLogger == nil {
errorLogger = log.New(ioutil.Discard, "", 0)
}
// Create our own Connection object wrapping it. // Create our own Connection object wrapping it.
connection, err := newConnection( connection, err := newConnection(
opContext, opContext,
debugLogger, config.DebugLogger,
errorLogger, config.ErrorLogger,
bfConn) bfConn)
if err != nil { if err != nil {