Implement Repeat with Builder to avoid one unnecessary allocation.

benchmark                       old ns/op     new ns/op     delta
BenchmarkRepeat/length-1-4      290           225           -22.41%
BenchmarkRepeat/length-5-4      621           391           -37.04%
BenchmarkRepeat/length-10-4     900           556           -38.22%

benchmark                       old allocs     new allocs     delta
BenchmarkRepeat/length-1-4      2              1              -50.00%
BenchmarkRepeat/length-5-4      2              1              -50.00%
BenchmarkRepeat/length-10-4     2              1              -50.00%
This commit is contained in:
Tapir Liu 2018-06-14 11:43:30 -04:00
parent 30a63ecee3
commit c1ad6e7474
2 changed files with 31 additions and 8 deletions

View File

@ -541,17 +541,26 @@ func Repeat(s string, count int) string {
// See Issue golang.org/issue/16237
if count < 0 {
panic("strings: negative Repeat count")
} else if count > 0 && len(s)*count/count != len(s) {
} else if count == 0 {
return ""
} else if len(s)*count/count != len(s) {
panic("strings: Repeat count causes overflow")
}
b := make([]byte, len(s)*count)
bp := copy(b, s)
for bp < len(b) {
copy(b[bp:], b[:bp])
bp *= 2
k, n := len(s), len(s)*count
var b Builder
b.Grow(n)
b.WriteString(s)
for k < n {
if k + k < n {
b.WriteString(b.String())
k = k + k
} else {
b.WriteString(b.String()[:n-k])
break
}
}
return string(b)
return b.String()
}
// ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case.

View File

@ -1648,7 +1648,21 @@ func BenchmarkSplitNMultiByteSeparator(b *testing.B) {
func BenchmarkRepeat(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat("-", 80)
b.Run("length-1", func(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat("-", 80)
}
})
b.Run("length-5", func(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat("-!@#$", 80)
}
})
b.Run("length-10", func(b *testing.B) {
for i := 0; i < b.N; i++ {
Repeat("-!@#$abcde", 80)
}
})
}
}