In our programming, dealing with strings is essential, we need to deal with strings for database text processing, Web text display, text data storage and so on, so for strings, search, splicing these are common operations, especially to splicing used more. Like splicing together a person’s name and age.

In Go (Golang), there are many ways to concatenate strings. Which one is the most efficient? Because memory is very expensive, performance is very important, sometimes inadvertently string conversion and copy, you can eat up your memory, low performance, have to consider.

A case in point

No study of any feature, performance, or method is more convincing than an example. Here, we use an example to demonstrate the concatenation of different strings and the corresponding performance analysis. Here’s an example:

Nickname: Fei Xue Heartless blog :http://www.flysnow.org/ wechat official account :flysnow_orgCopy the code

In this case, we’re going to concatenate the above by concatenating strings, and it’s important to note that in this case, line breaks are also part of the string concatenation, because we’re going to concatenate exactly the above.

Splicing +.

This concatenation is the simplest and easiest for us to use because it’s available in all programming languages, like Go and Java, and it’s the + operator that evaluates at run time. Now let’s show you the code for this splicing, although it’s a little simpler.

func StringPlus(a) string{
	var s string
	s+="Nickname"+":"+"Snow has no mercy"+"\n"
	s+="Blog"+":"+"http://www.flysnow.org/"+"\n"
	s+="Wechat Official Account"+":"+"flysnow_org"
	return s
}
Copy the code

We could write a test case of our own, and we could print out exactly what we did in our example. Let’s test the performance of the most common string concatenation method:

func BenchmarkStringPlus(b *testing.B) {
	for i:=0; i<b.N; i++{ StringPlus() } }Copy the code

Run go test-bench =. -benchmem to see the performance output:

BenchmarkStringPlus-8   20000000    108 ns/op   144 B/op    2 allocs/op
Copy the code

Each operation takes 108ns, with two memory allocations and 114 bytes of memory allocated.

FMT stitching

This concatenation, with the help of the FMT.Sprint series of functions, then returns the concatenated string.

func StringFmt(a) string{
	return fmt.Sprint("Nickname".":"."Snow has no mercy"."\n"."Blog".":"."http://www.flysnow.org/"."\n"."Wechat Official Account".":"."flysnow_org")}Copy the code

For demonstration purposes, the code does not have a line break, may affect the reading experience on the phone, sorry. Let’s test its performance and see how it works.

func BenchmarkStringFmt(b *testing.B) {
	for i:=0; i<b.N; i++{ StringFmt() } }Copy the code

Run to view test results:

BenchmarkStringFmt-8    5000000     385 ns/op   80 B/op     1 allocs/op
Copy the code

Although each operation allocates only one memory and 80 bytes is not much, each operation takes too long and performance is nowhere near as fast as the + sign operation.

The Join together

This is concatenated using the strings.Join function, which takes an array of strings and converts it into a concatenated string.

func StringJoin(a) string{
	s:=[]string{"Nickname".":"."Snow has no mercy"."\n"."Blog".":"."http://www.flysnow.org/"."\n"."Wechat Official Account".":"."flysnow_org"}
	return strings.Join(s,"")}func BenchmarkStringJoin(b *testing.B) {
	for i:=0; i<b.N; i++{ StringJoin() } }Copy the code

Having put the performance test code together for convenience, now look at the effect of the performance test.

BenchmarkStringJoin-8   10000000    177 ns/op   160 B/op    2 allocs/op
Copy the code

The overall operation is not much different from the + operation, about 0.5 times lower.

www.flysnow.org/2018/10/28/…

Go language string efficient splicing (a) | blowing merciless blog

Buffer stitching

Buffer is a flexible structure that can concatenate not only strings, but also bytes,rune, etc. It also implements the IO.Writer interface, making it very convenient to write.

func StringBuffer(a) string {
	var b bytes.Buffer
	b.WriteString("Nickname")
	b.WriteString(":")
	b.WriteString("Snow has no mercy")
	b.WriteString("\n")
	b.WriteString("Blog")
	b.WriteString(":")
	b.WriteString("http://www.flysnow.org/")
	b.WriteString("\n")
	b.WriteString("Wechat Official Account")
	b.WriteString(":")
	b.WriteString("flysnow_org")
	return b.String()
}

func BenchmarkStringBuffer(b *testing.B) {
	for i:=0; i<b.N; i++{ StringBuffer() } }Copy the code

To see its performance, run the output:

BenchmarkStringBuffer-8     5000000     291 ns/op   336 B/op    3 allocs/op
Copy the code

