A Go CLI tool that generates synthetic PGO (Profile-Guided Optimization) profiles from source code directives. Annotate call sites with //pgogen:hot comments to force the Go compiler's PGO inliner to treat them as hot, increasing the inlining budget from 80 to 2000 AST nodes.
Important
NOTE: While this tool can help with speeding up, some notes:
- The tool generates synthetic profiles, This can't really replace real PGO profiles, and is potentially alot more work.
- however with fast moving codebases, this can help with keeping up being fast(er)
- It can collide with real PGO profiles, so be careful when using both
- The tool is not perfect, and can miss some call sites, or generate invalid profiles
Add the tool to your project:
go get -tool github.com/daanv2/go-force-inline@latestPlace //pgogen:hot on the line immediately above a function call:
func handler(r *Request) *Response {
//pgogen:hot weight=10000
result := processRequest(r)
//pgogen:hot
validated := validateInput(r.Body)
return buildResponse(result, validated)
}The weight parameter is optional (default: 10000).
go tool github.com/daanv2/go-force-inline generate ./...| Flag | Description |
|---|---|
-o, --output |
Output file path (default: default.pgo) |
The Go toolchain auto-detects default.pgo in the module root:
go build ./...Or specify it explicitly:
go build -pgo=default.pgo ./...Inspect which edges in a profile are considered hot:
go tool github.com/daanv2/go-force-inline verify default.pgo| Flag | Description |
|---|---|
--threshold |
CDF hot threshold percentage (default: 99.0) |
Output:
Edge Weight CDF% Hot?
---- ------ ---- ----
main.handler → main.processRequest 10000 50.0% yes
main.handler → main.validateInput 10000 100.0% yes
Total weight: 20000
Hot threshold: 99% (cumulative weight < 19800)
| Flag | Description |
|---|---|
--log-level |
Log level: debug, info, warn, error, fatal (default: info) |
--log-format |
Log format: text, json, logfmt (default: text) |
--log-file |
Write logs to a file |
--log-report-caller |
Include source file in log output |
The tool warns about situations where forced inlining won't work:
- Directive not followed by a call expression
- Callee has
//go:noinline(PGO won't override it) - Interface method calls (cannot be inlined)
- Calls inside closures (fragile naming)
- Chained calls on one line (ambiguous target)