You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Example of changes you can experiment with include:
85
-
86
-
### Simple changes that can improve GC performance
87
-
88
-
1.**Reduce the payload size**
89
-
```go
90
-
payload:= strings.Repeat(
91
-
"name=arm&runtime=go&gc=default&value=12345;",
92
-
512,
93
-
)
94
-
```
95
-
A smaller payload creates fewer temporary objects and less garbage each iteration.
96
-
97
-
2.**Move `strings.Split(payload, ";")` outside the benchmark loop**
98
-
```go
99
-
parts:= strings.Split(payload, ";")
100
-
101
-
b.ResetTimer()
102
-
103
-
fori:=0; i < b.N; i++ {
104
-
...
105
-
}
106
-
```
107
-
This avoids repeatedly allocating the same slice of records on every iteration.
108
-
109
-
3.**Reuse the output slice**
110
-
```go
111
-
out:=make([]string, 0, len(parts))
112
-
113
-
fori:=0; i < b.N; i++ {
114
-
out = out[:0]
115
-
...
116
-
}
117
-
```
118
-
Reusing the backing array reduces allocations and GC pressure.
119
-
120
-
4.**Replace `strings.SplitN()` with `strings.IndexByte()`**
121
-
```go
122
-
idx:= strings.IndexByte(part, '=')
123
-
if idx >= 0 {
124
-
key:= part[:idx]
125
-
value:= part[idx+1:]
126
-
...
127
-
}
128
-
```
129
-
This avoids allocating a temporary `[]string` for every record processed.
130
-
131
-
5.**Avoid creating new strings in the hot loop**
132
-
```go
133
-
out = append(out, fields[0])
134
-
```
135
-
Instead of building `"key:length"` strings, store existing strings or simpler values to reduce allocations.
136
-
137
-
6.**Reduce the number of records processed**
138
-
```go
139
-
payload:= strings.Repeat(
140
-
"name=arm&runtime=go&gc=default&value=12345;",
141
-
1024, // instead of 2048
142
-
)
143
-
```
144
-
Fewer records means less allocation work and fewer GC cycles.
145
-
146
-
### Biggest GC wins
147
-
148
-
For this benchmark, the largest improvements typically come from:
149
-
150
-
- Moving `strings.Split(payload, ";")` outside the benchmark loop.
151
-
- Reusing the `out` slice instead of allocating a new one every iteration.
152
-
- Replacing `strings.SplitN()` with `strings.IndexByte()`.
153
-
154
-
When you test code changes, compare against this baseline before changing Go runtime settings. If you later tune `GOGC`, `GOMEMLIMIT`, `GODEBUG`, or `GOMAXPROCS`, treat that as a separate experiment because it changes the runtime operating mode.
84
+
## Experiment with Code Changes that influence GC Behavior
85
+
Now that you have a baseline, you can experiment with code changes that influence GC behavior. For example, you could try:
86
+
87
+
## Challenge 1
88
+
89
+
You just found out that the payload size this benchmark is intended to represent is actually only 128 records instead of 2048. What changes can we make from the baseline to test whether optimizing for this smaller workload affects GC frequency, pause times, and overall application performance?
90
+
91
+
### Idea: Reduce the payload size
92
+
93
+
### How
94
+
95
+
```go
96
+
payload:= strings.Repeat(
97
+
"name=arm&runtime=go&gc=default&value=12345;",
98
+
512,
99
+
)
100
+
```
101
+
102
+
### Why
103
+
104
+
A smaller payload creates fewer temporary objects and less garbage each iteration.
105
+
106
+
---
107
+
108
+
## Challenge 2
109
+
110
+
After profiling the application, you discover that the input payload rarely changes between requests. What modifications can we make to reuse preprocessing work and determine whether reducing repeated allocations improves GC behavior and throughput?
111
+
112
+
### Idea: Move `strings.Split(payload, ";")` outside the benchmark loop
113
+
114
+
### How
115
+
116
+
```go
117
+
parts:= strings.Split(payload, ";")
118
+
119
+
b.ResetTimer()
120
+
121
+
fori:=0; i < b.N; i++ {
122
+
...
123
+
}
124
+
```
125
+
126
+
### Why
127
+
128
+
This avoids repeatedly allocating the same slice of records on every iteration.
129
+
130
+
---
131
+
132
+
## Challenge 3
133
+
134
+
The benchmark currently creates a new output buffer for every operation, but production code processes millions of requests using the same worker. How can we modify the benchmark to reuse memory and evaluate the impact on GC activity and memory consumption?
135
+
136
+
### Idea: Reuse the output slice
137
+
138
+
### How
139
+
140
+
```go
141
+
out:=make([]string, 0, len(parts))
142
+
143
+
fori:=0; i < b.N; i++ {
144
+
out = out[:0]
145
+
...
146
+
}
147
+
```
148
+
149
+
### Why
150
+
151
+
Reusing the backing array reduces allocations and GC pressure.
152
+
153
+
---
154
+
155
+
## Challenge 4
156
+
157
+
A CPU profile shows that string parsing is one of the hottest code paths in the application. What changes can we make to reduce temporary allocations during parsing and measure whether this reduces GC overhead?
158
+
159
+
### Idea: Replace `strings.SplitN()` with `strings.IndexByte()`
160
+
161
+
### How
162
+
163
+
```go
164
+
idx:= strings.IndexByte(part, '=')
165
+
if idx >= 0 {
166
+
key:= part[:idx]
167
+
value:= part[idx+1:]
168
+
...
169
+
}
170
+
```
171
+
172
+
### Why
173
+
174
+
This avoids allocating a temporary `[]string` for every record processed.
175
+
176
+
---
177
+
178
+
## Challenge 5
179
+
180
+
Product requirements change and the application no longer needs to generate derived `"key:length"` strings. What modifications can we make to avoid unnecessary string allocations and test their effect on garbage collection performance?
181
+
182
+
### Idea: Avoid creating new strings in the hot loop
183
+
184
+
### How
185
+
186
+
```go
187
+
out = append(out, fields[0])
188
+
```
189
+
190
+
### Why
191
+
192
+
Instead of building `"key:length"` strings, store existing strings or simpler values to reduce allocations.
193
+
194
+
---
195
+
196
+
## Challenge 6
197
+
198
+
Usage analytics show that most customers send payloads that are half the size represented by the current benchmark. How can we adjust the workload to better reflect real-world traffic and evaluate whether the resulting reduction in allocations improves GC efficiency?
199
+
200
+
### Idea: Reduce the number of records processed
201
+
202
+
### How
203
+
204
+
```go
205
+
payload:= strings.Repeat(
206
+
"name=arm&runtime=go&gc=default&value=12345;",
207
+
1024,
208
+
)
209
+
```
210
+
211
+
### Why
212
+
213
+
Fewer records means less allocation work and fewer GC cycles.
Now that you have an idea of what GC is, and how to measure it, you can experiment with code changes that influence GC behavior. For example, you could try:
12
+
13
+
## Challenge 1
14
+
15
+
You just found out that the payload size this benchmark is intended to represent is actually only 128 records instead of 2048. What changes can we make from the baseline to test whether optimizing for this smaller workload affects GC frequency, pause times, and overall application performance?
16
+
17
+
### Idea: Reduce the payload size
18
+
19
+
### How
20
+
21
+
**Before**
22
+
23
+
```go
24
+
payload:= strings.Repeat(
25
+
"name=arm&runtime=go&gc=default&value=12345;",
26
+
2048,
27
+
)
28
+
```
29
+
30
+
**After**
31
+
32
+
```go
33
+
payload:= strings.Repeat(
34
+
"name=arm&runtime=go&gc=default&value=12345;",
35
+
512,
36
+
)
37
+
```
38
+
39
+
### Why
40
+
41
+
A smaller payload creates fewer temporary objects and less garbage each iteration.
42
+
43
+
---
44
+
45
+
## Challenge 2
46
+
47
+
After profiling the application, you discover that the input payload rarely changes between requests. What modifications can we make to reuse preprocessing work and determine whether reducing repeated allocations improves GC behavior and throughput?
48
+
49
+
### Idea: Move payload split logic outside the benchmark loop
50
+
51
+
### How
52
+
53
+
**Before**
54
+
55
+
```go
56
+
fori:=0; i < b.N; i++ {
57
+
parts:= strings.Split(payload, ";")
58
+
59
+
out:=make([]string, 0, len(parts))
60
+
61
+
...
62
+
}
63
+
```
64
+
65
+
**After**
66
+
67
+
```go
68
+
parts:= strings.Split(payload, ";")
69
+
70
+
fori:=0; i < b.N; i++ {
71
+
out:=make([]string, 0, len(parts))
72
+
73
+
...
74
+
}
75
+
```
76
+
77
+
### Why
78
+
79
+
This avoids repeatedly allocating the same slice of records on every iteration.
80
+
81
+
---
82
+
83
+
## Challenge 3
84
+
85
+
The benchmark currently creates a new output buffer for every operation, but production code processes millions of requests using the same worker. How can we modify the benchmark to reuse memory and evaluate the impact on GC activity and memory consumption?
86
+
87
+
### Idea: Reuse the output slice
88
+
89
+
### How
90
+
91
+
**Before**
92
+
93
+
```go
94
+
fori:=0; i < b.N; i++ {
95
+
out:=make([]string, 0, len(parts))
96
+
97
+
...
98
+
}
99
+
```
100
+
101
+
**After**
102
+
103
+
```go
104
+
out:=make([]string, 0, len(parts))
105
+
106
+
fori:=0; i < b.N; i++ {
107
+
out = out[:0]
108
+
109
+
...
110
+
}
111
+
```
112
+
113
+
### Why
114
+
115
+
Reusing the backing array reduces allocations and GC pressure.
116
+
117
+
---
118
+
119
+
## Challenge 4
120
+
121
+
A CPU profile shows that string parsing is one of the hottest code paths in the application. What changes can we make to reduce temporary allocations during parsing and measure whether this reduces GC overhead?
122
+
123
+
### Idea: Replace SplitN() with IndexByte()
124
+
125
+
### How
126
+
127
+
**Before**
128
+
129
+
```go
130
+
fields:= strings.SplitN(part, "=", 2)
131
+
132
+
iflen(fields) == 2 {
133
+
out = append(
134
+
out,
135
+
fields[0]+":"+strconv.Itoa(len(fields[1])),
136
+
)
137
+
}
138
+
```
139
+
140
+
**After**
141
+
142
+
```go
143
+
idx:= strings.IndexByte(part, '=')
144
+
145
+
if idx >= 0 {
146
+
key:= part[:idx]
147
+
value:= part[idx+1:]
148
+
149
+
out = append(
150
+
out,
151
+
key+":"+strconv.Itoa(len(value)),
152
+
)
153
+
}
154
+
```
155
+
156
+
### Why
157
+
158
+
This avoids allocating a temporary `[]string` for every record processed.
159
+
160
+
---
161
+
162
+
## Challenge 5
163
+
164
+
Product requirements change and the application no longer needs to generate derived `"key:length"` strings. What modifications can we make to avoid unnecessary string allocations and test their effect on garbage collection performance?
165
+
166
+
### Idea: Avoid creating new strings in the hot loop
167
+
168
+
### How
169
+
170
+
**Before**
171
+
172
+
```go
173
+
out = append(
174
+
out,
175
+
fields[0]+":"+strconv.Itoa(len(fields[1])),
176
+
)
177
+
```
178
+
179
+
**After**
180
+
181
+
```go
182
+
out = append(
183
+
out,
184
+
fields[0],
185
+
)
186
+
```
187
+
188
+
### Why
189
+
190
+
Instead of building `"key:length"` strings, store existing strings or simpler values to reduce allocations.
0 commit comments