perf(generate): memoize get_production_id

The production info depends only on the production, but was recomputed and
deduplicated via a linear deep-equality scan over production_infos for every
reduce item in every state. Cache `prod_id` -> `ProductionInfoId`.

Wall time reduction 0-3%, peak rss neutral.
This commit is contained in:
Will Lillis 2026-08-09 11:51:45 -05:00
parent d6ae19cc6d
commit 2a81995058

View file

@ -15,7 +15,7 @@ use super::{
};
use crate::{
Diagnostic,
build_tables::item::{LookaheadSetPool, prec_display},
build_tables::item::{LookaheadSetPool, START_PRODUCTION_ID, prec_display},
grammars::{LexicalGrammar, PrecedenceEntry, ReservedWordSetId, SyntaxGrammar, VariableType},
node_types::VariableInfo,
rules::{Associativity, Precedence, Symbol, SymbolType, TokenSet},
@ -73,6 +73,7 @@ struct ParseTableBuilder<'a> {
core_ids_by_core: FxHashMap<ParseItemSetCore<'a>, usize>,
state_ids_by_item_set: IndexMap<ParseItemSet<'a>, ParseStateId, BuildHasherDefault<FxHasher>>,
preceding_symbols_by_id: Vec<SymbolSequence>,
production_info_ids_by_prod_id: Vec<Option<ProductionInfoId>>,
parse_state_queue: VecDeque<ParseStateQueueEntry>,
non_terminal_extra_states: Vec<(Symbol, usize)>,
actual_conflicts: FxHashSet<Vec<Symbol>>,
@ -270,6 +271,7 @@ impl<'a> ParseTableBuilder<'a> {
state_ids_by_item_set: IndexMap::default(),
core_ids_by_core: FxHashMap::default(),
preceding_symbols_by_id: Vec::new(),
production_info_ids_by_prod_id: vec![None; syntax_grammar.productions.len()],
parse_state_queue: VecDeque::new(),
actual_conflicts: syntax_grammar.expected_conflicts.iter().cloned().collect(),
parse_table: ParseTable {
@ -1138,6 +1140,10 @@ impl<'a> ParseTableBuilder<'a> {
}
fn get_production_id(&mut self, item: &ParseItem) -> ProductionInfoId {
debug_assert_ne!(item.prod_id, START_PRODUCTION_ID);
if let Some(id) = self.production_info_ids_by_prod_id[item.prod_id as usize] {
return id;
}
let mut production_info = ProductionInfo {
alias_sequence: Vec::new(),
field_map: BTreeMap::new(),
@ -1191,7 +1197,7 @@ impl<'a> ParseTableBuilder<'a> {
item.production(self.syntax_grammar).steps.len();
}
if let Some(index) = self
let id = if let Some(index) = self
.parse_table
.production_infos
.iter()
@ -1201,7 +1207,9 @@ impl<'a> ParseTableBuilder<'a> {
} else {
self.parse_table.production_infos.push(production_info);
self.parse_table.production_infos.len() - 1
}
};
self.production_info_ids_by_prod_id[item.prod_id as usize] = Some(id);
id
}
fn symbol_name(&self, symbol: Symbol) -> String {