objectpath performance
### Summary
nilaway's use of `objectpath.Encoder.For()` in the primitivizer causes significant allocation pressure on large codebases, resulting in high GC overhead.
### Problem
When analyzing packages with many exported types, nilaway calls `objectpath.Encoder.For()` frequently to compute stable identifiers for cross-package inference. Profiling on a large monorepo (~9000 packages) showed:
- `objectpath` functions consumed **504MB (60%)** of allocations on hot packages
- GC overhead was **~40% of CPU time** (`gcDrain` + `scanobject`)
### Current Mitigation
We added a cache in `primitivizer.site()` to avoid redundant `objPathEncoder.For()` calls for the same `types.Object`:
```go
objPath, cached := p.objPathCache[obj]
if !cached {
objPath, err = p.objPathEncoder.For(obj)
// ...
p.objPathCache[obj] = objPath
}
```
This helps but doesn't reduce per-call allocation overhead within objectpath itself.
### Upstream Fix
I've filed an issue with golang.org/x/tools to add `sync.Pool` for path buffers and finder structs:
- **Issue:** https://github.com/golang/go/issues/77059
With the upstream fix applied:
| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| Allocations | 929MB | 445MB | **52% reduction** |
| GC cycles | 21 | 14 | **33% reduction** |
| CPU on GC | ~40% | <5% | **~90% reduction** |
### Suggestion
Once the upstream fix lands in x/tools, nilaway should update its dependency to benefit from the reduced allocations. In the meantime, the `objPathCache` in primitivizer provides partial mitigation.
/cc @yuxincs
3 条评论