Просмотр исходного кода

Avoid unnecessary net.Addr string allocations

Clients use maps of strings to keep track of connections, while the
ServerSelector interface returns net.Addrs.  To map a net.Addr from the
selector to a connection pool, `String()` is called.  And if a new
connection is necessary, `Network()` must be called as well.  This means
that, even if you have only one memcache backend and many persistent
connections, you still end up regenerating its address, and allocating a
string for it, over and over.

This commit introduces a simple struct which acts as a wrapper to a
net.Addr, caching its `String()` and `Network()` values.  If your
memcache client produces any significant traffic, this will avoid
potentially many allocations.

Before:
BenchmarkOnItem    300000        4317 ns/op       160 B/op  20 allocs/op

Now:
BenchmarkOnItem   1000000        1396 ns/op         0 B/op   0 allocs/op
Jed Denlea 10 лет назад
Родитель
Сommit
100439223a
2 измененных файлов с 50 добавлено и 2 удалено
  1. 33 0
      memcache/memcache_test.go
  2. 17 2
      memcache/selector.go

+ 33 - 0
memcache/memcache_test.go

@@ -18,7 +18,10 @@ limitations under the License.
 package memcache
 
 import (
+	"bufio"
 	"fmt"
+	"io"
+	"io/ioutil"
 	"net"
 	"os"
 	"os/exec"
@@ -228,3 +231,33 @@ func testTouchWithClient(t *testing.T, c *Client) {
 		}
 	}
 }
+
+func BenchmarkOnItem(b *testing.B) {
+	fakeServer, err := net.Listen("tcp", "localhost:0")
+	if err != nil {
+		b.Fatal("Could not open fake server: ", err)
+	}
+	defer fakeServer.Close()
+	go func() {
+		for {
+			if c, err := fakeServer.Accept(); err == nil {
+				go func() { io.Copy(ioutil.Discard, c) }()
+			} else {
+				return
+			}
+		}
+	}()
+
+	addr := fakeServer.Addr()
+	c := New(addr.String())
+	if _, err := c.getConn(addr); err != nil {
+		b.Fatal("failed to initialize connection to fake server")
+	}
+
+	item := Item{Key: "foo"}
+	dummyFn := func(_ *Client, _ *bufio.ReadWriter, _ *Item) error { return nil }
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		c.onItem(&item, dummyFn)
+	}
+}

+ 17 - 2
memcache/selector.go

@@ -41,6 +41,21 @@ type ServerList struct {
 	addrs []net.Addr
 }
 
+// staticAddr caches the Network() and String() values from any net.Addr.
+type staticAddr struct {
+	ntw, str string
+}
+
+func newStaticAddr(a net.Addr) net.Addr {
+	return &staticAddr{
+		ntw: a.Network(),
+		str: a.String(),
+	}
+}
+
+func (s *staticAddr) Network() string { return s.ntw }
+func (s *staticAddr) String() string  { return s.str }
+
 // SetServers changes a ServerList's set of servers at runtime and is
 // safe for concurrent use by multiple goroutines.
 //
@@ -58,13 +73,13 @@ func (ss *ServerList) SetServers(servers ...string) error {
 			if err != nil {
 				return err
 			}
-			naddr[i] = addr
+			naddr[i] = newStaticAddr(addr)
 		} else {
 			tcpaddr, err := net.ResolveTCPAddr("tcp", server)
 			if err != nil {
 				return err
 			}
-			naddr[i] = tcpaddr
+			naddr[i] = newStaticAddr(tcpaddr)
 		}
 	}