> go-performance

Use when optimizing Go code, investigating slow performance, or writing performance-critical sections. Also use when a user mentions slow Go code, string concatenation in loops, or asks about benchmarking, even if the user doesn't explicitly mention performance patterns. Does not cover concurrent performance patterns (see go-concurrency).

fetch
$curl "https://skillshub.wtf/cxuu/golang-skills/go-performance?format=md"
SKILL.mdgo-performance

Go Performance Patterns

Available Scripts

  • scripts/bench-compare.sh — Runs Go benchmarks N times with optional baseline comparison via benchstat. Supports saving results for future comparison. Run bash scripts/bench-compare.sh --help for options.

Performance-specific guidelines apply only to the hot path. Don't prematurely optimize—focus these patterns where they matter most.


Prefer strconv over fmt

When converting primitives to/from strings, strconv is faster than fmt:

s := strconv.Itoa(rand.Int()) // ~2x faster than fmt.Sprint()
ApproachSpeedAllocations
fmt.Sprint143 ns/op2 allocs/op
strconv.Itoa64.2 ns/op1 allocs/op

Read references/STRING-OPTIMIZATION.md when choosing between strconv and fmt for type conversions, or for the full conversion table.


Avoid Repeated String-to-Byte Conversions

Convert a fixed string to []byte once outside the loop:

data := []byte("Hello world")
for i := 0; i < b.N; i++ {
    w.Write(data) // ~7x faster than []byte("...") each iteration
}

Read references/STRING-OPTIMIZATION.md when optimizing repeated byte conversions in hot loops.


Prefer Specifying Container Capacity

Specify container capacity where possible to allocate memory up front. This minimizes subsequent allocations from copying and resizing as elements are added.

Map Capacity Hints

Provide capacity hints when initializing maps with make():

m := make(map[string]os.DirEntry, len(files))

Note: Unlike slices, map capacity hints do not guarantee complete preemptive allocation—they approximate the number of hashmap buckets required.

Slice Capacity

Provide capacity hints when initializing slices with make(), particularly when appending:

data := make([]int, 0, size)

Unlike maps, slice capacity is not a hint—the compiler allocates exactly that much memory. Subsequent append() operations incur zero allocations until capacity is reached.

ApproachTime (100M iterations)
No capacity2.48s
With capacity0.21s

The capacity version is ~12x faster due to zero reallocations during append.


Pass Values

Don't pass pointers as function arguments just to save a few bytes. If a function refers to its argument x only as *x throughout, then the argument shouldn't be a pointer.

func process(s string) { // not *string — strings are small fixed-size headers
    fmt.Println(s)
}

Common pass-by-value types: string, io.Reader, small structs.

Exceptions:

  • Large structs where copying is expensive
  • Small structs that might grow in the future

String Concatenation

Choose the right strategy based on complexity:

MethodBest For
+Few strings, simple concat
fmt.SprintfFormatted output with mixed types
strings.BuilderLoop/piecemeal construction
strings.JoinJoining a slice
Backtick literalConstant multi-line text

Read references/STRING-OPTIMIZATION.md when choosing a string concatenation strategy, using strings.Builder in loops, or deciding between fmt.Sprintf and manual concatenation.


Benchmarking and Profiling

Always measure before and after optimizing. Use Go's built-in benchmark framework and profiling tools.

go test -bench=. -benchmem -count=10 ./...

Read references/BENCHMARKS.md when writing benchmarks, comparing results with benchstat, profiling with pprof, or interpreting benchmark output.

Validation: After applying optimizations, run bash scripts/bench-compare.sh to measure the actual impact. Only keep optimizations with measurable improvement.


Quick Reference

PatternBadGoodImprovement
Int to stringfmt.Sprint(n)strconv.Itoa(n)~2x faster
Repeated []byte[]byte("str") in loopConvert once outside~7x faster
Map initializationmake(map[K]V)make(map[K]V, size)Fewer allocs
Slice initializationmake([]T, 0)make([]T, 0, cap)~12x faster
Small fixed-size args*string, *io.Readerstring, io.ReaderNo indirection
Simple string joins1 + " " + s2(already good)Use + for few strings
Loop string buildRepeated +=strings.BuilderO(n) vs O(n²)

Related Skills

  • Data structures: See go-data-structures when choosing between slices, maps, and arrays, or understanding allocation semantics
  • Declaration patterns: See go-declarations when using make with capacity hints or initializing maps and slices
  • Concurrency: See go-concurrency when parallelizing work across goroutines or using sync.Pool for buffer reuse
  • Style principles: See go-style-core when deciding whether an optimization is worth the readability cost

> related_skills --same-repo

> go-testing

Use when writing, reviewing, or improving Go test code — including table-driven tests, subtests, parallel tests, test helpers, test doubles, and assertions with cmp.Diff. Also use when a user asks to write a test for a Go function, even if they don't mention specific patterns like table-driven tests or subtests. Does not cover benchmark performance testing (see go-performance).

> go-style-core

Use when working with Go formatting, line length, nesting, naked returns, semicolons, or core style principles. Also use when a style question isn't covered by a more specific skill, even if the user doesn't reference a specific style rule. Does not cover domain-specific patterns like error handling, naming, or testing (see specialized skills). Acts as fallback when no more specific style skill applies.

> go-packages

Use when creating Go packages, organizing imports, managing dependencies, or deciding how to structure Go code into packages. Also use when starting a new Go project or splitting a growing codebase into packages, even if the user doesn't explicitly ask about package organization. Does not cover naming individual identifiers (see go-naming).

> go-naming

Use when naming any Go identifier — packages, types, functions, methods, variables, constants, or receivers — to ensure idiomatic, clear names. Also use when a user is creating new types, packages, or exported APIs, even if they don't explicitly ask about naming conventions. Does not cover package organization (see go-packages).

┌ stats

installs/wk0
░░░░░░░░░░
github stars50
██████████
first seenMar 17, 2026
└────────────

┌ repo

cxuu/golang-skills
by cxuu
└────────────

┌ tags

└────────────