]> Cypherpunks repositories - gostls13.git/commitdiff
strings: reduce allocation in byteStringReplacer.WriteString
authorRui Ueyama <ruiu@google.com>
Thu, 19 Jun 2014 18:22:50 +0000 (11:22 -0700)
committerRui Ueyama <ruiu@google.com>
Thu, 19 Jun 2014 18:22:50 +0000 (11:22 -0700)
Use WriteString instead of allocating a byte slice as a
buffer. This was a TODO.

benchmark               old ns/op    new ns/op    delta
BenchmarkWriteString        40139        19991  -50.20%

LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/107190044

src/pkg/strings/replace.go
src/pkg/strings/replace_test.go

index 16889bac99286c4a14f67a3f0d50a3b5f165bf2e..c66126561339ce8fd19756744ca5019d390686aa 100644 (file)
@@ -511,48 +511,32 @@ func (r *byteStringReplacer) Replace(s string) string {
        return string(buf)
 }
 
-// WriteString maintains one buffer that's at most 32KB.  The bytes in
-// s are enumerated and the buffer is filled.  If it reaches its
-// capacity or a byte has a replacement, the buffer is flushed to w.
 func (r *byteStringReplacer) WriteString(w io.Writer, s string) (n int, err error) {
-       // TODO(bradfitz): use io.WriteString with slices of s instead.
-       bufsize := 32 << 10
-       if len(s) < bufsize {
-               bufsize = len(s)
-       }
-       buf := make([]byte, bufsize)
-       bi := buf[:0]
-
+       sw := getStringWriter(w)
+       last := 0
        for i := 0; i < len(s); i++ {
                b := s[i]
-               var new []byte
-               if r.old[b>>5]&uint32(1<<(b&31)) != 0 {
-                       new = r.new[b]
-               } else {
-                       bi = append(bi, b)
-               }
-               if len(bi) == cap(bi) || (len(bi) > 0 && len(new) > 0) {
-                       nw, err := w.Write(bi)
-                       n += nw
-                       if err != nil {
-                               return n, err
-                       }
-                       bi = buf[:0]
+               if r.old[b>>5]&uint32(1<<(b&31)) == 0 {
+                       continue
                }
-               if len(new) > 0 {
-                       nw, err := w.Write(new)
+               if last != i {
+                       nw, err := sw.WriteString(s[last:i])
                        n += nw
                        if err != nil {
                                return n, err
                        }
                }
-       }
-       if len(bi) > 0 {
-               nw, err := w.Write(bi)
+               last = i + 1
+               nw, err := w.Write(r.new[b])
                n += nw
                if err != nil {
                        return n, err
                }
        }
-       return n, nil
+       if last != len(s) {
+               var nw int
+               nw, err = sw.WriteString(s[last:])
+               n += nw
+       }
+       return
 }
index 82e4b6ef08ebfa46a60a4a2e4daf745d4cfb9f39..ca57f08251ef9e30dfc688df61f4b81b9d2e4154 100644 (file)
@@ -480,6 +480,15 @@ func BenchmarkHTMLEscapeOld(b *testing.B) {
        }
 }
 
+func BenchmarkWriteString(b *testing.B) {
+       str := Repeat("I <3 to escape HTML & other text too.", 100)
+       buf := new(bytes.Buffer)
+       for i := 0; i < b.N; i++ {
+               htmlEscaper.WriteString(buf, str)
+               buf.Reset()
+       }
+}
+
 // BenchmarkByteByteReplaces compares byteByteImpl against multiple Replaces.
 func BenchmarkByteByteReplaces(b *testing.B) {
        str := Repeat("a", 100) + Repeat("b", 100)