Skip to content

Conversation

@lilnasy
Copy link
Contributor

@lilnasy lilnasy commented Oct 15, 2025

Part of #14564.

Deserialize program.comments lazily upon first access, instead of eagerly, on assumption that most JS plugins won't access comments.

deserializeWith cleans up the buffer once deserializing AST is complete, so we keep a local copy of the buffer in scope of the comments getter, and restore it when the getter is triggered.

Buffers are reused, so the if (localAstId !== astId) check is required to make sure the AST in the buffer is same AST i.e. that the getter isn't accessed after the file has completed linting.

@graphite-app
Copy link
Contributor

graphite-app bot commented Oct 15, 2025

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • 0-merge - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

@github-actions github-actions bot added A-ast Area - AST C-performance Category - Solution not expected to change functional behavior, only performance A-linter Area - Linter A-cli Area - CLI labels Oct 15, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented Oct 15, 2025

CodSpeed Performance Report

Merging #14637 will not alter performance

Comparing lilnasy:perf/lazy-deserialize-comments (8469d57) with main (5858641)

Summary

✅ 37 untouched

@lilnasy lilnasy force-pushed the perf/lazy-deserialize-comments branch 2 times, most recently from 42d51a6 to 09bc1a8 Compare October 16, 2025 09:39
@github-actions github-actions bot added the A-ast-tools Area - AST tools label Oct 16, 2025
@lilnasy lilnasy force-pushed the perf/lazy-deserialize-comments branch from 1831c50 to cd62cfb Compare October 16, 2025 13:37
@lilnasy lilnasy force-pushed the perf/lazy-deserialize-comments branch 2 times, most recently from 6ba5770 to f20c6f9 Compare October 17, 2025 06:16
@lilnasy
Copy link
Contributor Author

lilnasy commented Oct 17, 2025

Not sure what's up with AST check CI. The generated code is up to date. CI's expected output is just different.

@lilnasy lilnasy force-pushed the perf/lazy-deserialize-comments branch from a2dcd3c to 7a7949d Compare October 17, 2025 06:22
@overlookmotel
Copy link
Member

overlookmotel commented Oct 17, 2025

Not sure what's up with AST check CI. The generated code is up to date. CI's expected output is just different.

Looks like dprint (which we use for formatting JS/TS code) requires 2 passes to get to a stable state with this code. That's a bug in dprint, but #14723 provides a workaround. I'll merge it if we decide to go forwards with this PR.

The problem on this PR was that the autofix CI job ran dprint and pushed a commit with the changes. But then the "AST Changes" job checks the code committed is identical to the output of cargo run -p oxc_ast_tools, which it's not because of that extra commit. 🔄


Or just sidestep the formatting problem, by shortening the error message:

if (localAstId !== astId) throw new Error('Comments are only accessible while linting the file');

This is an edge case which shouldn't be hit in practice, so I don't think we need to provide too much explanation.

@overlookmotel overlookmotel changed the title perf(linter/jsplugins): lazy deserialize comments array perf(linter/plugins): lazy deserialize comments array Oct 17, 2025
@lilnasy
Copy link
Contributor Author

lilnasy commented Oct 17, 2025

I tried that as well, dprint introduces a block in that case.

@overlookmotel
Copy link
Member

Weird. I'm not seeing that locally. Anyway, it doesn't matter if it produces a block, only that it does so consistently.

@lilnasy lilnasy force-pushed the perf/lazy-deserialize-comments branch from 919bb6a to ed4acbf Compare October 17, 2025 11:39
Copy link
Member

@overlookmotel overlookmotel left a comment

Choose a reason for hiding this comment

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

All nits...

@lilnasy lilnasy force-pushed the perf/lazy-deserialize-comments branch from 757c717 to 18d50de Compare October 17, 2025 11:55
@lilnasy
Copy link
Contributor Author

lilnasy commented Oct 17, 2025

Addressed the review comments in 09baebc.

autofix-ci bot and others added 3 commits October 17, 2025 11:56
@overlookmotel overlookmotel marked this pull request as ready for review October 17, 2025 12:17
@overlookmotel overlookmotel requested review from Copilot and removed request for camc314 October 17, 2025 12:17
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Lazy-loads the Program.comments array to reduce upfront deserialization cost, guarding access with an AST session id and restoring buffers on demand.

  • Introduces a global astId to gate comment access to the active lint session.
  • Saves buffer references (uint32, uint8, sourceText) and adds a lazy getter for comments that deserializes on first access.

Reviewed Changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.

File Description
tasks/ast_tools/src/generators/raw_transfer.rs Adds astId initialization in the generated runtime to track the active AST session.
crates/oxc_ast/src/serialize/mod.rs Implements lazy comments getter: captures buffer refs, increments astId per Program, restores buffers and deserializes comments on first access.

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@overlookmotel overlookmotel merged commit 58ba6d6 into oxc-project:main Oct 17, 2025
28 checks passed
@lilnasy lilnasy deleted the perf/lazy-deserialize-comments branch October 17, 2025 12:43
@overlookmotel
Copy link
Member

Thank you. I did warn you it might be tricky, but I underestimated!

@lilnasy
Copy link
Contributor Author

lilnasy commented Oct 17, 2025

Thanks for guiding me through this. I did not imagine a project of oxc's scope would neat and tidy, and I enjoyed this welcome tour!

graphite-app bot pushed a commit that referenced this pull request Oct 17, 2025
Follow-on after #14637. Just a small stylistic thing - reorder a few lines.
graphite-app bot pushed a commit that referenced this pull request Oct 17, 2025
…linted (#14727)

Follow on after #14637. I now realize that there was a subtle race condition.

We use `astId` counter in the getter for `program.comments`, to check that the getter is not called after that AST is done with. Buffers are reused, so the buffer held in local reference is the same, but the *contents* of the buffer could have changed - it may now contain a *different* AST.

Previously, `astId` was incremented in `deserializeProgram`. So if `comments` getter is accessed after another AST is deserialized, it (rightly) throws.

But there's a small period of time in between walking the AST on JS side and the *next* call to `deserializeProgram`. During that period, the buffer may be being mutated on Rust side (in another thread), but `astId` hasn't been incremented yet, so the `localAstId === astId` check in `comments` getter will pass, and it won't throw an error as it should.

Fix this by incrementing `astId` as soon as traversal of *this* AST ends, instead of before deserializing the *next* AST begins.
graphite-app bot pushed a commit that referenced this pull request Oct 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ast Area - AST A-ast-tools Area - AST tools A-cli Area - CLI A-linter Area - Linter C-performance Category - Solution not expected to change functional behavior, only performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants