Commit 0d66ca07 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

After copying the input buffer, reuse it.

parent bb1bc0b5
...@@ -145,25 +145,11 @@ func (me *MountState) BufferPoolStats() string { ...@@ -145,25 +145,11 @@ func (me *MountState) BufferPoolStats() string {
func (me *MountState) newRequest() *request { func (me *MountState) newRequest() *request {
r := &request{ r := &request{
status: OK,
pool: me.buffers, pool: me.buffers,
bufferPoolInputBuf: me.buffers.AllocBuffer(uint32(me.opts.MaxWrite + 4096)),
} }
r.inputBuf = r.bufferPoolInputBuf
return r return r
} }
func (me *MountState) readRequest(req *request) Status {
n, err := me.mountFile.Read(req.inputBuf)
// If we start timing before the read, we may take into
// account waiting for input into the timing.
if me.latencies != nil {
req.startNs = time.Now().UnixNano()
}
req.inputBuf = req.inputBuf[0:n]
return ToStatus(err)
}
func (me *MountState) recordStats(req *request) { func (me *MountState) recordStats(req *request) {
if me.latencies != nil { if me.latencies != nil {
endNs := time.Now().UnixNano() endNs := time.Now().UnixNano()
...@@ -189,10 +175,16 @@ func (me *MountState) Loop() { ...@@ -189,10 +175,16 @@ func (me *MountState) Loop() {
} }
func (me *MountState) loop() { func (me *MountState) loop() {
var dest []byte
for { for {
req := me.newRequest() if dest == nil {
errNo := me.readRequest(req) dest = me.buffers.AllocBuffer(uint32(me.opts.MaxWrite + 4096))
if !errNo.Ok() { }
n, err := me.mountFile.Read(dest)
if err != nil {
errNo := ToStatus(err)
// Retry. // Retry.
if errNo == ENOENT { if errNo == ENOENT {
continue continue
...@@ -207,6 +199,14 @@ func (me *MountState) loop() { ...@@ -207,6 +199,14 @@ func (me *MountState) loop() {
break break
} }
req := me.newRequest()
if me.latencies != nil {
req.startNs = time.Now().UnixNano()
}
if req.setInput(dest[:n]) {
dest = nil
}
// When closely analyzing timings, the context switch // When closely analyzing timings, the context switch
// generates some delay. While unfortunate, the // generates some delay. While unfortunate, the
// alternative is to have a fixed goroutine pool, // alternative is to have a fixed goroutine pool,
......
...@@ -15,18 +15,16 @@ func (req *request) Discard() { ...@@ -15,18 +15,16 @@ func (req *request) Discard() {
req.pool.FreeBuffer(req.bufferPoolInputBuf) req.pool.FreeBuffer(req.bufferPoolInputBuf)
} }
// The largest input without data is 128 (setattr). This also fits small
// requests with short filenames.
const SMALL_BUF_THRESHOLD = 128
type request struct { type request struct {
// the input, if obtained through bufferpool // the input, if obtained through bufferpool
bufferPoolInputBuf []byte bufferPoolInputBuf []byte
pool BufferPool pool BufferPool
// If we have a small input, we quickly copy it to here, // If we have a small input, we quickly copy it to here, and
// and give back the large buffer to buffer pool. // give back the large buffer to buffer pool. The largest
smallInputBuf [SMALL_BUF_THRESHOLD]byte // input without data is 128 (setattr). 128 also fits small
// requests with short filenames.
smallInputBuf [128]byte
inputBuf []byte inputBuf []byte
...@@ -103,6 +101,18 @@ func (me *request) OutputDebug() string { ...@@ -103,6 +101,18 @@ func (me *request) OutputDebug() string {
operationName(me.inHeader.Opcode), me.status, dataStr, flatStr) operationName(me.inHeader.Opcode), me.status, dataStr, flatStr)
} }
// setInput returns true if it takes ownership of the argument, false if not.
func (me *request) setInput(input []byte) bool {
if len(input) < len(me.smallInputBuf) {
copy(me.smallInputBuf[:], input)
me.inputBuf = me.smallInputBuf[:len(input)]
return false
}
me.inputBuf = input
me.bufferPoolInputBuf = input
return true
}
func (me *request) parse() { func (me *request) parse() {
inHSize := int(unsafe.Sizeof(raw.InHeader{})) inHSize := int(unsafe.Sizeof(raw.InHeader{}))
...@@ -111,16 +121,6 @@ func (me *request) parse() { ...@@ -111,16 +121,6 @@ func (me *request) parse() {
return return
} }
// We return the input buffer early if possible. Only write
// requests require a large input buffer, so if we hang onto
// the large buffer, we create unnecessary memory pressure.
if len(me.inputBuf) < SMALL_BUF_THRESHOLD {
copy(me.smallInputBuf[:], me.inputBuf)
me.inputBuf = me.smallInputBuf[:len(me.inputBuf)]
me.pool.FreeBuffer(me.bufferPoolInputBuf)
me.bufferPoolInputBuf = nil
}
me.inHeader = (*raw.InHeader)(unsafe.Pointer(&me.inputBuf[0])) me.inHeader = (*raw.InHeader)(unsafe.Pointer(&me.inputBuf[0]))
me.arg = me.inputBuf[inHSize:] me.arg = me.inputBuf[inHSize:]
......
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