Skip to content

Commit f6a9687

Browse files
committed
perf(linter): store rules by AST type in a boxed array (#13578)
Follow-on after #13138. `rules_by_ast_type` has a fixed length, known at compile time. So use a boxed array for it, instead of a `Vec`. This has 2 advantages: 1. `Box<[Vec<T>; N]>` is 8 bytes, instead of 24 for `Vec<Vec<T>>`. 2. The length of the array is encoded in the type, so it may allow compiler to deduce that indexing into it with an `AstType` cannot go out of bounds, and remove bounds checks (maybe - if compiler is smart enough, it may already be able to deduce that with the `Vec`).
1 parent ed77c31 commit f6a9687

File tree

1 file changed

+11
-1
lines changed

1 file changed

+11
-1
lines changed

crates/oxc_linter/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,23 @@ impl Linter {
169169
//
170170
// See https://github.com/oxc-project/oxc/pull/6600 for more context.
171171
if semantic.nodes().len() > 200_000 {
172+
const AST_TYPES_LEN: usize = AST_TYPE_MAX as usize + 1;
173+
172174
// Collect rules into a Vec so that we can iterate over the rules multiple times
173175
let rules = rules.collect::<Vec<_>>();
174176

175177
// TODO: It seems like there is probably a more intelligent way to preallocate space here. This will
176178
// likely incur quite a few unnecessary reallocs currently. We theoretically could compute this at
177179
// compile-time since we know all of the rules and their AST node type information ahead of time.
178-
let mut rules_by_ast_type = vec![Vec::new(); AST_TYPE_MAX as usize + 1];
180+
//
181+
// Convert to boxed array to help compiler see that indexing into it with an `AstType`
182+
// cannot go out of bounds, and remove bounds checks. The `unwrap` is infallible and should be optimized out.
183+
let rules_by_ast_type = vec![Vec::new(); AST_TYPES_LEN];
184+
#[expect(clippy::missing_panics_doc, reason = "infallible")]
185+
let mut rules_by_ast_type =
186+
Box::<[_; AST_TYPES_LEN]>::try_from(rules_by_ast_type.into_boxed_slice())
187+
.ok()
188+
.unwrap();
179189
// TODO: Compute needed capacity. This is a slight overestimate as not 100% of rules will need to run on all
180190
// node types, but it at least guarantees we won't need to realloc.
181191
let mut rules_any_ast_type = Vec::with_capacity(rules.len());

0 commit comments

Comments
 (0)