Skip to content

Commit bd81c10

Browse files
committed
go gc lp
1 parent e4deec6 commit bd81c10

7 files changed

Lines changed: 553 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
---
2+
title: Measure Go GC behavior on AWS Graviton with default runtime settings
3+
description: Learn how to run a Go allocation benchmark on AWS Graviton and measure garbage collection behavior without changing Go runtime settings.
4+
5+
minutes_to_complete: 75
6+
7+
who_is_this_for: This Learning Path is for Go developers and performance engineers who want to measure garbage collection behavior on Arm servers without changing Go runtime GC settings.
8+
9+
learning_objectives:
10+
- Select an AWS Graviton instance for repeatable Go GC measurements
11+
- Install Go and Benchstat on an Arm Linux server
12+
- Confirm that Go runtime tuning variables are unset
13+
- Run a Go benchmark that reports allocation, GC, and pause-time metrics
14+
- Capture CPU and heap profiles without changing GC behavior
15+
16+
prerequisites:
17+
- An [AWS account](https://aws.amazon.com/) with permission to launch AWS Graviton EC2 instances
18+
- The [AWS CLI](/install-guides/aws-cli/) installed and configured on your local machine
19+
- An AWS Graviton instance running Ubuntu 24.04 LTS or another Arm Linux distribution
20+
- Basic familiarity with Go benchmarks and Linux shell commands
21+
22+
author: Geremy Cohen
23+
24+
### Tags
25+
skilllevels: Introductory
26+
subjects: Performance and Architecture
27+
cloud_service_providers:
28+
- AWS
29+
armips:
30+
- Neoverse
31+
tools_software_languages:
32+
- AWS
33+
- Go
34+
operatingsystems:
35+
- Linux
36+
37+
further_reading:
38+
- resource:
39+
title: Amazon EC2 M8g instances
40+
link: https://aws.amazon.com/ec2/instance-types/m8g/
41+
type: documentation
42+
- resource:
43+
title: Go GC guide
44+
link: https://go.dev/doc/gc-guide
45+
type: documentation
46+
- resource:
47+
title: Go runtime package
48+
link: https://pkg.go.dev/runtime
49+
type: documentation
50+
- resource:
51+
title: Go testing package
52+
link: https://pkg.go.dev/testing
53+
type: documentation
54+
- resource:
55+
title: Graviton Performance Runbook
56+
link: https://github.com/aws/aws-graviton-getting-started/blob/main/perfrunbook/README.md
57+
type: documentation
58+
- resource:
59+
title: Benchmark Go performance with Sweet and Benchstat
60+
link: /learning-paths/servers-and-cloud-computing/go-benchmarking-with-sweet/
61+
type: learning path
62+
63+
### FIXED, DO NOT MODIFY
64+
# ================================================================================
65+
weight: 1 # _index.md always has weight of 1 to order correctly
66+
layout: "learningpathall" # All files under learning paths have this same wrapper
67+
learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content.
68+
---
69+
70+
## Measure default Go GC behavior on Arm servers
71+
72+
Go applications can spend meaningful time allocating memory and running garbage collection (GC). You should measure that behavior before you change runtime settings.
73+
74+
In this Learning Path, you run Go benchmarks on an AWS Graviton instance and keep the Go runtime in its default GC mode. You do not set `GOGC`, `GOMEMLIMIT`, `GODEBUG`, or `GOMAXPROCS`.
75+
76+
The goal is to build a clean baseline. You will measure operation time, allocation rate, GC frequency, GC pause cost, and profiles before making tuning decisions.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
# ================================================================================
3+
# FIXED, DO NOT MODIFY THIS FILE
4+
# ================================================================================
5+
weight: 21 # The weight controls the order of the pages. _index.md always has weight 1.
6+
title: "Next Steps" # Always the same, html page title.
7+
layout: "learningpathall" # All files under learning paths have this same wrapper for Hugo processing.
8+
---
9+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
title: Choose an AWS Graviton instance
3+
weight: 2
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## Select an instance for Go GC measurements
10+
11+
Use an AWS Graviton instance that has enough CPU and memory to make Go runtime behavior visible, while keeping the Learning Path inexpensive to run.
12+
13+
For the first prototype, use `m8g.xlarge`.
14+
15+
`m8g.xlarge` is a good starting point because it provides four vCPUs and 16 GiB of memory on AWS Graviton4. Four vCPUs are enough to observe default Go CPU parallelism and GC worker behavior without requiring a large benchmark host. The 16 GiB memory size is enough for allocation-heavy benchmarks without immediately making the lab memory-bound.
16+
17+
Avoid burstable `t4g` instances for this Learning Path. CPU credits can affect benchmark repeatability and make GC measurements harder to explain.
18+
19+
If `m8g.xlarge` is not available in your AWS Region or Availability Zone, use `m7g.xlarge` as the fallback. It has the same vCPU and memory shape on an earlier Graviton generation, so the commands and benchmark workflow remain the same.
20+
21+
## Recommended prototype machine
22+
23+
Use this instance shape for the first version of the Learning Path:
24+
25+
| Purpose | Instance type | Processor | vCPUs | Memory |
26+
| --- | --- | --- | ---: | ---: |
27+
| Default prototype | `m8g.xlarge` | AWS Graviton4 | 4 | 16 GiB |
28+
| Fallback | `m7g.xlarge` | AWS Graviton3 | 4 | 16 GiB |
29+
30+
{{% notice Note %}}
31+
You can use larger instances, such as `m8g.2xlarge`, when you want more CPU width or more memory headroom. Start with `m8g.xlarge` so the first benchmark run is easy to reproduce and inexpensive.
32+
{{% /notice %}}
33+
34+
The commands in this Learning Path were validated on an `m8g.xlarge` instance running Ubuntu 24.04 LTS Arm64 and Go 1.26.3.
35+
36+
## Check instance availability
37+
38+
Use the AWS CLI to check whether `m8g.xlarge` is available in your selected Region.
39+
40+
Replace `us-east-1` with the Region you want to use.
41+
42+
```console
43+
aws ec2 describe-instance-type-offerings \
44+
--region us-east-1 \
45+
--location-type availability-zone \
46+
--filters Name=instance-type,Values=m8g.xlarge \
47+
--query 'InstanceTypeOfferings[].Location' \
48+
--output table
49+
```
50+
51+
If the command returns one or more Availability Zones, you can use `m8g.xlarge` in that Region.
52+
53+
Run the same command for `m7g.xlarge` if `m8g.xlarge` is not available:
54+
55+
```console
56+
aws ec2 describe-instance-type-offerings \
57+
--region us-east-1 \
58+
--location-type availability-zone \
59+
--filters Name=instance-type,Values=m7g.xlarge \
60+
--query 'InstanceTypeOfferings[].Location' \
61+
--output table
62+
```
63+
64+
You have now selected a repeatable AWS Graviton test machine. You will confirm the default Go runtime environment before running the benchmark.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
---
2+
title: Create a Go GC benchmark
3+
weight: 4
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## Create a benchmark module
10+
11+
Create a small Go module for the benchmark:
12+
13+
```console
14+
mkdir -p $HOME/go-gc-default/parsebench
15+
cd $HOME/go-gc-default
16+
go mod init example.com/go-gc-default
17+
```
18+
19+
Create the benchmark file:
20+
21+
```console
22+
cat > parsebench/parsebench_test.go <<'EOF'
23+
package parsebench
24+
25+
import (
26+
"runtime"
27+
"strconv"
28+
"strings"
29+
"testing"
30+
)
31+
32+
var sink []string
33+
34+
func BenchmarkParseAndAllocate(b *testing.B) {
35+
payload := strings.Repeat("name=arm&runtime=go&gc=default&value=12345;", 2048)
36+
37+
b.ReportAllocs()
38+
39+
var before runtime.MemStats
40+
runtime.ReadMemStats(&before)
41+
42+
b.ResetTimer()
43+
for i := 0; i < b.N; i++ {
44+
parts := strings.Split(payload, ";")
45+
out := make([]string, 0, len(parts))
46+
47+
for _, part := range parts {
48+
if part == "" {
49+
continue
50+
}
51+
fields := strings.SplitN(part, "=", 2)
52+
if len(fields) == 2 {
53+
out = append(out, fields[0]+":"+strconv.Itoa(len(fields[1])))
54+
}
55+
}
56+
57+
sink = out
58+
}
59+
b.StopTimer()
60+
61+
var after runtime.MemStats
62+
runtime.ReadMemStats(&after)
63+
64+
ops := float64(b.N)
65+
gcCycles := after.NumGC - before.NumGC
66+
pauseNs := after.PauseTotalNs - before.PauseTotalNs
67+
68+
if ops > 0 {
69+
b.ReportMetric(float64(gcCycles)/ops, "gc/op")
70+
b.ReportMetric(float64(pauseNs)/ops, "stw-ns/op")
71+
}
72+
if gcCycles > 0 {
73+
b.ReportMetric(float64(pauseNs)/float64(gcCycles), "stw-ns/GC")
74+
}
75+
}
76+
EOF
77+
```
78+
79+
This benchmark repeatedly parses and allocates strings. It reports the default Go benchmark metrics plus three GC-specific metrics:
80+
81+
| Metric | Meaning |
82+
| --- | --- |
83+
| `gc/op` | GC cycles per completed benchmark operation |
84+
| `stw-ns/op` | GC stop-the-world pause nanoseconds per completed operation |
85+
| `stw-ns/GC` | GC stop-the-world pause nanoseconds per GC cycle |
86+
87+
The benchmark reads `runtime.MemStats` before and after the timed loop. It does not set Go runtime tuning variables.
88+
89+
## Confirm the benchmark builds
90+
91+
Run one short benchmark pass:
92+
93+
```console
94+
cd $HOME/go-gc-default
95+
go test ./parsebench -run '^$' -bench BenchmarkParseAndAllocate -benchmem -count 1 -benchtime=2s
96+
```
97+
98+
You should see output with `ns/op`, `B/op`, `allocs/op`, and the GC-specific metrics:
99+
100+
```output
101+
goos: linux
102+
goarch: arm64
103+
pkg: example.com/go-gc-default/parsebench
104+
BenchmarkParseAndAllocate-4 14014 170814 ns/op 0.04553 gc/op 102956 stw-ns/GC 4687 stw-ns/op 163840 B/op 4098 allocs/op
105+
PASS
106+
ok example.com/go-gc-default/parsebench 4.127s
107+
```
108+
109+
Your exact numbers will differ by instance type, Go version, operating system, and system load.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
---
2+
title: Install Go and benchmark tools
3+
weight: 3
4+
5+
### FIXED, DO NOT MODIFY
6+
layout: learningpathall
7+
---
8+
9+
## Install Go on Arm Linux
10+
11+
Install Go on the AWS Graviton instance. The commands below use the Linux Arm64 archive from `go.dev`.
12+
13+
{{% notice Note %}}
14+
The following commands use Go 1.26.3. The same commands work with other Go versions. Replace the archive name and checksum with the values for your version of choice. To find the latest version, see the [Go downloads page](https://go.dev/dl/).
15+
{{% /notice %}}
16+
17+
Download the Go archive and verify the checksum:
18+
19+
```console
20+
cd $HOME
21+
curl -LO https://go.dev/dl/go1.26.3.linux-arm64.tar.gz
22+
echo "9d89a3ea57d141c2b22d70083f2c8459ba3890f2d9e818e7e933b75614936565 go1.26.3.linux-arm64.tar.gz" | sha256sum -c -
23+
```
24+
25+
Install Go under `/usr/local`:
26+
27+
```console
28+
sudo rm -rf /usr/local/go
29+
sudo tar -C /usr/local -xzf go1.26.3.linux-arm64.tar.gz
30+
```
31+
32+
Add Go to your shell path:
33+
34+
```console
35+
export PATH=/usr/local/go/bin:$HOME/go/bin:$PATH
36+
```
37+
38+
To make the path update persistent, add it to your shell profile:
39+
40+
```console
41+
echo 'export PATH=/usr/local/go/bin:$HOME/go/bin:$PATH' >> $HOME/.profile
42+
```
43+
44+
Verify that Go is installed for Arm64 Linux:
45+
46+
```console
47+
go version
48+
go env GOOS GOARCH
49+
```
50+
51+
The output should show `linux` and `arm64`:
52+
53+
```output
54+
linux
55+
arm64
56+
```
57+
58+
## Install Benchstat
59+
60+
Install Benchstat to summarize repeated Go benchmark runs:
61+
62+
```console
63+
go install golang.org/x/perf/cmd/benchstat@latest
64+
```
65+
66+
Verify that Benchstat is available:
67+
68+
```console
69+
benchstat -h
70+
```
71+
72+
You now have Go and Benchstat installed on the AWS Graviton instance.

0 commit comments

Comments
 (0)