-
Notifications
You must be signed in to change notification settings - Fork 46
Description
Description
The codebase has widespread use of make([]T, 0) without capacity hints, even when the final size is known. This causes multiple reallocations, memory copying, and increased GC pressure.
Problem
Metrics:
- Hundreds of instances of
make([]T, 0)without capacity argument - Zero uses of
slices.Grow()for pre-allocation (Go 1.20+ feature) - Most common in loop-heavy code paths processing known-size collections
Impact:
- Multiple reallocations as slices grow (typical growth: 0→1→2→4→8→16...)
- Memory copying on each reallocation
- Increased GC pressure from intermediate allocations
- Most impactful in loops processing known-size collections
Example Pattern
Current (inefficient):
results := make([]Result, 0) // ❌ No capacity hint
for _, item := range items { // len(items) is known!
results = append(results, process(item))
}Should be:
results := make([]Result, 0, len(items)) // ✅ Pre-allocated
for _, item := range items {
results = append(results, process(item))
}Focus Areas
High-allocation hot paths (prioritize these):
pkg/workflow/- compiler and processing pipelinespkg/cli/trial_command.go(1,002 lines)pkg/cli/mcp_inspect.go(1,011 lines)pkg/workflow/safe_outputs_config_generation.go(978 lines)
Implementation Patterns
Pattern 1 - Known Size
// Before
var builder []string
for _, x := range data {
builder = append(builder, x.Name)
}
// After
builder := make([]string, 0, len(data))
for _, x := range data {
builder = append(builder, x.Name)
}Pattern 2 - Estimated Size
// Before
var builder []string
for _, x := range data {
for _, y := range x.Children {
builder = append(builder, y.Name)
}
}
// After
estimatedSize := len(data) * 5 // Based on avg children
builder := make([]string, 0, estimatedSize)
for _, x := range data {
for _, y := range x.Children {
builder = append(builder, y.Name)
}
}Pattern 3 - Using slices.Grow (Go 1.20+)
import "slices"
var results []Result
for _, batch := range batches {
// Grow slice efficiently before processing batch
results = slices.Grow(results, len(batch))
for _, item := range batch {
results = append(results, process(item))
}
}Success Criteria
- Identify high-allocation files:
go test -bench=. -benchmem ./... | grep alloc - Apply pre-allocation to top 10 hot spots
- Benchmark before/after (measure allocs/op reduction)
- Full test suite passes:
go test ./... - Use pprof to validate:
go test -memprofile=mem.out - Document pattern in code review guidelines
-
make agent-finishpasses
Priority
Medium - Performance improvement with low implementation risk
Estimated Effort: Large (10-15 hours across many files, but low risk per change)
Source
Extracted from Sergo Performance Optimization Analysis - Discussion #11840
Analysis Quote:
"Missing slice pre-allocation - Widespread
make([]T, 0)without capacity hint, even when final size is known"
Performance Impact:
"Reduce memory allocations and GC pressure"
References:
AI generated by Discussion Task Miner - Code Quality Improvement Agent
- expires on Feb 16, 2026, 1:28 PM UTC