GO

golang-performance

Tools and techniques for profiling Go performance, benchmarking code, and reducing latency or memory usage.

Install

mkdir -p .claude/skills/golang-performance && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/9357" && unzip -o skill.zip -d .claude/skills/golang-performance && rm skill.zip

Installs to .claude/skills/golang-performance

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Use when profiling Go applications (pprof), running benchmarks, optimizing memory/CPU usage, or debugging performance bottlenecks in production Go code.
152 chars✓ has a “when” trigger
Advanced

Key capabilities

  • Profile CPU, memory, and goroutine usage
  • Benchmark Go function performance
  • Identify memory leaks and high allocations
  • Tune garbage collection parameters
  • Visualize performance data via pprof web UI

How it works

It utilizes the pprof tool to capture runtime data and the go test framework for benchmarking. It guides users to analyze flat and cumulative time metrics to isolate performance bottlenecks.

Inputs & outputs

You give it
Go source code or binary
You get back
Performance profiles and benchmark statistics

When to use golang-performance

  • Analyze CPU usage via pprof
  • Benchmark Go function performance
  • Investigate memory leaks in Go applications

About this skill

Go Performance Optimization

Overview

This skill provides comprehensive guidance for profiling, benchmarking, and optimizing Go applications. Use this skill when working on performance-critical code, investigating bottlenecks, or optimizing production systems.

When to Use This Skill:

  • Profiling application performance
  • Benchmarking code changes
  • Investigating memory leaks or high allocations
  • Optimizing hot paths
  • Tuning garbage collection
  • Reducing latency in production

Core Tools:

  • pprof - CPU, memory, and goroutine profiling
  • go test -bench - Benchmarking framework
  • go build -gcflags - Escape analysis
  • GOGC and GOMEMLIMIT - GC tuning

1. Profiling with pprof

1.1 CPU Profiling

Enable CPU Profiling in Code:

import (
    "os"
    "runtime/pprof"
)

func main() {
    f, err := os.Create("cpu.prof")
    if err != nil {
        log.Fatal("could not create CPU profile: ", err)
    }
    defer f.Close()

    if err := pprof.StartCPUProfile(f); err != nil {
        log.Fatal("could not start CPU profile: ", err)
    }
    defer pprof.StopCPUProfile()

    // Your application code here
    runApplication()
}

CLI Profiling:

# Profile a test
go test -cpuprofile=cpu.prof -bench=.

# Profile a binary
go test -c
./myapp.test -test.cpuprofile=cpu.prof -test.bench=.

Analysis Commands:

# Interactive web UI (recommended)
go tool pprof -http=:8080 cpu.prof

# Text output - top functions by CPU time
go tool pprof -top cpu.prof

# Top 20 with cumulative time
go tool pprof -top -cum cpu.prof | head -20

# Call graph visualization
go tool pprof -svg cpu.prof > cpu.svg

# Focus on specific function
go tool pprof -focus=processData cpu.prof

# Exclude standard library
go tool pprof -ignore=runtime cpu.prof

Interpreting CPU Profiles:

  • flat: Time spent in function itself (excludes callees)
  • flat%: Percentage of total runtime
  • sum%: Cumulative percentage
  • cum: Time spent in function and callees
  • cum%: Cumulative time percentage

Example Output:

Showing nodes accounting for 2.50s, 83.33% of 3.00s total
      flat  flat%   sum%        cum   cum%
     0.80s 26.67% 26.67%      1.20s 40.00%  processData
     0.60s 20.00% 46.67%      0.90s 30.00%  parseJSON
     0.50s 16.67% 63.34%      0.50s 16.67%  validateInput

Focus optimization on functions with high flat (own time) or cum (total time).


1.2 Memory Profiling

Heap Profiling:

import (
    "os"
    "runtime/pprof"
)

func captureHeapProfile() {
    f, err := os.Create("mem.prof")
    if err != nil {
        log.Fatal("could not create memory profile: ", err)
    }
    defer f.Close()

    // Force GC before capturing heap
    runtime.GC()

    if err := pprof.WriteHeapProfile(f); err != nil {
        log.Fatal("could not write memory profile: ", err)
    }
}

Memory Profiling via CLI:

# Profile memory allocations during test
go test -memprofile=mem.prof -bench=.

# Run benchmark multiple times for stable results
go test -memprofile=mem.prof -bench=. -benchtime=10s

Analysis Commands:

# Web UI showing allocation sites
go tool pprof -http=:8080 mem.prof

# Top allocators
go tool pprof -top mem.prof

# Focus on allocations (inuse_space)
go tool pprof -sample_index=inuse_space -top mem.prof

# Focus on allocation counts (inuse_objects)
go tool pprof -sample_index=inuse_objects -top mem.prof

# Show cumulative allocations (alloc_space)
go tool pprof -sample_index=alloc_space -top mem.prof

# Compare two profiles (before/after)
go tool pprof -base=before.prof after.prof

Memory Profile Types:

  • inuse_space: Memory currently in use (default)
  • inuse_objects: Objects currently in use
  • alloc_space: Total allocations since start
  • alloc_objects: Total object allocations

1.3 Goroutine Profiling

Detect Goroutine Leaks:

import (
    "os"
    "runtime/pprof"
)

func captureGoroutineProfile() {
    f, err := os.Create("goroutine.prof")
    if err != nil {
        log.Fatal("could not create goroutine profile: ", err)
    }
    defer f.Close()

    if err := pprof.Lookup("goroutine").WriteTo(f, 0); err != nil {
        log.Fatal("could not write goroutine profile: ", err)
    }
}

Analysis:

go tool pprof -http=:8080 goroutine.prof
go tool pprof -top goroutine.prof

Goroutine Leak Indicators:

  • Steadily increasing goroutine count
  • Many goroutines blocked on channel recv/send
  • Goroutines without termination mechanism

1.4 HTTP Profiling Endpoint (Production-Safe)

Enable pprof HTTP Server:

import (
    _ "net/http/pprof"
    "net/http"
)

func main() {
    // Start pprof server on separate port (localhost only)
    go func() {
        log.Println("pprof server listening on localhost:6060")
        log.Println(http.ListenAndServe("localhost:6060", nil))
    }()

    // Your application here
    runServer()
}

Access Profiles via HTTP:

# CPU profile (30 seconds)
curl http://localhost:6060/debug/pprof/profile?seconds=30 > cpu.prof

# Heap profile
curl http://localhost:6060/debug/pprof/heap > heap.prof

# Goroutine profile
curl http://localhost:6060/debug/pprof/goroutine > goroutine.prof

# Analyze immediately
go tool pprof http://localhost:6060/debug/pprof/profile

# Web UI
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/profile

Available Endpoints:

  • /debug/pprof/ - Index of all profiles
  • /debug/pprof/profile - CPU profile
  • /debug/pprof/heap - Heap profile
  • /debug/pprof/goroutine - Goroutine stack traces
  • /debug/pprof/threadcreate - Thread creation profile
  • /debug/pprof/block - Blocking profile
  • /debug/pprof/mutex - Mutex contention profile

Production Security:

// Only expose on localhost
http.ListenAndServe("localhost:6060", nil)

// Or use SSH port forwarding
// ssh -L 6060:localhost:6060 user@production-host
// Then access http://localhost:6060/debug/pprof/

2. Benchmarking

2.1 Basic Benchmarks

Simple Benchmark:

func BenchmarkStringConcat(b *testing.B) {
    for i := 0; i < b.N; i++ {
        result := "hello" + " " + "world"
        _ = result // Prevent compiler optimization
    }
}

Benchmark with Setup:

func BenchmarkProcessData(b *testing.B) {
    data := generateTestData(1000)
    b.ResetTimer() // Exclude setup time

    for i := 0; i < b.N; i++ {
        processData(data)
    }
}

Running Benchmarks:

# Run all benchmarks
go test -bench=.

# Run specific benchmark
go test -bench=BenchmarkStringConcat

# Benchmark with memory statistics
go test -bench=. -benchmem

# Run multiple iterations for stability
go test -bench=. -count=5

# Longer benchmark time for accurate results
go test -bench=. -benchtime=10s

# CPU profile during benchmark
go test -bench=. -cpuprofile=cpu.prof

2.2 Sub-Benchmarks

Compare Multiple Implementations:

func BenchmarkStringBuilding(b *testing.B) {
    items := []string{"hello", "world", "foo", "bar"}

    b.Run("Concat", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            result := ""
            for _, item := range items {
                result += item
            }
            _ = result
        }
    })

    b.Run("StringBuilder", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var sb strings.Builder
            for _, item := range items {
                sb.WriteString(item)
            }
            _ = sb.String()
        }
    })

    b.Run("Join", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            result := strings.Join(items, "")
            _ = result
        }
    })
}

Output:

BenchmarkStringBuilding/Concat-8          500000    3245 ns/op    96 B/op    5 allocs/op
BenchmarkStringBuilding/StringBuilder-8   2000000    825 ns/op    64 B/op    1 allocs/op
BenchmarkStringBuilding/Join-8            2000000    780 ns/op    48 B/op    1 allocs/op

