diff options
| author | Rui Ueyama <ruiu@google.com> | 2014-06-21 22:08:43 -0700 |
|---|---|---|
| committer | Rui Ueyama <ruiu@google.com> | 2014-06-21 22:08:43 -0700 |
| commit | 3142861ff86a8b4064256f31a0f63dcd23c2f971 (patch) | |
| tree | d26bc42254eeb316a7dd6b5c487595950e6e78bb /src/pkg/strings | |
| parent | 9bfb66e962b631c2faf831ccc29ba5d4018ccf04 (diff) | |
| download | go-3142861ff86a8b4064256f31a0f63dcd23c2f971.tar.xz | |
strings: use sync.Pool to cache buffer
benchmark old ns/op new ns/op delta
BenchmarkByteReplacerWriteString 3596 3094 -13.96%
benchmark old allocs new allocs delta
BenchmarkByteReplacerWriteString 1 0 -100.00%
LGTM=dvyukov
R=bradfitz, dave, dvyukov
CC=golang-codereviews
https://golang.org/cl/101330053
Diffstat (limited to 'src/pkg/strings')
| -rw-r--r-- | src/pkg/strings/replace.go | 33 | ||||
| -rw-r--r-- | src/pkg/strings/replace_test.go | 42 |
2 files changed, 50 insertions, 25 deletions
diff --git a/src/pkg/strings/replace.go b/src/pkg/strings/replace.go index cb9d7b1fa4..89aca95bae 100644 --- a/src/pkg/strings/replace.go +++ b/src/pkg/strings/replace.go @@ -4,7 +4,10 @@ package strings -import "io" +import ( + "io" + "sync" +) // A Replacer replaces a list of strings with replacements. type Replacer struct { @@ -451,27 +454,31 @@ func (r *byteReplacer) Replace(s string) string { return string(buf) } -func (r *byteReplacer) WriteString(w io.Writer, s string) (n int, err error) { - // TODO(bradfitz): use io.WriteString with slices of s, avoiding allocation. - bufsize := 32 << 10 - if len(s) < bufsize { - bufsize = len(s) - } - buf := make([]byte, bufsize) +var bufferPool = sync.Pool{ + New: func() interface{} { + b := make([]byte, 4096) + return &b + }, +} +func (r *byteReplacer) WriteString(w io.Writer, s string) (n int, err error) { + bp := bufferPool.Get().(*[]byte) + buf := *bp for len(s) > 0 { - ncopy := copy(buf, s[:]) - s = s[ncopy:] + ncopy := copy(buf, s) for i, b := range buf[:ncopy] { buf[i] = r.new[b] } - wn, err := w.Write(buf[:ncopy]) + s = s[ncopy:] + var wn int + wn, err = w.Write(buf[:ncopy]) n += wn if err != nil { - return n, err + break } } - return n, nil + bufferPool.Put(bp) + return } // byteStringReplacer is the implementation that's used when all the diff --git a/src/pkg/strings/replace_test.go b/src/pkg/strings/replace_test.go index 2cb318b69d..77e48b988b 100644 --- a/src/pkg/strings/replace_test.go +++ b/src/pkg/strings/replace_test.go @@ -308,20 +308,21 @@ func TestReplacer(t *testing.T) { } } +var algorithmTestCases = []struct { + r *Replacer + want string +}{ + {capitalLetters, "*strings.byteReplacer"}, + {htmlEscaper, "*strings.byteStringReplacer"}, + {NewReplacer("12", "123"), "*strings.singleStringReplacer"}, + {NewReplacer("1", "12"), "*strings.byteStringReplacer"}, + {NewReplacer("", "X"), "*strings.genericReplacer"}, + {NewReplacer("a", "1", "b", "12", "cde", "123"), "*strings.genericReplacer"}, +} + // TestPickAlgorithm tests that NewReplacer picks the correct algorithm. func TestPickAlgorithm(t *testing.T) { - testCases := []struct { - r *Replacer - want string - }{ - {capitalLetters, "*strings.byteReplacer"}, - {htmlEscaper, "*strings.byteStringReplacer"}, - {NewReplacer("12", "123"), "*strings.singleStringReplacer"}, - {NewReplacer("1", "12"), "*strings.byteStringReplacer"}, - {NewReplacer("", "X"), "*strings.genericReplacer"}, - {NewReplacer("a", "1", "b", "12", "cde", "123"), "*strings.genericReplacer"}, - } - for i, tc := range testCases { + for i, tc := range algorithmTestCases { got := fmt.Sprintf("%T", tc.r.Replacer()) if got != tc.want { t.Errorf("%d. algorithm = %s, want %s", i, got, tc.want) @@ -329,6 +330,23 @@ func TestPickAlgorithm(t *testing.T) { } } +type errWriter struct{} + +func (errWriter) Write(p []byte) (n int, err error) { + return 0, fmt.Errorf("unwritable") +} + +// TestWriteStringError tests that WriteString returns an error +// received from the underlying io.Writer. +func TestWriteStringError(t *testing.T) { + for i, tc := range algorithmTestCases { + n, err := tc.r.WriteString(errWriter{}, "abc") + if n != 0 || err == nil || err.Error() != "unwritable" { + t.Errorf("%d. WriteStringError = %d, %v, want 0, unwritable", i, n, err) + } + } +} + // TestGenericTrieBuilding verifies the structure of the generated trie. There // is one node per line, and the key ending with the current line is in the // trie if it ends with a "+". |
