From 74b441ab4c6af6b09197bed6a34ff35ee2a2cb07 Mon Sep 17 00:00:00 2001 From: Domenico Corvasce Date: Sat, 18 Jan 2025 11:16:19 +0100 Subject: [PATCH 1/4] Add notes about using and benchmarking strings.Builder --- iteration.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/iteration.md b/iteration.md index c2dc8b7a..65f94d8e 100644 --- a/iteration.md +++ b/iteration.md @@ -127,6 +127,43 @@ What `136 ns/op` means is our function takes on average 136 nanoseconds to run \ **Note:** Sometimes, Go can optimize your benchmarks in a way that makes them inaccurate, such as eliminating the function being benchmarked. Check your benchmarks to see if the values make sense. If they seem overly optimized, you can follow the strategies in this **[blog post](https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go)**. +Strings in Go are immutable, meaning every concatenation, such as in our `Repeat` function, involves copying memory to accommodate the new string. This impacts performance, particularly during heavy string concatenation. + +The standard library provides `strings.Builder`[stringsBuilder] which minimizes memory copying. +This type implements a `WriteString` method which you can use to concatenate strings: + +```go +const repeatCount = 5 + +func Repeat(character string) string { + var repeated strings.Builder + for i := 0; i < repeatCount; i++ { + repeated.WriteString(character) + } + return repeated.String() +} +``` + +**Note**: We have to call the `String` method to retrieve the final result. + +We can use our benchmark to confirm that `strings.Builder` significantly improves performance. +Furthermore, we can check that memory is allocated once per iteration. + +Run `go test -bench=. -benchmem`: + +```text +goos: darwin +goarch: amd64 +pkg: github.com/quii/learn-go-with-tests/for/v4 +10000000 25.70 ns/op 8 B/op 1 allocs/op +PASS +``` + +The `-benchmem` flag reports information about memory allocation: + +* `B/op`: the number of bytes allocated per iteration +* `allocs/op`: the number of memory allocations per iteration + ## Practice exercises * Change the test so a caller can specify how many times the character is repeated and then fix the code @@ -138,3 +175,5 @@ What `136 ns/op` means is our function takes on average 136 nanoseconds to run \ * More TDD practice * Learned `for` * Learned how to write benchmarks + +[stringsBuilder]: https://pkg.go.dev/strings#Builder From ceb7332190c9ca20b0747370369a3342eb092d1d Mon Sep 17 00:00:00 2001 From: Domenico Corvasce Date: Sat, 18 Jan 2025 11:17:14 +0100 Subject: [PATCH 2/4] Add new version of for example --- for/v3/repeat.go | 14 ++++++++++++++ for/v3/repeat_test.go | 12 ++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 for/v3/repeat.go create mode 100644 for/v3/repeat_test.go diff --git a/for/v3/repeat.go b/for/v3/repeat.go new file mode 100644 index 00000000..08656e4f --- /dev/null +++ b/for/v3/repeat.go @@ -0,0 +1,14 @@ +package iteration + +import "strings" + +const repeatCount = 5 + +// Repeat returns character repeated 5 times. +func Repeat(character string) string { + var repeated strings.Builder + for i := 0; i < repeatCount; i++ { + repeated.WriteString(character) + } + return repeated.String() +} diff --git a/for/v3/repeat_test.go b/for/v3/repeat_test.go new file mode 100644 index 00000000..fd7d5e69 --- /dev/null +++ b/for/v3/repeat_test.go @@ -0,0 +1,12 @@ +package iteration + +import "testing" + +func TestRepeat(t *testing.T) { + repeated := Repeat("a") + expected := "aaaaa" + + if repeated != expected { + t.Errorf("expected %q but got %q", expected, repeated) + } +} From 0a7fe4340160cec644591a54da5af7410b34932e Mon Sep 17 00:00:00 2001 From: Domenico Corvasce Date: Sat, 18 Jan 2025 11:20:38 +0100 Subject: [PATCH 3/4] Update roman numerals tutorial with backlink --- roman-numerals.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/roman-numerals.md b/roman-numerals.md index bf8912ba..e82a9f1e 100644 --- a/roman-numerals.md +++ b/roman-numerals.md @@ -208,7 +208,8 @@ func ConvertToRoman(arabic int) string { } ``` -You may not have used [`strings.Builder`](https://golang.org/pkg/strings/#Builder) before +You might remember [`strings.Builder`](https://golang.org/pkg/strings/#Builder) from our discussion +about [benchmarking](iteration.md#benchmarking) > A Builder is used to efficiently build a string using Write methods. It minimizes memory copying. From 93a3eb5cdd502b4eb0ea6b12659e372d62e74c61 Mon Sep 17 00:00:00 2001 From: Domenico Corvasce Date: Sat, 18 Jan 2025 11:31:36 +0100 Subject: [PATCH 4/4] Fix phrasing --- iteration.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/iteration.md b/iteration.md index 65f94d8e..044cb826 100644 --- a/iteration.md +++ b/iteration.md @@ -129,8 +129,8 @@ What `136 ns/op` means is our function takes on average 136 nanoseconds to run \ Strings in Go are immutable, meaning every concatenation, such as in our `Repeat` function, involves copying memory to accommodate the new string. This impacts performance, particularly during heavy string concatenation. -The standard library provides `strings.Builder`[stringsBuilder] which minimizes memory copying. -This type implements a `WriteString` method which you can use to concatenate strings: +The standard library provides the `strings.Builder`[stringsBuilder] type which minimizes memory copying. +It implements a `WriteString` method which we can use to concatenate strings: ```go const repeatCount = 5 @@ -146,9 +146,7 @@ func Repeat(character string) string { **Note**: We have to call the `String` method to retrieve the final result. -We can use our benchmark to confirm that `strings.Builder` significantly improves performance. -Furthermore, we can check that memory is allocated once per iteration. - +We can use `BenchmarkRepeat` to confirm that `strings.Builder` significantly improves performance. Run `go test -bench=. -benchmem`: ```text @@ -159,7 +157,7 @@ pkg: github.com/quii/learn-go-with-tests/for/v4 PASS ``` -The `-benchmem` flag reports information about memory allocation: +The `-benchmem` flag reports information about memory allocations: * `B/op`: the number of bytes allocated per iteration * `allocs/op`: the number of memory allocations per iteration