perf: do not reallocate rule context struct per-rule-per-file#742
Merged
graphite-app[bot] merged 1 commit intomainfrom Feb 26, 2026
Conversation
Member
Author
How to use the Graphite Merge QueueAdd the label 0-merge to this PR to add it to the merge queue. 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. This stack of pull requests is managed by Graphite. Learn more about stacking. |
0947a77 to
865254c
Compare
f009b39 to
08523d8
Compare
08523d8 to
9562f2d
Compare
9562f2d to
4b54cd9
Compare
This was referenced Feb 25, 2026
camc314
requested changes
Feb 25, 2026
4b54cd9 to
22e16c3
Compare
Contributor
Merge activity
|
graphite-app Bot
pushed a commit
that referenced
this pull request
Feb 26, 2026
~30% faster, ~48% less memory used, and ~37% fewer allocations on our simple benchmark. **IMPORTANT**: **this does _not_ mean that `oxlint` type-aware linting will run this much faster.** When I ran this with `oxlint`, it was not noticeably faster, it was maybe 1% better. But it was consistently faster, so I think it's worth merging. The intuition for the small real-world speedup is that the bottleneck is in the program creation and reading files from the filesystem, not the linting part itself. The main idea is that we are currently re-allocating the rule context for every single file and every single rule. In most cases, though the behavior is exactly the same, we are just changing what file are rule we are linting with. I refactored the diagnostic reporting code to be such that it never reallocates the diagnostic reporting functions: instead, we update the rule name and file and mutate the diagnostic reported with the current state after we've received it. This means we don't need to allocate this struct over and over again, which should reduce pressure on the GC quite a bit (this does show up in the benchmark too). Before: ``` goos: darwin goarch: arm64 pkg: github.com/typescript-eslint/tsgolint/cmd/tsgolint cpu: Apple M1 BenchmarkAllRulesHeadless-8 4495 2604683 ns/op 7640387 B/op 83526 allocs/op BenchmarkAllRulesHeadless-8 4442 2599917 ns/op 7675330 B/op 83833 allocs/op BenchmarkAllRulesHeadless-8 4574 2590637 ns/op 7669025 B/op 83771 allocs/op BenchmarkAllRulesHeadless-8 4442 2686293 ns/op 7647700 B/op 83587 allocs/op BenchmarkAllRulesHeadless-8 4291 2728212 ns/op 7654198 B/op 83647 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2041 5863713 ns/op 7629003 B/op 83638 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2029 5873142 ns/op 7622800 B/op 83581 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2034 5863612 ns/op 7614713 B/op 83508 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2018 5881180 ns/op 7630698 B/op 83649 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2038 5890125 ns/op 7635571 B/op 83689 allocs/op ``` After: ``` goos: darwin goarch: arm64 pkg: github.com/typescript-eslint/tsgolint/cmd/tsgolint cpu: Apple M1 BenchmarkAllRulesHeadless-8 6270 1827035 ns/op 4372362 B/op 52719 allocs/op BenchmarkAllRulesHeadless-8 6024 1794715 ns/op 4372635 B/op 52719 allocs/op BenchmarkAllRulesHeadless-8 6276 1795347 ns/op 4354735 B/op 52559 allocs/op BenchmarkAllRulesHeadless-8 6327 1837421 ns/op 4389465 B/op 52866 allocs/op BenchmarkAllRulesHeadless-8 6244 1863864 ns/op 4375612 B/op 52741 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2756 4325317 ns/op 3948056 B/op 52395 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2791 4327110 ns/op 3953848 B/op 52463 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2770 4336311 ns/op 3924766 B/op 52215 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2758 4342168 ns/op 3948428 B/op 52401 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2726 4339023 ns/op 3954526 B/op 52460 allocs/op ```
22e16c3 to
a1c5ecd
Compare
~30% faster, ~48% less memory used, and ~37% fewer allocations on our simple benchmark. **IMPORTANT**: **this does _not_ mean that `oxlint` type-aware linting will run this much faster.** When I ran this with `oxlint`, it was not noticeably faster, it was maybe 1% better. But it was consistently faster, so I think it's worth merging. The intuition for the small real-world speedup is that the bottleneck is in the program creation and reading files from the filesystem, not the linting part itself. The main idea is that we are currently re-allocating the rule context for every single file and every single rule. In most cases, though the behavior is exactly the same, we are just changing what file are rule we are linting with. I refactored the diagnostic reporting code to be such that it never reallocates the diagnostic reporting functions: instead, we update the rule name and file and mutate the diagnostic reported with the current state after we've received it. This means we don't need to allocate this struct over and over again, which should reduce pressure on the GC quite a bit (this does show up in the benchmark too). Before: ``` goos: darwin goarch: arm64 pkg: github.com/typescript-eslint/tsgolint/cmd/tsgolint cpu: Apple M1 BenchmarkAllRulesHeadless-8 4495 2604683 ns/op 7640387 B/op 83526 allocs/op BenchmarkAllRulesHeadless-8 4442 2599917 ns/op 7675330 B/op 83833 allocs/op BenchmarkAllRulesHeadless-8 4574 2590637 ns/op 7669025 B/op 83771 allocs/op BenchmarkAllRulesHeadless-8 4442 2686293 ns/op 7647700 B/op 83587 allocs/op BenchmarkAllRulesHeadless-8 4291 2728212 ns/op 7654198 B/op 83647 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2041 5863713 ns/op 7629003 B/op 83638 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2029 5873142 ns/op 7622800 B/op 83581 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2034 5863612 ns/op 7614713 B/op 83508 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2018 5881180 ns/op 7630698 B/op 83649 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2038 5890125 ns/op 7635571 B/op 83689 allocs/op ``` After: ``` goos: darwin goarch: arm64 pkg: github.com/typescript-eslint/tsgolint/cmd/tsgolint cpu: Apple M1 BenchmarkAllRulesHeadless-8 6270 1827035 ns/op 4372362 B/op 52719 allocs/op BenchmarkAllRulesHeadless-8 6024 1794715 ns/op 4372635 B/op 52719 allocs/op BenchmarkAllRulesHeadless-8 6276 1795347 ns/op 4354735 B/op 52559 allocs/op BenchmarkAllRulesHeadless-8 6327 1837421 ns/op 4389465 B/op 52866 allocs/op BenchmarkAllRulesHeadless-8 6244 1863864 ns/op 4375612 B/op 52741 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2756 4325317 ns/op 3948056 B/op 52395 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2791 4327110 ns/op 3953848 B/op 52463 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2770 4336311 ns/op 3924766 B/op 52215 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2758 4342168 ns/op 3948428 B/op 52401 allocs/op BenchmarkAllRulesHeadlessSingleThread-8 2726 4339023 ns/op 3954526 B/op 52460 allocs/op ```
a1c5ecd to
502890a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

~30% faster, ~48% less memory used, and ~37% fewer allocations on our simple benchmark. IMPORTANT: this does not mean that
oxlinttype-aware linting will run this much faster. When I ran this withoxlint, it was not noticeably faster, it was maybe 1% better. But it was consistently faster, so I think it's worth merging. The intuition for the small real-world speedup is that the bottleneck is in the program creation and reading files from the filesystem, not the linting part itself.The main idea is that we are currently re-allocating the rule context for every single file and every single rule. In most cases, though the behavior is exactly the same, we are just changing what file are rule we are linting with.
I refactored the diagnostic reporting code to be such that it never reallocates the diagnostic reporting functions: instead, we update the rule name and file and mutate the diagnostic reported with the current state after we've received it. This means we don't need to allocate this struct over and over again, which should reduce pressure on the GC quite a bit (this does show up in the benchmark too).
Before:
After: