Preventive performance analysis for Go - Catch O(n²) loops, N+1 queries, and other performance anti-patterns before they hit production.
Most performance tools are reactive - they tell you what's slow after it's in production. goperf is preventive - it catches performance anti-patterns during development, before they become problems.
| Tool | Focus | Timing |
|---|---|---|
pprof |
Runtime profiling | After deployment |
golangci-lint |
Code correctness | Before commit |
goperf |
Performance patterns | Before commit |
go install github.com/cschuman/goperf@latestOr build from source:
git clone https://github.com/cschuman/goperf.git
cd goperf
go build -o goperf .# Audit entire project
goperf ./...
# Check only critical patterns (O(n²), N+1 queries)
goperf --rules=algorithm,database ./...
# CI mode - fail on high severity issues
goperf --fail-on=high --format=json ./...
# Show fix suggestions (does not modify files)
goperf --suggest ./...- Nested range loops - Quadratic complexity that explodes with data size
- Linear search in loops - Should use maps for O(1) lookup
- Unpreallocated slices - Repeated allocations from slice growth
- String concatenation in loops - Creates O(n²) allocations
- Maps without size hints - Causes rehashing as map grows
- SQL in loops - Each iteration hits the database separately
- Unbatched inserts - Should use bulk operations
- Unbuffered channels - Can cause unexpected blocking
- Mutex in loops - Lock contention from repeated acquire/release
- Goroutine leaks - Goroutines without termination mechanism
- JSON marshal in loops - Reflection overhead multiplied
- http.Client creation - Should reuse clients for connection pooling
- ReadAll usage - Loads entire content into memory
- regexp.Compile in functions - Should compile once at package level
- template.Parse in functions - Should parse once at startup
╭─────────────────────────────────────────────────────────────╮
│ PERF-AUDIT: 4 issues found (1 critical, 2 high, 1 medium) │
╰─────────────────────────────────────────────────────────────╯
CRITICAL │ Database Exec() called inside loop - N+1 query pattern
│ internal/db/repository.go:156:13
│
│ 153│ for _, item := range items {
│ 154│ // Process each item
│ → 155│ _, err := db.Exec(query, item.ID, item.Value)
│ 156│ if err != nil {
│ 157│ return err
│
│ WHY: Each iteration makes a separate database round-trip. With 100
│ items, that's 100 queries instead of 1. Network latency
│ dominates, making this extremely slow.
│ FIX: Use batch operations: SELECT ... WHERE id IN (...), bulk INSERT,
│ or collect IDs and query once outside the loop
name: Performance Audit
on: [push, pull_request]
jobs:
goperf:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
- run: go install github.com/cschuman/goperf@latest
- run: goperf --fail-on=high ./...#!/bin/sh
goperf --fail-on=critical ./...goperf will load .goperf.yml from the current directory. CLI flags override config values.
Example:
rules:
- algorithm
- database
ignore_paths:
- vendor
- testdata
fail_on: high
format: console
context: 3
verbose: falseSee .goperf.yml.example for a fully documented template.
| Flag | Default | Description |
|---|---|---|
--rules |
all |
Rules to run: algorithm,allocation,database,concurrency,io,cache,context,memory,benchmark |
--format |
console |
Output format: console, json, diff |
--fail-on |
- | Exit code 1 if issues at this level: low,medium,high,critical |
--context |
3 |
Lines of code context to show |
--ignore |
- | Comma-separated paths to ignore |
--verbose |
false |
Show verbose output |
--suggest |
false |
Show fix suggestions (does not modify files) |
| Level | Meaning | Action |
|---|---|---|
| CRITICAL | Will cause production issues | Fix immediately |
| HIGH | Significant performance impact | Fix before release |
| MEDIUM | Moderate impact | Should fix |
| LOW | Minor optimization | Nice to have |
Sometimes you need to suppress a warning - perhaps it's a false positive, or you've verified the code is intentional. Use // perf:ignore comments:
// perf:ignore
for _, item := range items {
db.Exec(query, item) // This line is ignored
}Or on the same line:
db.Exec(query, item) // perf:ignore// perf:ignore sql-in-loop
for _, item := range items {
db.Exec(query, item) // Only sql-in-loop is ignored
result = append(result, item) // Still flagged for append-in-loop
}// perf:ignore-start
for _, item := range items {
db.Exec(query, item)
}
for _, other := range others {
db.Query(q, other)
}
// perf:ignore-endWe run goperf on its own codebase. Here's what happened:
$ goperf ./...
╭───────────────────────────────────────────────────────────────────╮
│ PERF-AUDIT: 147 issues found (36 medium, 111 low) │
╰───────────────────────────────────────────────────────────────────╯
We manually addressed 34 actionable issues based on suggestions:
| Issue Type | Count | Fix Applied |
|---|---|---|
| Unpreallocated slices | 31 | make([]T, 0, N) with capacity hints |
| String concat in loops | 2 | strings.Builder |
| Map without size hint | 1 | make(map[K]V, size) |
After applying those changes:
$ goperf ./...
╭───────────────────────────────────────────────────────────────────╮
│ PERF-AUDIT: 113 issues found (3 medium, 110 low) │
╰───────────────────────────────────────────────────────────────────╯
The remaining 113 issues are:
- 3 medium: Nested loops for AST traversal (intentional, not O(n²) on data)
- 110 low: "Consider adding benchmarks" suggestions
This demonstrates that goperf finds real issues - including in itself - and that acting on suggestions is straightforward.
Contributions welcome! See CONTRIBUTING.md for guidelines.
Areas we'd love help with:
- More detection rules
- False positive reduction
- IDE integrations (VS Code, GoLand)
- Benchmark integration
- Fix suggestions
Please read our Code of Conduct before contributing.
Found a security issue? Please report it responsibly. See SECURITY.md for details.
MIT License - see LICENSE for details.
Inspired by the philosophy that preventing performance problems is better than fixing them.
Built with Go's excellent go/ast package for static analysis.
Note: automatic code fixing is not yet implemented. goperf only produces suggestions for manual changes.