Skip to content

Cache localized bundle resolution to cut main-thread disk lookups (#1347) - #1355

Merged
steipete merged 2 commits into
steipete:mainfrom
Yuxin-Qiao:perf/cache-localized-bundle
Jun 8, 2026
Merged

Cache localized bundle resolution to cut main-thread disk lookups (#1347)#1355
steipete merged 2 commits into
steipete:mainfrom
Yuxin-Qiao:perf/cache-localized-bundle

Conversation

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor

Summary

Fixes the localization hot path behind the long-uptime idle main-thread spin in #1347 (the part not covered by the isRunningTestsProcess() cache already on main, as confirmed by @YossiGS's sample landing on rebuildClosedMenuIfNeeded → populateMenu → menu body getters → localizedBundle()).

localizedBundle(), codexBarLocalizationResourceBundle() and lprojBundle() did Bundle(url:) / Bundle(path:) filesystem lookups on every call. A single L("…") resolves the resource bundle twice, reads UserDefaults, and does an .lproj disk lookup — all uncached. Menu row bodies (MetricRow, ProviderCostContent, UsageMenuCardView.Model) call L() / codexBarLocalizationSignature(), and they re-evaluate on every closed-menu rebuild tick on the main thread, so the per-call cost shows up as sustained idle CPU.

This PR caches:

  • the resource bundle (constant for the process lifetime; only the default .main resolution is cached, custom-arg calls used by tests stay direct), and
  • the resolved localizedBundle() keyed on the current language, so a settings/locale language switch transparently re-resolves.

A single NSLock with the compute happening outside the lock keeps the disk work off the critical section and avoids re-entrant deadlock (the localized-bundle compute calls back into the resource-bundle accessor).

Proof

Micro-benchmark of localizedBundle() resolution, 20k iterations (temporary test, removed before commit):

uncached (per-call disk resolve): 3.933 us/call
cached   (lock + key compare):    0.973 us/call
saved per L()/signature call:     2.961 us (75.3% less)

This is a lower bound: in the test harness Bundle.main is not a .app, so the resource-bundle Bundle.main.url(forResource:"CodexBar_CodexBar") + Bundle(url:) path (run twice per L() pre-fix) isn't even exercised. In the shipped app that disk work is also eliminated, so the real-app savings are larger. Each menu rebuild calls L() many times across providers/rows, multiplying the saved time per closed-menu tick.

Test plan

  • swift build
  • New LocalizationBundleCacheTests (4 cases): correct .lproj per language, re-resolve on switch + switch-back, unknown-language fallback to en.lproj, survives explicit cache reset. Assertions are concurrency-safe (they verify the resolved bundle is always correct for the requested language rather than relying on global cache identity).
  • make check (SwiftFormat + SwiftLint, 0 violations)
  • swift test — only pre-existing, environment-dependent failures remain (PreferencesPaneSmokeTests/MiniMaxMenuCardModelTests), which also fail on clean origin/main on a machine whose UserDefaults.standard appLanguage is set (verified by reverting this patch); they pass where appLanguage is unset (CI).

Refs #1347

…eipete#1347)

localizedBundle()/codexBarLocalizationResourceBundle()/lprojBundle() did
Bundle(url:)/Bundle(path:) filesystem lookups on every call. Menu row bodies
re-evaluate them on every closed-menu rebuild tick on the main thread, so idle
CPU climbs. Cache the (constant) resource bundle and the resolved localized
bundle keyed on the current language; a language switch transparently
re-resolves. Single lock with compute-outside-lock keeps disk work off the
critical section and avoids re-entrant deadlock.

Co-authored-by: Cursor <cursoragent@cursor.com>
@clawsweeper

clawsweeper Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed June 7, 2026, 11:53 PM ET / 03:53 UTC.

Summary
This PR adds process-wide caching for the default localization resource bundle and language-keyed localized bundle resolution, plus focused Swift Testing coverage for cache correctness.

Reproducibility: yes. at source level: current main still resolves the resource bundle and .lproj bundle during each L() path, and the linked issue discussion includes a sample pointing at localizedBundle() under closed-menu rebuilds. I did not run a live multi-day app reproduction in this read-only review.

Review metrics: 2 noteworthy metrics.

  • Diff scope: 2 files changed; 153 added, 1 deleted. The patch is narrowly scoped to the localization helper and focused regression tests.
  • Cache behavior coverage: 4 tests added. The tests cover the main correctness risks introduced by caching: language changes, fallback, repeated hits, and reset behavior.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Risk before merge

  • [P1] The proof is function-level benchmark output for localizedBundle() rather than a packaged-app idle CPU sample, so maintainers still need to decide whether that is enough end-to-end performance evidence before merge.

Maintainer options:

  1. Decide the mitigation before merge
    Land the focused localization-cache fix after CI and maintainer review, then use Main thread spins after a few days of uptime — Settings won't open, ~9% CPU at idle #1347 for any remaining closed-menu rebuild or formatter hot paths outside this PR.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P2] No repair lane is needed because review found no narrow blocking defect; maintainers should finish ordinary CI and performance-review judgment.

Security
Cleared: The diff only changes Swift localization caching and adds tests; it does not touch dependencies, workflows, secrets, signing, release scripts, or other supply-chain-sensitive paths.

Review details

Best possible solution:

Land the focused localization-cache fix after CI and maintainer review, then use #1347 for any remaining closed-menu rebuild or formatter hot paths outside this PR.

Do we have a high-confidence way to reproduce the issue?

Yes, at source level: current main still resolves the resource bundle and .lproj bundle during each L() path, and the linked issue discussion includes a sample pointing at localizedBundle() under closed-menu rebuilds. I did not run a live multi-day app reproduction in this read-only review.

Is this the best way to solve the issue?

Yes, the proposed cache is a narrow maintainable fix for repeated localization filesystem lookups and keeps explicit language switching covered by tests. Packaged-app CPU proof would improve end-to-end confidence but does not point to a different implementation.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against db184430bc4b.

Label changes

Label justifications:

  • P2: This is a normal-priority performance fix for a credible idle CPU hot path with limited localization/menu blast radius.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes copied 20k-iteration benchmark output for the changed localizedBundle() path showing a before/after improvement, which is sufficient function-level runtime proof for this performance patch.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes copied 20k-iteration benchmark output for the changed localizedBundle() path showing a before/after improvement, which is sufficient function-level runtime proof for this performance patch.
Evidence reviewed

What I checked:

Likely related people:

  • steipete: git blame shows Peter Steinberger authored the current Localization.swift baseline that this PR optimizes, including resource-bundle and localized-bundle lookup behavior. (role: baseline localization owner; confidence: medium; commits: 723734ef3422; files: Sources/CodexBar/Localization.swift)
  • hhh2210: Recent merged work changed Localization.swift for the related test-process cache and touched adjacent menu-card rebuild hot paths tied to the same idle CPU symptom. (role: recent performance contributor; confidence: high; commits: c7f8336e1314, ef17fbd45d91; files: Sources/CodexBar/Localization.swift, Sources/CodexBar/StatusItemController+MenuTracking.swift, Sources/CodexBar/MenuCardHeightFingerprint.swift)
  • Nicolas Hidemaru Ogoshi / 大越ニコラス秀丸: Recent merged work changed closed-menu rebuild gating in the path that can repeatedly exercise localization lookups during idle menu refreshes. (role: adjacent menu rebuild contributor; confidence: medium; commits: 4652e40682a8; files: Sources/CodexBar/StatusItemController+MenuTracking.swift, Tests/CodexBarTests/StatusMenuClosedPreparationTests.swift, Tests/CodexBarTests/StatusMenuOpenRefreshTests.swift)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal priority bug or improvement with limited blast radius. labels Jun 8, 2026
CI failed because the language-switch assertion used "ja", which has no
ja.lproj in the repo and correctly falls back to en.lproj. Use "es"
instead, which matches an actual catalog.

Co-authored-by: Cursor <cursoragent@cursor.com>
@steipete
steipete merged commit 1818638 into steipete:main Jun 8, 2026
4 checks passed
@steipete steipete mentioned this pull request Jun 11, 2026
@Yuxin-Qiao
Yuxin-Qiao deleted the perf/cache-localized-bundle branch June 25, 2026 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants