Golang Performance
Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe
- Skill
- Guidelines
- Software development
- MIT
- Low risk
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured approach to identifying and resolving performance bottlenecks in Golang applications. It emphasizes a data-driven philosophy: profile first, hypothesize, apply a single optimization, and re-measure. It covers critical areas including memory allocation reduction, CPU efficiency, cache locality, runtime tuning, and algorithmic improvements. The skill is designed to guide developers through the iterative cycle of benchmarking and optimization, ensuring that changes are statistically significant and documented.
What it does
- Provides a decision tree for mapping pprof signals to specific optimization patterns.
- Defines an iterative workflow for benchmarking, diagnosing, and verifying performance improvements.
- Offers deep-dive patterns for memory management, CPU hot-path optimization, and I/O efficiency.
- Includes guidance on avoiding common Go performance pitfalls like lock contention and unnecessary allocations.
- Supports architectural performance reviews through multi-agent orchestration.
- Integrates with standard Go tooling like benchstat for statistical validation of changes.
When to use it
- When profiling or benchmarks have identified a specific performance bottleneck.
- When conducting a performance-focused code review to suggest structural improvements.
- When you need to reduce GC pressure or latency in high-throughput Go services.
- When you need to verify if a proposed optimization actually yields a measurable gain.
Advantages
- Reduces reliance on intuition by enforcing a measurement-first methodology.
- Provides actionable patterns for common Go-specific performance issues.
- Ensures performance improvements are statistically validated using benchstat.
- Helps prevent performance regressions by encouraging documented benchmarks.
- Enables systematic identification of bottlenecks across memory, CPU, and I/O.
Usage examples
- “My service has high GC overhead. How can I reduce allocations?”
- The agent will guide you through identifying allocation hot-spots using pprof, suggest patterns like sync.Pool or pre-allocation, and help you verify the reduction in allocs/op using benchstat.
- “Review this package for performance anti-patterns.”
- The agent will perform an architectural scan, checking for issues like unbounded goroutines, missing connection pools, or inefficient data structures, and provide a report with optimization recommendations.
- “I suspect a function is CPU-bound. How do I optimize it?”
- The agent will help you establish a baseline benchmark, analyze the CPU profile to check for inlining or cache locality issues, and suggest specific code changes to improve execution speed.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/samber/golang-performance/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip golang-performance.zip -d .agents/skills
Documentation by samber
Persona: You are a Go performance engineer. You never optimize without profiling first — measure, hypothesize, change one thing, re-measure.
Thinking mode: Reason as thoroughly as possible for performance optimization — shallow analysis misidentifies bottlenecks and deep reasoning ensures the right optimization is applied to the right problem. On Claude Code, use ultrathink to trigger extended thinking explicitly.
Orchestration mode: Fan out the three sub-agents described in Review mode (architecture) (allocation and memory layout, I/O and concurrency, algorithmic complexity and caching) for a broad architectural performance review. A single hot-path review stays sequential; fan-out only pays off at package/service scope. On Claude Code, use ultracode to opt into multi-agent orchestration explicitly.
Modes:
Review mode (architecture) — broad scan of a package or service for structural anti-patterns (missing connection pools, unbounded goroutines, wrong data structures). Use up to 3 parallel sub-agents split by concern: (1) allocation and memory layout, (2) I/O and concurrency, (3) algorithmic complexity and caching.
Review mode (hot path) — focused analysis of a single function or tight loop identified by the caller. Work sequentially; one sub-agent is sufficient.
Optimize mode — a bottleneck has been identified by profiling. Follow the iterative cycle (define metric → baseline → diagnose → improve → compare) sequentially — one change at a time is the discipline.
Dependencies:
benchstat:
go install golang.org/x/perf/cmd/benchstat@latest
Go Performance Optimization
Core Philosophy
Profile before optimizing — intuition about bottlenecks is wrong ~80% of the time. Use pprof to find actual hot spots (→ See
samber/cc-skills-golang@golang-troubleshootingskill)Allocation reduction yields the biggest ROI — Go's GC is fast but not free. Reducing allocations per request often matters more than micro-optimizing CPU
Document optimizations — add code comments explaining why a pattern is faster, with benchmark numbers when available. Future readers need context to avoid reverting an "unnecessary" optimization
Rule Out External Bottlenecks First
Before optimizing Go code, verify the bottleneck is in your process — if 90% of latency is a slow DB query or API call, reducing allocations won't help.
Diagnose: 1- fgprof — captures on-CPU and off-CPU (I/O wait) time; if off-CPU dominates, the bottleneck is external 2- go tool pprof (goroutine profile) — many goroutines blocked in net.(*conn).Read or database/sql = external wait 3- Distributed tracing (OpenTelemetry) — span breakdown shows which upstream is slow
When external: optimize that component instead — query tuning, caching, connection pools, circuit breakers (→ See samber/cc-skills-golang@golang-database skill, Caching Patterns).
Iterative Optimization Methodology
The cycle: Define Goals → Benchmark → Diagnose → Improve → Benchmark
Define your metric — latency, throughput, memory, or CPU? Without a target, optimizations are random
Write an atomic benchmark — isolate one function per benchmark to avoid result contamination (→ See
samber/cc-skills-golang@golang-benchmarkskill)Measure baseline —
go test -bench=BenchmarkMyFunc -benchmem -count=6 ./pkg/... | tee /tmp/report-1.txtDiagnose — use the Diagnose lines in each deep-dive section to pick the right tool
Improve — apply ONE optimization at a time with an explanatory comment
Compare —
benchstat /tmp/report-1.txt /tmp/report-2.txtto confirm statistical significanceCommit — paste the benchstat output in the commit body so reviewers and future readers see the exact improvement; follow the
perf(scope): summarycommit typeRepeat — increment report number, tackle next bottleneck
Refer to library documentation for known patterns before inventing custom solutions. Keep all /tmp/report-*.txt files as an audit trail.
When multiple candidate optimizations compete for the same bottleneck, implement each in an isolated worktree via a separate sub-agent — then → See samber/cc-skills-golang@golang-benchmark skill for comparing the variants and its serial-measurement caveat (concurrent benchmark runs on shared CPU contaminate results, even when the implementations themselves were built in parallel).
Decision Tree: Where Is Time Spent?
| Bottleneck | Signal (from pprof) | Action |
|---|---|---|
| Too many allocations | alloc_objects high in heap profile | Memory optimization |
| CPU-bound hot loop | function dominates CPU profile | CPU optimization |
| GC pauses / OOM | high GC%, container limits | Runtime tuning |
| Network / I/O latency | goroutines blocked on I/O | I/O & networking |
| Repeated expensive work | same computation/fetch multiple times | Caching patterns |
| Wrong algorithm | O(n²) where O(n) exists | Algorithmic complexity |
| Lock contention | mutex/block profile hot | → See samber/cc-skills-golang@golang-concurrency skill |
| Slow queries | DB time dominates traces | → See samber/cc-skills-golang@golang-database skill |
Common Mistakes
| Mistake | Fix |
|---|---|
| Optimizing without profiling | Profile with pprof first — intuition is wrong ~80% of the time |
Default http.Client without Transport | MaxIdleConnsPerHost defaults to 2; set to match your concurrency level |
| Logging in hot loops | Log calls prevent inlining and allocate even when the level is disabled. Use slog.LogAttrs |
panic/recover as control flow | panic allocates a stack trace and unwinds the stack; use error returns |
unsafe without benchmark proof | Only justified when profiling shows >10% improvement in a verified hot path |
| No GC tuning in containers | Set GOMEMLIMIT to 80-90% of container memory to prevent OOM kills |
reflect.DeepEqual in production | 50-200x slower than typed comparison; use slices.Equal, maps.Equal, bytes.Equal |
Deep Dives
Memory Optimization — allocation patterns, backing array leaks, sync.Pool, struct alignment
CPU Optimization — inlining, cache locality, false sharing, ILP, reflection avoidance
I/O & Networking — HTTP transport config, streaming, JSON performance, cgo, batch operations
Runtime Tuning — GOGC, GOMEMLIMIT, GC diagnostics, GOMAXPROCS, PGO
Caching Patterns — algorithmic complexity, compiled patterns, singleflight, work avoidance
Production Observability — Prometheus metrics, PromQL queries, continuous profiling, alerting rules
CI Regression Detection
Automate benchmark comparison in CI to catch regressions before they reach production. → See samber/cc-skills-golang@golang-benchmark skill for benchdiff and cob setup.
Cross-References
→ See
samber/cc-skills-golang@golang-benchmarkskill for benchmarking methodology,benchstat, andb.Loop()(Go 1.24+)→ See
samber/cc-skills-golang@golang-troubleshootingskill for pprof workflow, escape analysis diagnostics, and performance debugging→ See
samber/cc-skills-golang@golang-data-structuresskill for slice/map preallocation andstrings.Builder→ See
samber/cc-skills-golang@golang-concurrencyskill for worker pools,sync.PoolAPI, goroutine lifecycle, and lock contention→ See
samber/cc-skills-golang@golang-safetyskill for defer in loops, slice backing array aliasing→ See
samber/cc-skills-golang@golang-databaseskill for connection pool tuning and batch processing→ See
samber/cc-skills-golang@golang-observabilityskill for continuous profiling in production
Files
- ATTRIBUTION.mdAdded by AutoGineer911 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md9.3 KiB
- assets/prometheus-alerts.yml662 B
- evals/evals.json91.6 KiB
- references/caching.md8.7 KiB
- references/cpu.md18.3 KiB
- references/io-networking.md14.2 KiB
- references/memory.md10.1 KiB
- references/observability.md5.5 KiB
- references/runtime.md11.8 KiB
references/cpu.md
CPU Optimization
CPU-bound bottlenecks show up as functions dominating the CPU profile. The patterns below target the most common causes: missed inlining opportunities, poor cache utilization, and unnecessary computation.
Table of Contents
Function Inlining
Value receivers enable inlining
Cache Locality
Row-major traversal
Contiguous 2D allocation
Struct of Arrays (SoA) vs Array of Structs (AoS)
Pointer-heavy vs value-heavy data
False Sharing
Instruction-Level Parallelism
SIMD (Single Instruction, Multiple Data)
Handling CPU-specific instruction sets
Tight Loops and the Scheduler
Reflection and Type Assertions
Monotonic Time
Function Inlining
Diagnose: 1- go tool pprof (CPU profile) — look for hot functions with high cumulative CPU time; if a small helper dominates the profile, it's likely not being inlined 2- go build -gcflags="-m" — grep for "cannot inline" on your hot-path functions; the reason (e.g., "function too complex", "unhandled op") tells you what to simplify
The Go compiler inlines small functions, eliminating call overhead. Functions that are too complex (loops, many statements, or calls to non-inlineable functions) won't be inlined — this matters in tight loops called millions of times.
// Bad — log call prevents inlining
func abs(x int) int {
if x < 0 {
log.Printf("negative: %d", x) // blocks inlining
return -x
}
return x
}
// Good — simple enough to inline
func abs(x int) int {
if x < 0 { return -x }
return x
}Check inlining decisions:
go build -gcflags="-m" ./... 2>&1 | grep "can inline"
go build -gcflags="-m" ./... 2>&1 | grep "inlining call"Move side effects (logging, metrics) outside hot-path functions or guard them with conditional checks.
Value receivers enable inlining
Value receivers allow the compiler to fully inline fluent method chains. Pointer receivers add indirection that blocks inlining:
// Pointer receiver — indirection prevents inlining, constant overhead per call
func (c *config) WithTimeout(d time.Duration) *config { c.timeout = d; return c }
// Value receiver — fully inlined, -80% time in fluent chains
func (c config) WithTimeout(d time.Duration) config { c.timeout = d; return c }Cache Locality
Diagnose: 1- go tool pprof (CPU profile) — look for loops over slices/matrices consuming disproportionate CPU; cache-miss-heavy code shows high runtime.memmove or flat time in simple index operations 2- go test -bench — benchmark row-first vs column-first traversal; expect 10-50x difference on large matrices purely from cache effects
Modern CPUs fetch data in 64-byte cache lines. Sequential memory access is dramatically faster than random access because the prefetcher can load the next cache line before you need it.
Row-major traversal
Go stores 2D arrays in row-major order. Column-first traversal jumps across memory, causing cache misses:
// Bad — column-first, jumps across memory (~10M cache misses)
for col := 0; col < 1024; col++ {
for row := 0; row < 1024; row++ {
sum += matrix[row][col]
}
}
// Good — row-first, sequential access (~125K cache misses)
for row := 0; row < 1024; row++ {
for col := 0; col < 1024; col++ {
sum += matrix[row][col]
}
}Performance difference: 10-50x purely from cache effects.
Contiguous 2D allocation
Allocating each row separately scatters data across the heap:
// Bad — N separate allocations, poor cache locality
matrix := make([][]float64, rows)
for i := range matrix { matrix[i] = make([]float64, cols) }
// Good — single contiguous allocation, cache-friendly
data := make([]float64, rows*cols)
matrix := make([][]float64, rows)
for i := range matrix { matrix[i] = data[i*cols : (i+1)*cols] }Struct of Arrays (SoA) vs Array of Structs (AoS)
When iterating over a single field of a struct, AoS wastes cache space loading unused fields:
// AoS — loading each Point (24 bytes) to read only x (8 bytes) = 66% cache waste
type Point struct { x, y, z float64 }
points := make([]Point, n)
for i := range points { sum += points[i].x }
// SoA — all x values contiguous, 100% cache utilization
type Points struct { xs, ys, zs []float64 }
for i := range ps.xs { sum += ps.xs[i] }Use SoA when iterating over a subset of fields (physics, graphics, analytics). AoS is fine when accessing all fields together or for small structs.
Pointer-heavy vs value-heavy data
Index-based data structures (nodes stored in a contiguous array, referenced by index) beat pointer-based structures for cache locality:
// Pointer-based tree — each node scattered in heap, random cache misses
type Node struct { value int; left, right *Node }
// Index-based tree — nodes in contiguous array, cache-friendly
type Tree struct { nodes []Node }
type Node struct { value int; left, right int } // indices into nodesFalse Sharing
Diagnose: 1- go tool pprof (CPU profile + mutex profile) — look for atomic operations or counter updates consuming unexpectedly high CPU; in the mutex profile, look for contention on variables that shouldn't need locking 2- go test -bench — benchmark concurrent counter increments; if adding goroutines makes it slower instead of faster, false sharing is likely
When goroutines update variables that share the same 64-byte CPU cache line, each write invalidates the other core's cache, causing severe degradation:
// Bad — a and b on same cache line, cores fight for it
type Counters struct { a, b int64 }
// Good — separate cache lines, no interference
type Counters struct {
a int64 // 8 bytes
_ [56]byte // 64 - 8 = 56 bytes padding
b int64 // 8 bytes
}Only apply cache-line padding when profiling confirms contention on concurrent counters/flags.
Instruction-Level Parallelism
Diagnose: 1- go tool pprof (CPU profile) — look for tight arithmetic loops (sum, dot product) where the loop body itself dominates CPU; these are candidates for multi-accumulator optimization 2- go test -bench — benchmark single vs multi-accumulator versions; expect 2-4x improvement when the loop is truly CPU-bound with a dependency chain
Modern CPUs execute multiple independent instructions simultaneously. A single accumulator creates a dependency chain — each addition waits for the previous one:
// Bad — sequential dependency, CPU pipeline stalls
var total int64
for _, v := range data { total += v }
// Good — 4 independent accumulators, CPU pipelines all 4 in parallel
var s0, s1, s2, s3 int64
limit := len(data) - len(data)%4
for i := 0; i < limit; i += 4 {
s0 += data[i]; s1 += data[i+1]; s2 += data[i+2]; s3 += data[i+3]
}
for i := limit; i < len(data); i++ { s0 += data[i] }
total := s0 + s1 + s2 + s3Expect 2-4x improvement for tight arithmetic loops. Only use when profiling shows the loop is a bottleneck.
SIMD (Single Instruction, Multiple Data)
Diagnose: 1- go tool pprof (CPU profile) — confirm a numeric inner loop consumes >20% of CPU; SIMD only helps CPU-bound numeric work, not allocation or I/O bottlenecks 2- go test -bench — measure the loop's baseline ns/op; provides the reference point to validate SIMD gains 3- go build -gcflags="-d=ssa/prove/debug=2" — check if the compiler already auto-vectorized the loop; look for "Proved" bounds-check eliminations that enable vectorization 4- GOSSAFUNC=MyFunc go build — generate SSA dump (ssa.html) to inspect whether the compiler produces vector instructions for the hot loop 5- go tool objdump -s MyFunc ./binary — verify the final assembly contains SIMD instructions (e.g., VMOVAPD, VADDPD on amd64) rather than scalar equivalents
Go 1.26+ includes an experimental simd/archsimd package (requires GOEXPERIMENT=simd flag) providing low-level, architecture-specific SIMD intrinsics — amd64 with 128/256/512-bit vectors, and arm64/wasm at 128-bit as of Go 1.27. Go 1.27 also adds a portable simd package (vector types like Int8s, Float32s) that works across architectures and falls back to scalar code without hardware support. For broader portability, the compiler auto-vectorizes simple loops, and several strategies exist.
Options for explicit SIMD in Go:
Experimental
simd/simd/archsimd(Go 1.26+, speculative) — Direct SIMD intrinsics via vector types with CPU feature detection.simd(Go 1.27+) is portable across architectures;simd/archsimdis architecture-specific (amd64, plus arm64/wasm as of Go 1.27). Use with caution: both are experimental, in-progress APIs (GOEXPERIMENT=simd) whose package paths and type names are subject to change before stabilization. Not covered by Go 1 compatibility guarantees, and should never be exposed in public APIs. Verify the actual import path and API against the Go toolchain you are using.// Requires: GOEXPERIMENT=simd go build // WARNING: experimental API — package path and types may change import "simd/archsimd" v := archsimd.Int32x4{1, 2, 3, 4}Let the compiler do it — write simple, idiomatic loops on
[]float64/[]int32slices. Check auto-vectorization:go build -gcflags="-d=ssa/prove/debug=2" ./...math/bits— operations likeOnesCount,LeadingZeros,RotateLeftmap directly to hardware instructions (POPCNT, CLZ, ROL)Hand-written assembly —
.sfiles with AVX2/NEON instructions for critical inner loops. Libraries likeklauspost/compressandminio/sha256-simduse this approachThird-party vectorized libraries — for common operations (hashing, compression, encoding), use libraries that already have optimized SIMD implementations rather than writing your own
Handling CPU-specific instruction sets
Hand-written assembly unlocks higher performance but couples code to specific CPU features (AVX2, NEON, etc.). Three strategies exist:
1. Compile on a production-similar machine
Build binaries on hardware matching your deployment target, so the compiler generates code for the exact CPU instruction set available at runtime:
# Compiling on production hardware ensures optimal code generation
# for that specific CPU architecture and generation
ssh prod-server "cd /path && go build -o app ."Tradeoff: Simplest approach, but requires access to production hardware and different binaries per CPU type (Intel vs AMD vs Apple Silicon). Breaks CI/CD portability.
2. Runtime CPU feature detection + multiple implementations
Implement the function multiple times — one for each CPU capability — and dispatch at runtime:
// dispatch.go
var sumImpl func([]int64) int64
func init() {
if cpu.X86.HasAVX2 {
sumImpl = sumAVX2
} else {
sumImpl = sumGeneric
}
}
func Sum(data []int64) int64 {
return sumImpl(data)
}
// sum_generic.go
func sumGeneric(data []int64) int64 {
var total int64
for _, v := range data { total += v }
return total
}
// sum_amd64.s
TEXT ·sumAVX2(SB), NOSPLIT, $0-32
// AVX2 implementation
VMOVAPD (SI), Y0
// ...Tradeoff: Single binary works everywhere; trades one function-call dispatch overhead for full CPU feature utilization. Libraries like encoding/base64 and sha256 use this pattern.
3. Compile-time selection with //go:build tags
Use conditional compilation to generate different code at build time for each target:
// sum_fast.go
//go:build amd64 && !nosimd
package mylib
// AVX2 assembly via cgo or inline
func Sum(data []int64) int64 {
return sumAVX2(data) // or calls to .s file
}
// sum_generic.go
//go:build !amd64 || nosimd
package mylib
func Sum(data []int64) int64 {
var total int64
for _, v := range data { total += v }
return total
}Build different binaries per target:
GOOS=linux GOARCH=amd64 go build -o app-avx2 . # Uses sum_fast.go
GOOS=darwin GOARCH=arm64 go build -o app-neon . # Uses sum_generic.go
go build -tags=nosimd -o app-safe . # Fallback everywhereTradeoff: Zero runtime overhead; each binary is fully optimized for its target. Requires shipping multiple binaries and coordinating which binary runs where.
When SIMD is NOT worth pursuing:
Go's lack of intrinsics means SIMD requires assembly — high maintenance burden, platform-specific, and harder to debug
Auto-vectorization covers the most common cases (simple numeric loops)
If your bottleneck is allocations or I/O, SIMD won't help
Recommendation:
Start with auto-vectorization.
For Go 1.27+, evaluate the portable
simdpackage for cross-architecture code, orsimd/archsimdfor architecture-specific tuning (amd64, arm64, wasm) — remembering both are experimental.Move to runtime detection (option 2 above) if profiling shows a bottleneck and the code needs to run on heterogeneous hardware.
Only use compile-time selection (option 3) if you control the deployment environment and can test each per-binary variant.
Only invest in hand-written SIMD when profiling shows a numeric inner loop consuming >20% of CPU and the compiler isn't auto-vectorizing it.
Tight Loops and the Scheduler
Diagnose: 1- go tool pprof (goroutine profile) — look for many goroutines stuck in "runnable" state (waiting for CPU) while one goroutine monopolizes execution 2- go tool trace — visualize goroutine scheduling over time; look for long uninterrupted execution spans on one goroutine while others show scheduling gaps 3- GODEBUG=schedtrace=1000 — print scheduler state every second; look for unbalanced runqueue counts across P's indicating one P is starved 4- runtime/metrics (/sched/latencies:seconds) — measure how long goroutines wait before getting CPU; high p99 latencies confirm starvation 5- Prometheus rate(process_cpu_seconds_total[2m]) — monitor if CPU usage hits GOMAXPROCS ceiling; if saturated while other goroutines are starved, a tight loop is monopolizing P's
A goroutine running a CPU-intensive tight loop without function calls may not yield to the scheduler, starving other goroutines. Go 1.14+ added asynchronous preemption, but very tight loops with fully inlined operations can still cause issues:
// Potential starvation — pure computation, no function calls
for { x = x*a + b }
// Safe — non-inlined call triggers preemption check
for item := range work {
processBatch(item) // function call = preemption point
}When to use non-inlined calls for scheduling: Use non-inlined function calls when:
The loop runs for a long time (hundreds of milliseconds or more of uninterrupted computation)
Other goroutines are waiting to run (e.g., handling requests, I/O completion, channel operations)
The loop contains only arithmetic or memory operations with no function calls
For short bursts of computation (< 10ms), preemption isn't critical and inlining for CPU efficiency takes priority.
Detecting scheduler starvation: Use these tools to confirm goroutines are being starved:
go tool pprofgoroutine profile — shows goroutines stuck in "runnable" state (waiting for CPU). If many goroutines are runnable while one dominates CPU, starvation is happeninggo tool trace— visualizes goroutine scheduling over time. Look for gaps where goroutines aren't running because one goroutine monopolized the schedulerruntime/metrics(Go 1.19+) — measure/sched/latencies:secondsto quantify how long goroutines wait for CPUObservable symptoms — high response latency, requests timing out, uneven request distribution, goroutine counts climbing
Preventing inlining with //go:noinline: If you have a function that's normally inlinable (small, hot) but you specifically want it to not inline to force scheduler preemption checks, use the //go:noinline compiler directive:
//go:noinline
func processBatch(item WorkItem) {
// CPU-intensive work here
// This call site will NOT be inlined, even if the function is small
// The function call itself becomes a preemption point for the scheduler
}
// In tight loop
for item := range work {
processBatch(item) // Guaranteed preemption point
}Trade-off: Using //go:noinline prevents inlining, which:
Pros: Guarantees scheduler preemption checks; prevents goroutine starvation
Cons: Adds function call overhead (~10-30 CPU cycles); reduces instruction-level parallelism (ILP) in the caller
Only use //go:noinline if profiling shows that scheduler preemption starvation is actually blocking other goroutines. Unnecessary //go:noinline directives penalize throughput and latency.
Reflection and Type Assertions
Diagnose: 1- go tool pprof (CPU profile) — look for reflect.Value.*, reflect.DeepEqual, or fmt.Sprintf (which uses reflect internally) appearing in hot paths 2- go test -bench — compare reflection-based vs typed versions; expect 10-200x difference depending on the reflection operation
reflectin hot paths — 10-100x slower due to type introspection and boxing. Replace with generics or typed codereflect.DeepEqual— 50-200x slower than typed comparisons. Useslices.Equal,maps.Equal,bytes.Equal(Go 1.21+)Type switch vs repeated assertions — type switch dispatches in one evaluation:
// Bad — evaluates interface multiple times
if s, ok := v.(string); ok { return s }
if i, ok := v.(int); ok { return strconv.Itoa(i) }
// Good — single dispatch
switch v := v.(type) {
case string: return v
case int: return strconv.Itoa(v)
}Monotonic Time
Diagnose: 1- go test -bench — benchmark time.Since(start) vs time.Now().Sub(start); expect a small but consistent improvement from monotonic clock avoiding wall-clock syscall
time.Since(start) uses the monotonic clock, which is immune to wall-clock adjustments (NTP, DST) and slightly faster:
var appStart = time.Now() // captures monotonic time + wall-clock on program start
func myFunc() {
// Compare durations, not wall-clock times
elapsed := time.Since(appStart)
if elapsed > threshold { ... }
}Requirements and compatibility
Written for: Claude Code, OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Claude Code frontmatter
The author says: Designed for Claude Code, Codex or similar harness, and for projects using Golang.
Security
What our scanner noticed· Low risk
- Instructions that use the network
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- samber
- Publisher
- Samuel Berthe (Community)
- Distributed by
- Samuel Berthe
- Source
- samber/cc-skills-golang · skills/golang-performance · commit bb1d43d
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by samber or Samuel Berthe.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download golang-performance.zip