summaryrefslogtreecommitdiffhomepage
path: root/pkg/p9/messages.go
diff options
context:
space:
mode:
authorFabricio Voznika <fvoznika@google.com>2019-04-29 15:32:45 -0700
committerShentubot <shentubot@google.com>2019-04-29 15:33:47 -0700
commitddab854b9a895603664fa4abfa525f6a29047083 (patch)
tree349b428a042c7e7e0d7de71e56a319f7df3ae29b /pkg/p9/messages.go
parent4d52a5520101a88424fb63dd99412a1db33fbd06 (diff)
Reduce memory allocations on serving path
Cache last used messages and reuse them for subsequent requests. If more messages are needed, they are created outside the cache on demand. PiperOrigin-RevId: 245836910 Change-Id: Icf099ddff95df420db8e09f5cdd41dcdce406c61
Diffstat (limited to 'pkg/p9/messages.go')
-rw-r--r--pkg/p9/messages.go232
1 files changed, 131 insertions, 101 deletions
diff --git a/pkg/p9/messages.go b/pkg/p9/messages.go
index 833defbd6..3c7898cc1 100644
--- a/pkg/p9/messages.go
+++ b/pkg/p9/messages.go
@@ -193,6 +193,7 @@ func (t *Twalk) Decode(b *buffer) {
t.FID = b.ReadFID()
t.NewFID = b.ReadFID()
n := b.Read16()
+ t.Names = t.Names[:0]
for i := 0; i < int(n); i++ {
t.Names = append(t.Names, b.ReadString())
}
@@ -227,6 +228,7 @@ type Rwalk struct {
// Decode implements encoder.Decode.
func (r *Rwalk) Decode(b *buffer) {
n := b.Read16()
+ r.QIDs = r.QIDs[:0]
for i := 0; i < int(n); i++ {
var q QID
q.Decode(b)
@@ -1608,6 +1610,7 @@ type Rreaddir struct {
func (r *Rreaddir) Decode(b *buffer) {
r.Count = b.Read32()
entriesBuf := buffer{data: r.payload}
+ r.Entries = r.Entries[:0]
for {
var d Dirent
d.Decode(&entriesBuf)
@@ -1827,6 +1830,7 @@ func (t *Twalkgetattr) Decode(b *buffer) {
t.FID = b.ReadFID()
t.NewFID = b.ReadFID()
n := b.Read16()
+ t.Names = t.Names[:0]
for i := 0; i < int(n); i++ {
t.Names = append(t.Names, b.ReadString())
}
@@ -1869,6 +1873,7 @@ func (r *Rwalkgetattr) Decode(b *buffer) {
r.Valid.Decode(b)
r.Attr.Decode(b)
n := b.Read16()
+ r.QIDs = r.QIDs[:0]
for i := 0; i < int(n); i++ {
var q QID
q.Decode(b)
@@ -2139,34 +2144,80 @@ func (r *Rlconnect) String() string {
return fmt.Sprintf("Rlconnect{File: %v}", r.File)
}
-// messageRegistry indexes all messages by type.
-var messageRegistry = make([]func() message, math.MaxUint8)
+const maxCacheSize = 3
-// messageByType creates a new message by type.
+// msgFactory is used to reduce allocations by caching messages for reuse.
+type msgFactory struct {
+ create func() message
+ cache chan message
+}
+
+// msgRegistry indexes all message factories by type.
+var msgRegistry registry
+
+type registry struct {
+ factories [math.MaxUint8]msgFactory
+
+ // largestFixedSize is computed so that given some message size M, you can
+ // compute the maximum payload size (e.g. for Twrite, Rread) with
+ // M-largestFixedSize. You could do this individual on a per-message basis,
+ // but it's easier to compute a single maximum safe payload.
+ largestFixedSize uint32
+}
+
+// get returns a new message by type.
//
// An error is returned in the case of an unknown message.
//
// This takes, and ignores, a message tag so that it may be used directly as a
// lookupTagAndType function for recv (by design).
-func messageByType(_ Tag, t MsgType) (message, error) {
- fn := messageRegistry[t]
- if fn == nil {
+func (r *registry) get(_ Tag, t MsgType) (message, error) {
+ entry := &r.factories[t]
+ if entry.create == nil {
return nil, &ErrInvalidMsgType{t}
}
- return fn(), nil
+
+ select {
+ case msg := <-entry.cache:
+ return msg, nil
+ default:
+ return entry.create(), nil
+ }
+}
+
+func (r *registry) put(msg message) {
+ if p, ok := msg.(payloader); ok {
+ p.SetPayload(nil)
+ }
+ if f, ok := msg.(filer); ok {
+ f.SetFilePayload(nil)
+ }
+
+ entry := &r.factories[msg.Type()]
+ select {
+ case entry.cache <- msg:
+ default:
+ }
}
// register registers the given message type.
//
// This may cause panic on failure and should only be used from init.
-func register(t MsgType, fn func() message) {
- if int(t) >= len(messageRegistry) {
- panic(fmt.Sprintf("message type %d is too large. It must be smaller than %d", t, len(messageRegistry)))
+func (r *registry) register(t MsgType, fn func() message) {
+ if int(t) >= len(r.factories) {
+ panic(fmt.Sprintf("message type %d is too large. It must be smaller than %d", t, len(r.factories)))
}
- if messageRegistry[t] != nil {
- panic(fmt.Sprintf("duplicate message type %d: first is %T, second is %T", t, messageRegistry[t](), fn()))
+ if r.factories[t].create != nil {
+ panic(fmt.Sprintf("duplicate message type %d: first is %T, second is %T", t, r.factories[t].create(), fn()))
+ }
+ r.factories[t] = msgFactory{
+ create: fn,
+ cache: make(chan message, maxCacheSize),
+ }
+
+ if size := calculateSize(fn()); size > r.largestFixedSize {
+ r.largestFixedSize = size
}
- messageRegistry[t] = fn
}
func calculateSize(m message) uint32 {
@@ -2178,93 +2229,72 @@ func calculateSize(m message) uint32 {
return uint32(len(dataBuf.data))
}
-// largestFixedSize is computed within calculateLargestSize.
-//
-// This is computed so that given some message size M, you can compute
-// the maximum payload size (e.g. for Twrite, Rread) with M-largestFixedSize.
-// You could do this individual on a per-message basis, but it's easier to
-// compute a single maximum safe payload.
-var largestFixedSize uint32
-
-// calculateLargestFixedSize is called from within init.
-func calculateLargestFixedSize() {
- for _, fn := range messageRegistry {
- if fn != nil {
- if size := calculateSize(fn()); size > largestFixedSize {
- largestFixedSize = size
- }
- }
- }
-}
-
func init() {
- register(MsgRlerror, func() message { return &Rlerror{} })
- register(MsgTstatfs, func() message { return &Tstatfs{} })
- register(MsgRstatfs, func() message { return &Rstatfs{} })
- register(MsgTlopen, func() message { return &Tlopen{} })
- register(MsgRlopen, func() message { return &Rlopen{} })
- register(MsgTlcreate, func() message { return &Tlcreate{} })
- register(MsgRlcreate, func() message { return &Rlcreate{} })
- register(MsgTsymlink, func() message { return &Tsymlink{} })
- register(MsgRsymlink, func() message { return &Rsymlink{} })
- register(MsgTmknod, func() message { return &Tmknod{} })
- register(MsgRmknod, func() message { return &Rmknod{} })
- register(MsgTrename, func() message { return &Trename{} })
- register(MsgRrename, func() message { return &Rrename{} })
- register(MsgTreadlink, func() message { return &Treadlink{} })
- register(MsgRreadlink, func() message { return &Rreadlink{} })
- register(MsgTgetattr, func() message { return &Tgetattr{} })
- register(MsgRgetattr, func() message { return &Rgetattr{} })
- register(MsgTsetattr, func() message { return &Tsetattr{} })
- register(MsgRsetattr, func() message { return &Rsetattr{} })
- register(MsgTxattrwalk, func() message { return &Txattrwalk{} })
- register(MsgRxattrwalk, func() message { return &Rxattrwalk{} })
- register(MsgTxattrcreate, func() message { return &Txattrcreate{} })
- register(MsgRxattrcreate, func() message { return &Rxattrcreate{} })
- register(MsgTreaddir, func() message { return &Treaddir{} })
- register(MsgRreaddir, func() message { return &Rreaddir{} })
- register(MsgTfsync, func() message { return &Tfsync{} })
- register(MsgRfsync, func() message { return &Rfsync{} })
- register(MsgTlink, func() message { return &Tlink{} })
- register(MsgRlink, func() message { return &Rlink{} })
- register(MsgTmkdir, func() message { return &Tmkdir{} })
- register(MsgRmkdir, func() message { return &Rmkdir{} })
- register(MsgTrenameat, func() message { return &Trenameat{} })
- register(MsgRrenameat, func() message { return &Rrenameat{} })
- register(MsgTunlinkat, func() message { return &Tunlinkat{} })
- register(MsgRunlinkat, func() message { return &Runlinkat{} })
- register(MsgTversion, func() message { return &Tversion{} })
- register(MsgRversion, func() message { return &Rversion{} })
- register(MsgTauth, func() message { return &Tauth{} })
- register(MsgRauth, func() message { return &Rauth{} })
- register(MsgTattach, func() message { return &Tattach{} })
- register(MsgRattach, func() message { return &Rattach{} })
- register(MsgTflush, func() message { return &Tflush{} })
- register(MsgRflush, func() message { return &Rflush{} })
- register(MsgTwalk, func() message { return &Twalk{} })
- register(MsgRwalk, func() message { return &Rwalk{} })
- register(MsgTread, func() message { return &Tread{} })
- register(MsgRread, func() message { return &Rread{} })
- register(MsgTwrite, func() message { return &Twrite{} })
- register(MsgRwrite, func() message { return &Rwrite{} })
- register(MsgTclunk, func() message { return &Tclunk{} })
- register(MsgRclunk, func() message { return &Rclunk{} })
- register(MsgTremove, func() message { return &Tremove{} })
- register(MsgRremove, func() message { return &Rremove{} })
- register(MsgTflushf, func() message { return &Tflushf{} })
- register(MsgRflushf, func() message { return &Rflushf{} })
- register(MsgTwalkgetattr, func() message { return &Twalkgetattr{} })
- register(MsgRwalkgetattr, func() message { return &Rwalkgetattr{} })
- register(MsgTucreate, func() message { return &Tucreate{} })
- register(MsgRucreate, func() message { return &Rucreate{} })
- register(MsgTumkdir, func() message { return &Tumkdir{} })
- register(MsgRumkdir, func() message { return &Rumkdir{} })
- register(MsgTumknod, func() message { return &Tumknod{} })
- register(MsgRumknod, func() message { return &Rumknod{} })
- register(MsgTusymlink, func() message { return &Tusymlink{} })
- register(MsgRusymlink, func() message { return &Rusymlink{} })
- register(MsgTlconnect, func() message { return &Tlconnect{} })
- register(MsgRlconnect, func() message { return &Rlconnect{} })
-
- calculateLargestFixedSize()
+ msgRegistry.register(MsgRlerror, func() message { return &Rlerror{} })
+ msgRegistry.register(MsgTstatfs, func() message { return &Tstatfs{} })
+ msgRegistry.register(MsgRstatfs, func() message { return &Rstatfs{} })
+ msgRegistry.register(MsgTlopen, func() message { return &Tlopen{} })
+ msgRegistry.register(MsgRlopen, func() message { return &Rlopen{} })
+ msgRegistry.register(MsgTlcreate, func() message { return &Tlcreate{} })
+ msgRegistry.register(MsgRlcreate, func() message { return &Rlcreate{} })
+ msgRegistry.register(MsgTsymlink, func() message { return &Tsymlink{} })
+ msgRegistry.register(MsgRsymlink, func() message { return &Rsymlink{} })
+ msgRegistry.register(MsgTmknod, func() message { return &Tmknod{} })
+ msgRegistry.register(MsgRmknod, func() message { return &Rmknod{} })
+ msgRegistry.register(MsgTrename, func() message { return &Trename{} })
+ msgRegistry.register(MsgRrename, func() message { return &Rrename{} })
+ msgRegistry.register(MsgTreadlink, func() message { return &Treadlink{} })
+ msgRegistry.register(MsgRreadlink, func() message { return &Rreadlink{} })
+ msgRegistry.register(MsgTgetattr, func() message { return &Tgetattr{} })
+ msgRegistry.register(MsgRgetattr, func() message { return &Rgetattr{} })
+ msgRegistry.register(MsgTsetattr, func() message { return &Tsetattr{} })
+ msgRegistry.register(MsgRsetattr, func() message { return &Rsetattr{} })
+ msgRegistry.register(MsgTxattrwalk, func() message { return &Txattrwalk{} })
+ msgRegistry.register(MsgRxattrwalk, func() message { return &Rxattrwalk{} })
+ msgRegistry.register(MsgTxattrcreate, func() message { return &Txattrcreate{} })
+ msgRegistry.register(MsgRxattrcreate, func() message { return &Rxattrcreate{} })
+ msgRegistry.register(MsgTreaddir, func() message { return &Treaddir{} })
+ msgRegistry.register(MsgRreaddir, func() message { return &Rreaddir{} })
+ msgRegistry.register(MsgTfsync, func() message { return &Tfsync{} })
+ msgRegistry.register(MsgRfsync, func() message { return &Rfsync{} })
+ msgRegistry.register(MsgTlink, func() message { return &Tlink{} })
+ msgRegistry.register(MsgRlink, func() message { return &Rlink{} })
+ msgRegistry.register(MsgTmkdir, func() message { return &Tmkdir{} })
+ msgRegistry.register(MsgRmkdir, func() message { return &Rmkdir{} })
+ msgRegistry.register(MsgTrenameat, func() message { return &Trenameat{} })
+ msgRegistry.register(MsgRrenameat, func() message { return &Rrenameat{} })
+ msgRegistry.register(MsgTunlinkat, func() message { return &Tunlinkat{} })
+ msgRegistry.register(MsgRunlinkat, func() message { return &Runlinkat{} })
+ msgRegistry.register(MsgTversion, func() message { return &Tversion{} })
+ msgRegistry.register(MsgRversion, func() message { return &Rversion{} })
+ msgRegistry.register(MsgTauth, func() message { return &Tauth{} })
+ msgRegistry.register(MsgRauth, func() message { return &Rauth{} })
+ msgRegistry.register(MsgTattach, func() message { return &Tattach{} })
+ msgRegistry.register(MsgRattach, func() message { return &Rattach{} })
+ msgRegistry.register(MsgTflush, func() message { return &Tflush{} })
+ msgRegistry.register(MsgRflush, func() message { return &Rflush{} })
+ msgRegistry.register(MsgTwalk, func() message { return &Twalk{} })
+ msgRegistry.register(MsgRwalk, func() message { return &Rwalk{} })
+ msgRegistry.register(MsgTread, func() message { return &Tread{} })
+ msgRegistry.register(MsgRread, func() message { return &Rread{} })
+ msgRegistry.register(MsgTwrite, func() message { return &Twrite{} })
+ msgRegistry.register(MsgRwrite, func() message { return &Rwrite{} })
+ msgRegistry.register(MsgTclunk, func() message { return &Tclunk{} })
+ msgRegistry.register(MsgRclunk, func() message { return &Rclunk{} })
+ msgRegistry.register(MsgTremove, func() message { return &Tremove{} })
+ msgRegistry.register(MsgRremove, func() message { return &Rremove{} })
+ msgRegistry.register(MsgTflushf, func() message { return &Tflushf{} })
+ msgRegistry.register(MsgRflushf, func() message { return &Rflushf{} })
+ msgRegistry.register(MsgTwalkgetattr, func() message { return &Twalkgetattr{} })
+ msgRegistry.register(MsgRwalkgetattr, func() message { return &Rwalkgetattr{} })
+ msgRegistry.register(MsgTucreate, func() message { return &Tucreate{} })
+ msgRegistry.register(MsgRucreate, func() message { return &Rucreate{} })
+ msgRegistry.register(MsgTumkdir, func() message { return &Tumkdir{} })
+ msgRegistry.register(MsgRumkdir, func() message { return &Rumkdir{} })
+ msgRegistry.register(MsgTumknod, func() message { return &Tumknod{} })
+ msgRegistry.register(MsgRumknod, func() message { return &Rumknod{} })
+ msgRegistry.register(MsgTusymlink, func() message { return &Tusymlink{} })
+ msgRegistry.register(MsgRusymlink, func() message { return &Rusymlink{} })
+ msgRegistry.register(MsgTlconnect, func() message { return &Tlconnect{} })
+ msgRegistry.register(MsgRlconnect, func() message { return &Rlconnect{} })
}