Quellcode durchsuchen

Reduce memory allocation.

Gary Burd vor 12 Jahren
Ursprung
Commit
b94bc06734
2 geänderte Dateien mit 97 neuen und 19 gelöschten Zeilen
  1. 89 19
      redis/conn.go
  2. 8 0
      redis/conn_test.go

+ 89 - 19
redis/conn.go

@@ -45,7 +45,7 @@ type conn struct {
 
 	// Scratch space for formatting argument length.
 	// '*' or '$', length, "\r\n"
-	lenScratch [1 + 19 + 2]byte
+	lenScratch [32]byte
 
 	// Scratch space for formatting integers and floats.
 	numScratch [40]byte
@@ -114,10 +114,19 @@ func (c *conn) Err() error {
 }
 
 func (c *conn) writeLen(prefix byte, n int) error {
-	c.lenScratch[0] = prefix
-	s := strconv.AppendInt(c.lenScratch[:1], int64(n), 10)
-	s = append(s, "\r\n"...)
-	_, err := c.bw.Write(s)
+	c.lenScratch[len(c.lenScratch)-1] = '\n'
+	c.lenScratch[len(c.lenScratch)-2] = '\r'
+	i := len(c.lenScratch) - 3
+	for {
+		c.lenScratch[i] = byte('0' + n%10)
+		i -= 1
+		n = n / 10
+		if n == 0 {
+			break
+		}
+	}
+	c.lenScratch[i] = prefix
+	_, err := c.bw.Write(c.lenScratch[i:])
 	return err
 }
 
@@ -193,6 +202,64 @@ func (c *conn) readLine() ([]byte, error) {
 	return p[:i], nil
 }
 
+// parseLen parses bulk and multi-bulk lengths.
+func parseLen(p []byte) (int, error) {
+	if len(p) == 0 {
+		return -1, errors.New("redigo: malformed length")
+	}
+
+	if p[0] == '-' && len(p) == 2 && p[1] == '1' {
+		// handle $-1 and $-1 null replies.
+		return -1, nil
+	}
+
+	var n int
+	for _, b := range p {
+		n *= 10
+		if b < '0' || b > '9' {
+			return -1, errors.New("redigo: illegal bytes in length")
+		}
+		n += int(b - '0')
+	}
+
+	return n, nil
+}
+
+// parseInt parses an integer reply.
+func parseInt(p []byte) (interface{}, error) {
+	if len(p) == 0 {
+		return 0, errors.New("redigo: malformed integer")
+	}
+
+	var negate bool
+	if p[0] == '-' {
+		negate = true
+		p = p[1:]
+		if len(p) == 0 {
+			return 0, errors.New("redigo: malformed integer")
+		}
+	}
+
+	var n int64
+	for _, b := range p {
+		n *= 10
+		if b < '0' || b > '9' {
+			return 0, errors.New("redigo: illegal bytes in length")
+		}
+		n += int64(b - '0')
+	}
+
+	if negate {
+		n = -n
+	}
+	return n, nil
+}
+
+var (
+	okReply   interface{} = "OK"
+	pongReply interface{} = "PONG"
+)
+
 func (c *conn) readReply() (interface{}, error) {
 	line, err := c.readLine()
 	if err != nil {
@@ -203,18 +270,23 @@ func (c *conn) readReply() (interface{}, error) {
 	}
 	switch line[0] {
 	case '+':
-		return string(line[1:]), nil
+		switch {
+		case len(line) == 3 && line[1] == 'O' && line[2] == 'K':
+			// Avoid allocation for frequent "+OK" response.
+			return okReply, nil
+		case len(line) == 5 && line[1] == 'P' && line[2] == 'O' && line[3] == 'N' && line[4] == 'G':
+			// Avoid allocation in PING command benchmarks :)
+			return pongReply, nil
+		default:
+			return string(line[1:]), nil
+		}
 	case '-':
 		return Error(string(line[1:])), nil
 	case ':':
-		n, err := strconv.ParseInt(string(line[1:]), 10, 64)
-		if err != nil {
-			return nil, err
-		}
-		return n, nil
+		return parseInt(line[1:])
 	case '$':
-		n, err := strconv.Atoi(string(line[1:]))
-		if err != nil || n < 0 {
+		n, err := parseLen(line[1:])
+		if n < 0 {
 			return nil, err
 		}
 		p := make([]byte, n)
@@ -222,17 +294,15 @@ func (c *conn) readReply() (interface{}, error) {
 		if err != nil {
 			return nil, err
 		}
-		line, err := c.readLine()
-		if err != nil {
+		if line, err := c.readLine(); err != nil {
 			return nil, err
-		}
-		if len(line) != 0 {
+		} else if len(line) != 0 {
 			return nil, errors.New("redigo: bad bulk format")
 		}
 		return p, nil
 	case '*':
-		n, err := strconv.Atoi(string(line[1:]))
-		if err != nil || n < 0 {
+		n, err := parseLen(line[1:])
+		if n < 0 {
 			return nil, err
 		}
 		r := make([]interface{}, n)

+ 8 - 0
redis/conn_test.go

@@ -94,6 +94,10 @@ var readTests = []struct {
 		"+OK\r\n",
 		"OK",
 	},
+	{
+		"+PONG\r\n",
+		"PONG",
+	},
 	{
 		"@OK\r\n",
 		errorSentinel,
@@ -110,6 +114,10 @@ var readTests = []struct {
 		":1\r\n",
 		int64(1),
 	},
+	{
+		":-2\r\n",
+		int64(-2),
+	},
 	{
 		"*0\r\n",
 		[]interface{}{},