Skip to content

Instantly share code, notes, and snippets.

@itchyny
Created February 24, 2021 11:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save itchyny/bcd4bbd6909d2db6f500c6b68934d134 to your computer and use it in GitHub Desktop.
Save itchyny/bcd4bbd6909d2db6f500c6b68934d134 to your computer and use it in GitHub Desktop.
Performance comparison of implementations of strings.Join
package main
import (
"bytes"
"strings"
)
func main() {}
func JoinPlus(xs []string, sep string) string {
var ret string
for i, x := range xs {
if i > 0 {
ret += sep
}
ret += x
}
return ret
}
func JoinBytesBuffer(xs []string, sep string) string {
var buf bytes.Buffer
for i, x := range xs {
if i > 0 {
buf.WriteString(sep)
}
buf.WriteString(x)
}
return buf.String()
}
func JoinStringsBuilder(xs []string, sep string) string {
var sb strings.Builder
for i, x := range xs {
if i > 0 {
sb.WriteString(sep)
}
sb.WriteString(x)
}
return sb.String()
}
func JoinStringsBuilderGrow(xs []string, sep string) string {
if len(xs) == 0 {
return ""
}
size := len(sep) * (len(xs) - 1)
for _, x := range xs {
size += len(x)
}
var sb strings.Builder
sb.Grow(size)
for i, x := range xs {
if i > 0 {
sb.WriteString(sep)
}
sb.WriteString(x)
}
return sb.String()
}
package main
import (
"strconv"
"strings"
"testing"
)
var (
xs []string
sep string
expected string
)
func init() {
xs = make([]string, 2000)
for i := range xs {
xs[i] = strconv.Itoa(i)
}
sep = ", "
expected = strings.Join(xs, sep)
}
func TestJoin(t *testing.T) {
if expected != JoinPlus(xs, sep) {
t.Error("JoinPlus(xs, sep)")
}
if expected != JoinBytesBuffer(xs, sep) {
t.Error("JoinBytesBuffer(xs, sep)")
}
if expected != JoinStringsBuilder(xs, sep) {
t.Error("JoinStringsBuilder(xs, sep)")
}
if expected != JoinStringsBuilderGrow(xs, sep) {
t.Error("JoinStringsBuilderGrow(xs, sep)")
}
}
func BenchmarkJoinPlus(b *testing.B) {
for i := 0; i < b.N; i++ {
JoinPlus(xs, sep)
}
}
func BenchmarkJoinBytesBuffer(b *testing.B) {
for i := 0; i < b.N; i++ {
JoinBytesBuffer(xs, sep)
}
}
func BenchmarkJoinStringsBuilder(b *testing.B) {
for i := 0; i < b.N; i++ {
JoinStringsBuilder(xs, sep)
}
}
func BenchmarkJoinStringsBuilderGrow(b *testing.B) {
for i := 0; i < b.N; i++ {
JoinStringsBuilderGrow(xs, sep)
}
}
func BenchmarkStringsJoin(b *testing.B) {
for i := 0; i < b.N; i++ {
strings.Join(xs, sep)
}
}
❯ go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: join-benchmark
cpu: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
BenchmarkJoinPlus-16 366 2961378 ns/op 21757467 B/op 3998 allocs/op
BenchmarkJoinBytesBuffer-16 40668 28744 ns/op 49584 B/op 10 allocs/op
BenchmarkJoinStringsBuilder-16 53319 22469 ns/op 48504 B/op 17 allocs/op
BenchmarkJoinStringsBuilderGrow-16 70018 17527 ns/op 12288 B/op 1 allocs/op
BenchmarkStringsJoin-16 68562 17164 ns/op 12288 B/op 1 allocs/op
PASS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment