-
Notifications
You must be signed in to change notification settings - Fork 18.9k
Description
Summary
The objectpath.Encoder.For() function allocates heavily, causing significant GC pressure in tools that call it frequently (e.g., static analyzers like nilaway that compute object paths for cross-package inference).
Problem
Profiling nogo with ~40 analyzers on a large monorepo (~9000 packages) revealed that objectpath functions consumed 504MB (60%) of allocations on hot packages:
271MB (32%) objectpath.(*finder).find
101MB (12%) strconv.AppendInt
68MB (8%) objectpath.(*Encoder).For
65MB (8%) objectpath.appendOpArg
The main allocation sources are:
make([]byte, 0, 48)inEncoder.For()- allocated per callstrconv.AppendIntinappendOpArg- allocates for each method/field index&finder{}infind()- new struct per search with lazily-allocated maps
Proposed Solution
- Pool path buffers using
sync.Poolinstead of allocating per call - Pre-compute small integers (0-99) to avoid
strconv.AppendIntfor common indices - Pool finder structs with pre-allocated maps, using
clear()to reset between uses
Results
On a package with heavy objectpath usage:
| Metric | Before | After | Improvement |
|---|---|---|---|
| Allocations | 929MB | 445MB | 52% reduction |
| GC cycles | 21 | 14 | 33% reduction |
| GC pause | 1.77ms | 0.81ms | 54% reduction |
After the change, objectpath functions no longer appear in the top allocators.
Patch
I have a working patch ready to submit as a PR. The changes are backward-compatible and do not change any public APIs.