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
Copy file name to clipboardExpand all lines: content/learning-paths/servers-and-cloud-computing/go-gc-default-settings/_index.md
+1-2Lines changed: 1 addition & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ draft: true
4
4
cascade:
5
5
draft: true
6
6
7
-
description: Learn how to measure and observe Go garbage collection metrics on AWS Graviton instances.
7
+
description: Learn how to run Go benchmarks on AWS Graviton, capture GC metrics and pprof profiles, and establish a reproducible default-GC baseline for memory-intensive workloads on Arm.
Copy file name to clipboardExpand all lines: content/learning-paths/servers-and-cloud-computing/go-gc-default-settings/choose_aws_instance.md
+17-4Lines changed: 17 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,15 +6,15 @@ weight: 2
6
6
layout: learningpathall
7
7
---
8
8
## What is Garbage Collection? (GC)
9
-
Memory management is a critical aspects of application performance, and Garbage Collection (GC) plays a central role in automating that process. GC continuously identifies and removes objects that are no longer needed, freeing memory for re-use for other purposes..
9
+
Memory management is a critical aspect of application performance, and Garbage Collection (GC) plays a central role in automating that process. GC continuously identifies and removes objects that are no longer needed, freeing memory for reuse.
10
10
11
11
While this automation improves productivity and application safety, inefficient garbage collection can lead to increased CPU usage, longer response times, and unexpected application pauses.
12
12
13
13
Tracking GC metrics provides a window into an application's memory health, helping engineers optimize performance, and ensuring the system can scale efficiently under load.
14
14
15
15
## Measuring default Go GC behavior on Arm servers
16
16
17
-
Go is one such language which implements GC. As Go applications can spend meaningful time allocating memory and running garbage collection, it is important to understand how the Go runtime behaves under default settings.
17
+
Go is one such language that implements GC. As Go applications can spend meaningful time allocating memory and running garbage collection, it is important to understand how the Go runtime behaves under default settings.
18
18
19
19
In this Learning Path, you'll run Go benchmarks on an AWS Graviton instance. The goal is to build a clean baseline, measuring operation time, allocation rate, GC frequency, and GC pause cost.
If the command returns one or more Availability Zones, you can use `m8g.xlarge` in that Region. If you are unable to find `m8g.xlarge` in your Region, you can try a different Region, or fallback to an 'm7g.xlarge' instance, which is based on the previous generation AWS Graviton3:
47
+
If the command returns one or more Availability Zones, you can use `m8g.xlarge` in that Region. If `m8g.xlarge`is not available in your Region, try a different Region, or fall back to an `m7g.xlarge` instance, which is based on the previous generation AWS Graviton3:
Once you have chosen an instance type, provision it to run Ubuntu 24.04 LTS Arm64. Once the instance is running, and you are ssh'd into it, you can proceed to the next step.
58
+
After selecting an instance type, launch it with the Ubuntu 24.04 LTS (arm64) AMI. You can do this through the [AWS EC2 console](https://console.aws.amazon.com/ec2/) or the AWS CLI. When configuring the instance:
59
+
60
+
- Select your chosen instance type (`m8g.xlarge` or `m7g.xlarge`)
61
+
- Choose the Ubuntu 24.04 LTS arm64 AMI for your Region
62
+
- Configure a security group that allows inbound SSH (port 22) from your IP address
63
+
- Associate an existing key pair or create a new one for SSH access
64
+
65
+
After the instance reaches the running state, connect to it over SSH:
You now have a running Arm Linux instance on AWS Graviton. In the next section you'll install Go and Benchstat so the instance is ready to run benchmarks.
`benchstat` is a Go performance analysis tool that compares benchmark results and provides statistical analysis of performance differences between runs. It helps developers determine whether observed changes in benchmark metrics are statistically significant rather than simply the result of normal measurement variability. We'll use `benchstat` for that purpose in this learning path.
62
+
`benchstat` is a Go performance analysis tool that compares benchmark results and provides statistical analysis of performance differences between runs. It helps developers determine whether observed changes in benchmark metrics are statistically significant rather than simply the result of normal measurement variability. You'll use `benchstat` for that purpose in this Learning Path.
To understand better what these benchmarks results are showing, firt open the Benchstat summary:
11
+
To understand what the benchmark results are showing, first open the Benchstat summary:
12
12
13
13
```bash
14
14
cat default_gc_benchstat.txt
15
15
```
16
16
17
-
The the metrics you see explain the following:
17
+
The metrics you see explain the following:
18
18
19
19
| Metric | Read |
20
20
| --- | --- |
@@ -37,6 +37,8 @@ cat cpu_default_top.txt
37
37
38
38
Look for functions that dominate CPU time. In an allocation-heavy benchmark, you can expect to see time in string handling, allocation paths, and some runtime or GC support functions.
39
39
40
+
The `flat` column shows CPU time spent directly in that function. The `cum` (cumulative) column includes time spent in the function and all functions it called. A function with low `flat` but high `cum` is spending most of its time in callees, which can point to allocation chains or deep call stacks.
41
+
40
42
On the validated `m8g.xlarge` instance, the top CPU profile entries included string scanning, string concatenation, split handling, and allocation paths:
41
43
42
44
```output
@@ -48,11 +50,9 @@ On the validated `m8g.xlarge` instance, the top CPU profile entries included str
48
50
0.72s 4.56% 53.36% 5.17s 32.76% runtime.mallocgc
49
51
```
50
52
51
-
Open the heap allocation profile summary:
53
+
`IndexByteString` and `concatstrings` dominate because the benchmark splits the payload with `strings.Split` (which scans for `;` byte-by-byte) and builds `key:length` strings with `+` concatenation on every iteration. These operations create a large volume of short-lived strings, giving the GC constant work to do. On Graviton, `pprof` makes these allocation chains directly visible because the Arm64 stack unwinder captures the full call path without frame pointer ambiguity.
52
54
53
-
```bash
54
-
cat mem_default_alloc_top.txt
55
-
```
55
+
Open the heap allocation profile summary:
56
56
57
57
Look for application functions that allocate the most heap memory. Reducing allocation volume in those functions usually gives the Go GC less work to do.
58
58
@@ -68,7 +68,7 @@ On the validated `m8g.xlarge` instance, the allocation profile showed that `stri
68
68
69
69
## Keeping a baseline to compare to future changes
70
70
71
-
These results show your default Go GC baseline stats for this benchmarking app on AWS Graviton. By keeping the following files together (eg, each stored in their own zip file, folder, etc) you can easily see how making code changes affects your apps overall performance:
71
+
These results show your default Go GC baseline for this benchmarking app on AWS Graviton. By keeping the following files together — for example, in a versioned folder or archive — you can see how code changes affect overall performance:
72
72
73
73
```output
74
74
default_runtime_baseline.txt
@@ -81,133 +81,8 @@ mem_default.out
81
81
mem_default_alloc_top.txt
82
82
```
83
83
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
-
```
84
+
## What you've accomplished and what's next
210
85
211
-
### Why
86
+
In this section, you ran Benchstat to summarize ten benchmark samples and inspected CPU and heap profiles to identify the hottest code paths. You now have a documented default-GC baseline showing operation time, allocation rate, GC frequency, and stop-the-world pause costs on AWS Graviton.
212
87
213
-
Fewer records means less allocation work and fewer GC cycles.
88
+
In the next section, you'll apply code changes to the benchmark and measure how each change affects these metrics.
0 commit comments