It is not very good, similar to the worst FMT concatenation, far from + sign, Join concatenation, and more memory allocation. Each operation takes a long time.

Builder stitching

To improve the performance of buffer concatenation, starting with go 1.10, a Builder type was added to improve the performance of string concatenation. It is used almost the same as buffer.

func StringBuilder(a) string {
	var b strings.Builder
	b.WriteString("Nickname")
	b.WriteString(":")
	b.WriteString("Snow has no mercy")
	b.WriteString("\n")
	b.WriteString("Blog")
	b.WriteString(":")
	b.WriteString("http://www.flysnow.org/")
	b.WriteString("\n")
	b.WriteString("Wechat Official Account")
	b.WriteString(":")
	b.WriteString("flysnow_org")
	return b.String()
}

func BenchmarkStringBuilder(b *testing.B) {
	for i:=0; i<b.N; i++{ StringBuilder() } }Copy the code

The official said that the performance is better than buffer, let’s see the results of the performance test.

BenchmarkStringBuilder-8    10000000    170 ns/op   232 B/op    4 allocs/op
Copy the code

Yes, it’s doubled. Although the amount of memory allocated is a bit high, the amount of memory allocated is less than buffer.

The performance comparison

Since the Benchmark test only displays performance, I set the test time to 3s (seconds) to make it longer for comparison and generated a CPU profile for performance analysis.

Run go test-bench =. -benchmem-benchtime = 3s-cpuprofile =profile.out

StringPlus-8    50000000    112 ns/op   144 B/op    2 allocs/op
StringFmt-8     20000000    344 ns/op   80 B/op     1 allocs/op
StringJoin-8    30000000    171 ns/op   160 B/op    2 allocs/op
StringBuffer-8  20000000    302 ns/op   336 B/op    3 allocs/op
StringBuilder-8 30000000    171 ns/op   232 B/op    4 allocs/op
Copy the code

Let’s go tool pprof profile.out to see the output CPU profile information. The top command is mainly used here.

Showing top 15 nodes out of 89 flat flat% sum% cum cum% 11.99s 42.55% 42.55% 11.99s 42.55% runtime.kevent 6.30s 22.36% Pthread_cond_wait 1.65s 5.86% 70.76% 1.65s 5.86% Runtime. pthread_cond_wait 1.11s 3.94% Relative_relative_relative_np 0.58s 2.74.70%1.11s 3.94%runtime.usleep 1.9s 3.90%3.90%78.60%1.9s 3.90%3.90%runtime.pthread_cond_timedWait_relative_np 0.58s 2.06% WbBufFlush1 0.51s 1.81% 82.47% 0.51s 1.81% runtime. Memmove 0.44s 1.56% 84.03% 1.81s 6.42% (*pp).printarg 0.39s 1.38% 85.42% 2.36s 8.37%fmt.(*pp).doprint 0.36s 1.28% 86.69% 0.70s 2.48% FMT.(*buffer).writeString (inline) 0.34s 1.21% 87.90% 0.93s 3.30% Runtime.mallocgc 0.20s 0.71% 88.61% 1.20s 4.26% FMT.(* FMT).FMTS 0.18s 0.64% 89.25% 0.18s 0.64% FMT.(* FMT). Truncate 0.16s 0.57% 89.82% 0.16s 0.57% The runtime. MemclrNoHeapPointers 0.15 s 0.53% 90.35% s 4.79% FMT. 1.35 pp (*). FmtStringCopy the code

Of the top 15, it can be seen that FMT stitching is the worst, because many methods in FMT take the most time. The WriteString method of buffer is also time-consuming.

The TOP above may not be too intuitive, if you look at the flame diagram, it will be more clear. The best performance is + sign splicing and Join splicing, and the slowest is FMT splicing. Here, Builder splicing and Buffer splicing are almost the same, but they do not give full play to their capabilities.

conclusion

From the overall performance testing and analysis, the Builder didn’t work out as well as we expected. Does that mean builder isn’t practical? How about + sign and Join concatenation? We will continue the analysis in the next article, and here are some early leaks: For example:

  1. The size of the concatenated string
  2. Number of concatenated strings

These two things are important to see which one my examples fall into.

Well, for more details, see the next string concatenation analysis.

This article is an original article, reprinted with notes of origin, “there are always bad people grab the article when also remove my original description” welcome to scan the code to follow the public number flysnow_org or www.flysnow.org/, the first time to see the follow-up wonderful article. “Anti-rotten person remarks **…… &*¥” feel good, feel free to share it in moments, thank you for your support.