2.3 Memory Reporting

Track Allocations:

func BenchmarkWithAllocs(b *testing.B) {
    b.ReportAllocs()

    for i := 0; i < b.N; i++ {
        data := make([]int, 1000)
        _ = data
    }
}

Output Interpretation:

BenchmarkWithAllocs-8    200000    8234 ns/op    8192 B/op    1 allocs/op
                         ------    ----           ----         ----
                         iters     ns/op          bytes/op     allocs/op
  • ns/op: Nanoseconds per operation
  • B/op: Bytes allocated per operation
  • allocs/op: Number of allocations per operation

Zero Allocation Goal:

// Bad: 2 allocations
func process(data string) string {
    upper := strings.ToUpper(data) // 1 alloc
    return strings.TrimSpace(upper) // 1 alloc
}

// Better: 1 allocation (reuse buffer)
func process(data string) string {
    var sb strings.Builder
    sb.Grow(len(data))
    for _, r := range data {
        if !unicode.IsSpace(r) {
            sb.WriteRune(unicode.ToUpper(r))
        }
    }
    return sb.String()
}

2.4 Benchmark Analysis with benchstat

Compare Before/After:

# Baseline
go test -bench=. -count=10 > old.txt

# After optimization
go test -bench=. -count=10 > new.txt

# Statistical comparison
go install golang.org/x/perf/cmd/benchstat@latest
benchstat old.txt new.txt

Example Output:

name                old time/op    new time/op    delta
StringConcat-8      3.24µs ± 2%    0.82µs ± 1%   -74.69%  (p=0.000 n=10+10)

name                old alloc/op   new alloc/op   delta
StringConcat-8       96.0B ± 0%     64.0B ± 0%   -33.33%  (p=0.000 n=10+10)

name                old allocs/op  new allocs/op  delta
StringConcat-8        5.00 ± 0%      1.00 ± 0%   -80.00%  (p=0.000 n=10+10)

Interpretation:

  • ±2% - Variance across runs
  • (p=0.000) - Statistical significance (p < 0.05 = significant)
  • n=10+10 - Number of samples used

3. Memory Optimiza


Content truncated.

When not to use it

  • General Go language syntax questions
  • Database query optimization without profiling data

Prerequisites

Go development environmentpprof tool

Limitations

  • Profiling adds overhead to application execution
  • Requires production-safe endpoint configuration for live systems

How it compares

This approach relies on empirical profiling data rather than manual code inspection to identify performance issues.

Compared to similar skills

golang-performance side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
golang-performance (this skill)46moReviewAdvanced
go-benchmark01moReviewIntermediate
common-performance-engineering01moNo flagsIntermediate
golang-pro144moNo flagsAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

More by MadAppGang

View all by MadAppGang

claudish-usage

MadAppGang

CRITICAL - Guide for using Claudish CLI ONLY through sub-agents to run Claude Code with any AI model (OpenRouter, Gemini, OpenAI, local models). NEVER run Claudish directly in main context unless user explicitly requests it. Use when user mentions external AI models, Claudish, OpenRouter, Gemini, OpenAI, Ollama, or alternative models. Includes mandatory sub-agent delegation patterns, agent selection guide, file-based instructions, and strict rules to prevent context window pollution.

442

golang

MadAppGang

Use when building Go backend services, implementing goroutines/channels, handling errors idiomatically, writing tests with testify, or following Go best practices for APIs/CLI tools.

313

schemas

MadAppGang

YAML frontmatter schemas for Claude Code agents and commands. Use when creating or validating agent/command files.

34

external-model-selection

MadAppGang

Choose optimal external AI models for code analysis, bug investigation, and architectural decisions. Use when consulting multiple LLMs via claudish, comparing model perspectives, or investigating complex Go/LSP/transpiler issues. Provides empirically validated model rankings (91/100 for MiniMax M2, 83/100 for Grok Code Fast) and proven consultation strategies based on real-world testing.

218

adr-documentation

MadAppGang

Architecture Decision Records (ADR) documentation practice. Use when documenting architectural decisions, recording technical trade-offs, creating decision logs, or establishing architectural patterns. Trigger keywords - "ADR", "architecture decision", "decision record", "trade-offs", "architectural decision", "decision log".

12

claudemem-orchestration

MadAppGang

Use when orchestrating multi-agent code analysis with claudemem. Run claudemem once, share output across parallel agents. Enables parallel investigation, consensus analysis, and role-based command mapping.

12

Search skills

Search the agent skills registry