Skip to content

Instantly share code, notes, and snippets.

@alexrios
Created February 5, 2026 22:57
Show Gist options
  • Select an option

  • Save alexrios/2c4d118695c4e24da36c3136ef334018 to your computer and use it in GitHub Desktop.

Select an option

Save alexrios/2c4d118695c4e24da36c3136ef334018 to your computer and use it in GitHub Desktop.
Green Tea GC benchmarking examples — companion code for https://alexrios.me/blog/greentea-gc-series-benchmarking
// Package main provides benchmark examples for verifying Green Tea GC blog post code.
// These benchmarks demonstrate proper Go benchmarking patterns.
package main
import (
"runtime"
"runtime/metrics"
"testing"
)
// Sample data structure to allocate
type SmallObject struct {
ID int64
Name string
Value float64
Tags [4]string
}
// BenchmarkAllocation demonstrates allocation-heavy workload
// that would benefit from Green Tea GC optimization.
func BenchmarkAllocation(b *testing.B) {
for b.Loop() {
// Allocate small objects (< 512 bytes) - Green Tea's target
obj := &SmallObject{
ID: 42,
Name: "test",
Value: 3.14,
Tags: [4]string{"a", "b", "c", "d"},
}
_ = obj
}
}
// BenchmarkAllocationOldStyle shows the pre-Go 1.24 pattern for comparison.
// The b.Loop() pattern above is preferred in Go 1.24+.
func BenchmarkAllocationOldStyle(b *testing.B) {
for i := 0; i < b.N; i++ {
obj := &SmallObject{
ID: int64(i),
Name: "test",
Value: 3.14,
Tags: [4]string{"a", "b", "c", "d"},
}
_ = obj
}
}
// BenchmarkSingleAllocation shows that runtime.KeepAlive alone doesn't force heap allocation.
// Escape analysis still keeps the object on the stack because the pointer never actually escapes.
func BenchmarkSingleAllocation(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
obj := &SmallObject{
ID: 42,
Name: "test",
Value: 3.14,
Tags: [4]string{"a", "b", "c", "d"},
}
runtime.KeepAlive(obj)
}
}
// BenchmarkWithGCPressure creates enough allocations to trigger GC.
func BenchmarkWithGCPressure(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
// Allocate a slice of small objects
objects := make([]*SmallObject, 1000)
for j := 0; j < 1000; j++ {
objects[j] = &SmallObject{
ID: int64(j),
Name: "pressure-test",
Value: float64(j) * 1.5,
}
}
// Force objects to escape
runtime.KeepAlive(objects)
}
}
// BenchmarkLargeObjects shows workload that won't benefit from Green Tea.
// Green Tea only optimizes objects <= 512 bytes.
func BenchmarkLargeObjects(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
// Large allocation - won't benefit from Green Tea
data := make([]byte, 8192)
data[0] = 1
runtime.KeepAlive(data)
}
}
// TestReadGCMetrics demonstrates the runtime/metrics API usage from the blog post.
func TestReadGCMetrics(t *testing.T) {
// Read GC metrics - this is the exact code from the blog post
samples := []metrics.Sample{
{Name: "/gc/heap/allocs:bytes"},
{Name: "/gc/heap/goal:bytes"},
{Name: "/gc/scan/heap:bytes"},
{Name: "/sched/pauses/stopping/gc:seconds"},
}
metrics.Read(samples)
// The /sched/pauses/stopping/gc:seconds returns a histogram
// showing STW (Stop The World) pause distribution
for _, s := range samples {
switch s.Value.Kind() {
case metrics.KindUint64:
println(s.Name, "=", s.Value.Uint64())
case metrics.KindFloat64:
println(s.Name, "=", s.Value.Float64())
case metrics.KindFloat64Histogram:
println(s.Name, "= <histogram>")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment