perf(inode): optimize lookupCount struct layout to 8-byte scalar - #5000
perf(inode): optimize lookupCount struct layout to 8-byte scalar#5000kislaykishore wants to merge 1 commit into
Conversation
## Summary
This commit refactors `lookupCount` from a 24-byte composite struct (`{id, count, destroyed}`) into an 8-byte scalar `int64` (using `-1` as the sentinel for destroyed state). Inode IDs are passed dynamically on `Inc(id)` and `Dec(id, n)` for error formatting instead of being retained in the struct.
## Struct Sizing & Go Heap Class Reduction
Reducing `lookupCount` from 24 B to 8 B shrinks all inode types. Crucially, `FileInode` shrinks from 304 B to 288 B, crossing a Go runtime heap allocator size class boundary (320 B -> 288 B) and saving **32 bytes (-10.0%) of real heap memory per file inode**.
| Struct / Type | Baseline Size | Optimized Size | Struct Delta | Go Heap Class | Heap Allocation Delta | RAM Saved (per 1M files) | RAM Saved (per 10M files) |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| `lookupCount` | 24 B | **8 B** | -16 B (-66.7%) | Stack/Internal | — | — | — |
| `FileInode` | 304 B | **288 B** | -16 B (-5.3%) | 320 B -> **288 B** | **-32 B (-10.0%)** | **32.0 MB** | **320.0 MB** |
| `dirInode` | 240 B | **224 B** | -16 B (-6.7%) | 240 B -> **224 B** | **-16 B (-6.7%)** | **16.0 MB** | **160.0 MB** |
| `SymlinkInode` | 152 B | **136 B** | -16 B (-10.5%) | 160 B -> **144 B** | **-16 B (-10.0%)** | **16.0 MB** | **160.0 MB** |
| `baseDirInode` | 152 B | **136 B** | -16 B (-10.5%) | 160 B -> **144 B** | **-16 B (-10.0%)** | **16.0 MB** | **160.0 MB** |
## Benchmark Performance
Measured via `benchstat` (n=10, p < 0.05):
| Benchmark Target | Master Baseline | Optimized Branch | CPU Latency Delta | Baseline Memory | Branch Memory | Allocation Delta |
|---|:---:|:---:|:---:|:---:|:---:|:---:|
| `LookupCount_Inc` | 2.161 ns/op | **1.881 ns/op** | **-12.93% (p=0.000)** | 0 B/op, 0 alloc | 0 B/op, 0 alloc | 0.0% |
| `LookupCount_Dec` | 2.340 ns/op | **2.285 ns/op** | **-2.35% (p=0.043)** | 0 B/op, 0 alloc | 0 B/op, 0 alloc | 0.0% |
| `Alloc_FileInode` | 130.9 ns/op | 129.3 ns/op | ~0.0% (parity) | 320 B/op, 1 alloc | **288 B/op, 1 alloc** | **-10.0% B/op** |
## Verification
- Unit tests: `go test -v -race ./internal/fs/inode/...`
- Benchmarks: `go test -bench=BenchmarkLookupCount -benchmem ./internal/fs/inode/...`
There was a problem hiding this comment.
Code Review
This pull request refactors the lookupCount helper in internal/fs/inode/lookup_count.go from a struct to a custom int64 type, representing a destroyed state with a value of -1. This change simplifies the type and removes the need for an explicit initialization step, instead requiring the inode ID to be passed directly to the Inc and Dec methods. Corresponding updates were made across various inode implementations (base_dir.go, dir.go, file.go, and symlink.go) to pass the ID during increment/decrement operations and to explicitly call Destroy(). Additionally, a new test suite lookup_count_test.go was introduced to verify the behavior of the refactored lookupCount type. There are no review comments to address, and we have no further feedback to provide.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5000 +/- ##
==========================================
+ Coverage 83.81% 83.86% +0.04%
==========================================
Files 174 174
Lines 21376 21376
==========================================
+ Hits 17916 17926 +10
+ Misses 2779 2773 -6
+ Partials 681 677 -4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Description
This PR optimizes the
lookupCounthelper ininternal/fs/inodeby converting it from a 24-byte composite struct ({id fuseops.InodeID, count uint64, destroyed bool}) into an 8-byte scalarint64(where-1denotes the destroyed state). Inode IDs are passed dynamically toInc(id)andDec(id, n)for panic formatting instead of being retained inside each inode instance.Memory Savings & Go Heap Allocator Class Boundary
FileInodeshrinks from 304 B to 288 B, crossing a Go runtime heap allocator size class boundary (320 B -> 288 B).dirInode,SymlinkInode, andbaseDirInodeby 16 bytes each.lookupCountFileInodedirInodeSymlinkInodebaseDirInodeBenchmark Performance
Measured via
benchstat(n=10, p < 0.05):LookupCount_IncLookupCount_DecAlloc_FileInodeLink to the issue in case of a bug fix.
N/A
Testing details
make buildand verified formatting/linter with 0 issues.go test -v -race ./internal/fs/inode/....Any backward incompatible change? If so, please explain.
N/A