From 3e822e5138238b6cb612942f6397bc513ace358d Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sat, 18 Jun 2016 05:02:51 -0400 Subject: Use a pool of byte arrays to reduce GC pressure. --- proto/io.go | 57 +++++++++++++++++++++++++++++++++-- proto/server/func_handlerequest.go.sh | 4 +++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/proto/io.go b/proto/io.go index 34421b1..0f231c8 100644 --- a/proto/io.go +++ b/proto/io.go @@ -23,6 +23,7 @@ import ( "io" "net" "reflect" + "sync" "syscall" ) @@ -41,6 +42,7 @@ type nslcdObjectPtr interface { } type String []byte + func (s String) String() string { return string(s) } @@ -54,7 +56,7 @@ func (data String) nslcdWrite(fd io.Writer) { func (data *String) nslcdRead(fd io.Reader) { var len int32 Read(fd, &len) - buf := make([]byte, len) + buf := makeBytes(len) Read(fd, &buf) *data = String(buf) } @@ -94,7 +96,7 @@ func Write(fd io.Writer, data interface{}) { } var bytes []byte if af < 0 { - bytes = make([]byte, 0) + bytes = makeBytes(0) } else { bytes = data } @@ -163,7 +165,7 @@ func Read(fd io.Reader, data interface{}) { if len != _len { panic(NslcdError(fmt.Sprintf("address length incorrect: %d", len))) } - buf := make([]byte, len) + buf := makeBytes(len) Read(fd, &buf) *data = buf case *[]net.IP: @@ -184,3 +186,52 @@ func Read(fd io.Reader, data interface{}) { } } } + +// "Free" an object and its members, returning them to various pools. +// This keeps the GC sane. +func Free(data interface{}) { + switch data := data.(type) { + case *[]byte: + bytePool.Put(*data) + case *int32: + // do nothing + case *String: + bytes := []byte(*data) + bytePool.Put(bytes) + case *[]String: + for _, str := range *data { + Free(&str) + } + case *net.IP: + bytes := []byte(*data) + bytePool.Put(bytes) + case *[]net.IP: + for _, ip := range *data { + Free(&ip) + } + default: + p := reflect.ValueOf(data) + v := reflect.Indirect(p) + if p == v || v.Kind() != reflect.Struct { + panic(fmt.Sprintf("The argument to nslcd_proto.Free() must be a pointer: %T ( %#v )", data, data)) + } + for i, n := 0, v.NumField(); i < n; i++ { + Free(v.Field(i).Addr().Interface()) + } + } +} + +var bytePool = sync.Pool{ + New: func() interface{} { + return make([]byte, 0, 256) + }, +} + +func makeBytes(l int32) []byte { + buf := bytePool.Get().([]byte) + if cap(buf) < int(l) { + bytePool.Put(buf) + buf = make([]byte, l) + } + return buf[:l] +} diff --git a/proto/server/func_handlerequest.go.sh b/proto/server/func_handlerequest.go.sh index 0dffe39..ea07439 100755 --- a/proto/server/func_handlerequest.go.sh +++ b/proto/server/func_handlerequest.go.sh @@ -75,6 +75,7 @@ while read -r request; do echo '_req := req' echo '_req.Password = sensitive' echo 'fmt.Fprintf(os.Stderr, "Request: %#v\n", _req)' + echo 'p.Free(&_req)' ;; PAM_PwMod) echo '_req := req' @@ -83,11 +84,13 @@ while read -r request; do echo '}' echo '_req.NewPassword = sensitive' echo 'fmt.Fprintf(os.Stderr, "Request: %#v\n", _req)' + echo 'p.Free(&_req)' ;; PAM_UserMod) echo '_req := req' echo '_req.Password = sensitive' echo 'fmt.Fprintf(os.Stderr, "Request: %#v\n", _req)' + echo 'p.Free(&_req)' ;; *) echo 'fmt.Fprintf(os.Stderr, "Request: %#v\n", req)' @@ -96,6 +99,7 @@ while read -r request; do ) _ch := backend.${request}(cred, req) go func() { + defer p.Free(&req) defer close(ch) for obj := range _ch { ch <- obj -- cgit v1.2.3