Skip to content

perf(cache): pack CachedPathImpl::meta into a CachedMeta byte#1144

Merged
graphite-app[bot] merged 1 commit into
mainfrom
perf/cached-path-meta-atomic
May 23, 2026
Merged

perf(cache): pack CachedPathImpl::meta into a CachedMeta byte#1144
graphite-app[bot] merged 1 commit into
mainfrom
perf/cached-path-meta-atomic

Conversation

@Boshen

@Boshen Boshen commented May 23, 2026

Copy link
Copy Markdown
Member

Summary

CachedPathImpl::meta was a OnceLock<Option<(bool, bool)>> (16 bytes) recording whether the path is a file and/or directory. Filesystem metadata is idempotent — OnceLock's exactly-once initialization is a nicety, not a correctness requirement: if two threads race they both re-stat and both store the same answer.

This PR introduces a CachedMeta struct in src/cache/cached_meta.rs that wraps an AtomicU8, encapsulates the encoding of the six possible states (uninitialized / None / the four (is_file, is_dir) combinations), and exposes a single get_or_init accessor.

CachedPathImpl::meta becomes:

pub meta: CachedMeta,

…and the accessor on CachedPath collapses to a one-liner:

fn metadata<Fs: FileSystem>(&self, fs: &Fs) -> Option<(bool, bool)> {
    self.meta.get_or_init(|| fs.metadata(&self.path).ok().map(|r| (r.is_file, r.is_dir)))
}

All encoding/decoding/AtomicU8 plumbing is private to the new module.

Size impact

The field shrinks from 16 bytes to 1, saving 15 bytes per cached path entry. For sessions with 10k cached paths that's ~150 KB; with 100k it's ~1.5 MB.

🤖 Generated with Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21311dc7dc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cache/cached_path.rs Outdated
@codecov

codecov Bot commented May 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.11%. Comparing base (98617e3) to head (72a163c).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/cache/cached_meta.rs 92.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1144      +/-   ##
==========================================
- Coverage   93.12%   93.11%   -0.02%     
==========================================
  Files          21       22       +1     
  Lines        4089     4139      +50     
==========================================
+ Hits         3808     3854      +46     
- Misses        281      285       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq

codspeed-hq Bot commented May 23, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 3.69%

❌ 3 regressed benchmarks
✅ 10 untouched benchmarks
⏩ 5 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
resolver_memory[resolve from symlinks] 35.7 ms 36.9 ms -3.42%
resolver_real[resolve from symlinks] 35.5 ms 36.8 ms -3.54%
resolver_memory[multi-thread] 376.8 µs 392.9 µs -4.1%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing perf/cached-path-meta-atomic (72a163c) with main (98617e3)2

Open in CodSpeed

Footnotes

  1. 5 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (72a163c) during the generation of this report, so 98617e3 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@Boshen Boshen force-pushed the perf/cached-path-meta-atomic branch from 21311dc to 5e7e087 Compare May 23, 2026 06:05
@Boshen Boshen changed the title perf(cache): pack CachedPathImpl::meta into an AtomicU8 perf(cache): pack CachedPathImpl::meta into a CachedMeta byte May 23, 2026
@Boshen Boshen force-pushed the perf/cached-path-meta-atomic branch from 5e7e087 to c831047 Compare May 23, 2026 10:37
@Boshen Boshen added the merge label May 23, 2026

Boshen commented May 23, 2026

Copy link
Copy Markdown
Member Author

Merge activity

## Summary

`CachedPathImpl::meta` was a `OnceLock<Option<(bool, bool)>>` (16 bytes) recording whether the path is a file and/or directory. Filesystem metadata is idempotent — `OnceLock`'s exactly-once initialization is a nicety, not a correctness requirement: if two threads race they both re-`stat` and both store the same answer.

This PR introduces a `CachedMeta` struct in `src/cache/cached_meta.rs` that wraps an `AtomicU8`, encapsulates the encoding of the six possible states (uninitialized / `None` / the four `(is_file, is_dir)` combinations), and exposes a single `get_or_init` accessor.

`CachedPathImpl::meta` becomes:

```rust
pub meta: CachedMeta,
```

…and the accessor on `CachedPath` collapses to a one-liner:

```rust
fn metadata<Fs: FileSystem>(&self, fs: &Fs) -> Option<(bool, bool)> {
    self.meta.get_or_init(|| fs.metadata(&self.path).ok().map(|r| (r.is_file, r.is_dir)))
}
```

All encoding/decoding/`AtomicU8` plumbing is private to the new module.

## Size impact

The field shrinks from 16 bytes to 1, saving **15 bytes per cached path entry**. For sessions with 10k cached paths that's ~150 KB; with 100k it's ~1.5 MB.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@graphite-app graphite-app Bot force-pushed the perf/cached-path-meta-atomic branch from c831047 to 72a163c Compare May 23, 2026 10:45
@graphite-app graphite-app Bot merged commit 72a163c into main May 23, 2026
18 checks passed
@graphite-app graphite-app Bot removed the merge label May 23, 2026
@graphite-app graphite-app Bot deleted the perf/cached-path-meta-atomic branch May 23, 2026 10:48
@oxc-guard oxc-guard Bot mentioned this pull request May 23, 2026
Boshen added a commit that referenced this pull request May 25, 2026
## 🤖 New release

* `oxc_resolver`: 11.19.1 -> 11.19.2
* `oxc_resolver_napi`: 11.19.1 -> 11.19.2

<details><summary><i><b>Changelog</b></i></summary><p>

## `oxc_resolver`

<blockquote>

##
[11.19.2](v11.19.1...v11.19.2)
- 2026-05-25

### <!-- 1 -->🐛 Bug Fixes

- *(tsconfig)* apply later-wins semantics for extends array
([#1156](#1156)) (by
@Boshen)
- *(tsconfig)* walk past a tsconfig that doesn't claim the file
([#1154](#1154)) (by
@Boshen)
- *(tsconfig)* let project references take priority over their parent
([#1151](#1151)) (by
@Boshen)
- *(tsconfig)* resolve `rootDirs` against the config that declared them
([#1150](#1150)) (by
@Boshen)
- *(tsconfig)* resolve `baseUrl` / `paths` against the canonical
tsconfig path
([#1148](#1148)) (by
@Boshen)
- strip query fragments when calling `find_tsconfig`
([#1147](#1147)) (by
@Boshen)
- avoid panic in resolve_file for parentless paths
([#1053](#1053)) (by
@Boshen)
- *(dts)* strip ./ prefix from package entry when matching typesVersions
([#1051](#1051)) (by
@Boshen)
- *(dts)* expand Declaration to TypeScript|Declaration for package entry
resolution
([#1050](#1050)) (by
@Boshen)
- *(dts)* prefer declaration extensions over JS in exports-resolved
paths ([#1047](#1047))
(by @Boshen)
- avoid wasm/wasi dead-code lint in NodePath
([#1043](#1043)) (by
@Boshen)
- *(napi)* replace panics with error returns to prevent WASM traps
([#1055](#1055)) (by
@Boshen)

### <!-- 2 -->🚜 Refactor

- remove clear_cache test that dynamically creates fixtures (by @Boshen)
- move resolve and misc fixtures into fixtures/integration (by @Boshen)
- replace ignored doctest with link to example (by @Boshen)
- consolidate fixture directories for better test file mapping (by
@Boshen)
- replace `url` crate with `percent-encoding`
([#1065](#1065)) (by
@Boshen)

### <!-- 4 -->⚡ Performance

- *(cache)* pack CachedPathImpl::meta into a CachedMeta byte
([#1144](#1144)) (by
@Boshen)
- *(cache)* store canonical path as Box<Path> instead of PathBuf
([#1143](#1143)) (by
@Boshen)
- *(alias)* fast-reject alias entries by cached first byte
([#1142](#1142)) (by
@Boshen)

### <!-- 6 -->🧪 Testing

- *(tsconfig)* port lookup scenarios from typescript-go
([#1155](#1155)) (by
@Boshen)
- add 28 tests to improve coverage (92% → 93%)
([#1082](#1082)) (by
@Boshen)

### Contributors

* @Boshen
* @renovate[bot]
</blockquote>



</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).

Co-authored-by: oxc-guard[bot] <276638029+oxc-guard[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant