From 98fa3c33c288ec0332b6055fc8cfb6025771d282 Mon Sep 17 00:00:00 2001 From: "Guillem L. Jara" <4lon3ly0@tutanota.com> Date: Mon, 25 May 2026 01:50:22 +0200 Subject: [PATCH] perf: switch to ahash, indexmap-allocator-api --- Cargo.lock | 79 +++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 +- interpreter/Cargo.toml | 6 ++- interpreter/src/ir/lower.rs | 4 +- interpreter/src/vm.rs | 25 ++++++------ parser/Cargo.toml | 1 + parser/src/ast.rs | 5 ++- parser/src/lib.rs | 3 +- 8 files changed, 102 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8fa38a2..24819c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,19 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -420,6 +433,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + [[package]] name = "getrandom" version = "0.4.2" @@ -428,7 +453,7 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "rand_core", "wasip2", "wasip3", @@ -452,6 +477,8 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2 0.2.21", + "equivalent", "foldhash 0.1.5", ] @@ -496,16 +523,27 @@ dependencies = [ "serde_core", ] +[[package]] +name = "indexmap-allocator-api" +version = "2.7.1" +source = "git+https://github.com/Hans-Halverson/indexmap-allocator-api.git?rev=36852f985c01fb9f58f2d9e510655ca1dbb1f4d0#36852f985c01fb9f58f2d9e510655ca1dbb1f4d0" +dependencies = [ + "allocator-api2 0.2.21", + "equivalent", + "hashbrown 0.15.5", +] + [[package]] name = "interpreter" version = "0.1.0" dependencies = [ + "ahash", "allocator-api2 0.4.0", "bumpalo", "color-eyre", "either", "hashbrown 0.17.1", - "indexmap", + "indexmap-allocator-api", "memchr", "parser", "thiserror", @@ -769,6 +807,7 @@ checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" name = "parser" version = "0.1.0" dependencies = [ + "ahash", "allocator-api2 0.4.0", "ariadne", "bumpalo", @@ -846,6 +885,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r-efi" version = "6.0.0" @@ -859,7 +904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", - "getrandom", + "getrandom 0.4.2", "rand_core", ] @@ -1036,7 +1081,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom", + "getrandom 0.4.2", "once_cell", "rustix", "windows-sys 0.61.2", @@ -1324,6 +1369,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasip2" version = "1.0.3+wasi-0.2.9" @@ -1584,6 +1635,26 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zerofrom" version = "0.1.7" diff --git a/Cargo.toml b/Cargo.toml index 7bf82c3..2dc0861 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,9 +30,9 @@ smallvec = { version = "1.15.1", features = ["union", "const_generics", "const_n bumpalo = { version = "3.20.2", features = ["boxed", "collections", "std", "allocator-api2"] } hashbrown = "0.17.0" allocator-api2 = "0.4.0" +ahash = "0.8.12" [dependencies] -# lexer.workspace = true # FIXME(parser complete): bypass through parser interpreter.workspace = true parser.workspace = true color-eyre.workspace = true diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml index f2ba5fc..90c0bb3 100644 --- a/interpreter/Cargo.toml +++ b/interpreter/Cargo.toml @@ -14,7 +14,11 @@ parser.workspace = true bumpalo.workspace = true allocator-api2.workspace = true hashbrown.workspace = true -indexmap = "2.14.0" +ahash.workspace = true +# I'm not particularly happy about this, but it's crucial for performance, and +# I have audited the two commits. It's pinned to that hash to avoid possible +# supply-chain attacks. Hopefully upstream maintainers will change their mind. +indexmap-allocator-api = { git = 'https://github.com/Hans-Halverson/indexmap-allocator-api.git', rev = "36852f985c01fb9f58f2d9e510655ca1dbb1f4d0" } [lints] workspace = true diff --git a/interpreter/src/ir/lower.rs b/interpreter/src/ir/lower.rs index 161d48c..6bc11d7 100644 --- a/interpreter/src/ir/lower.rs +++ b/interpreter/src/ir/lower.rs @@ -20,7 +20,7 @@ use crate::{ pub struct Code<'arena> { pub arena: &'arena Bump, pub bc: Bytecode<'arena>, - pub consts: Consts, + pub consts: Consts<'arena>, pub symbols: SymbolTable<'arena>, free_regs: Vec<'arena, Reg>, pub reg_pointer: u16, @@ -240,7 +240,7 @@ pub fn test_interpreter(stmnt: &Body<'_>) -> String { let mut c = Code { arena: &bump, bc: Bytecode::new_in(&bump), - consts: Consts::new(), + consts: Consts::new_in(&bump), symbols: SymbolTable::new_in(&bump), reg_pointer: 0, free_regs: Vec::new_in(&bump), diff --git a/interpreter/src/vm.rs b/interpreter/src/vm.rs index 10970c7..e02a298 100644 --- a/interpreter/src/vm.rs +++ b/interpreter/src/vm.rs @@ -1,8 +1,9 @@ use std::fmt::{self, Display}; +use ahash::RandomState; use bumpalo::{Bump, collections::Vec}; -use hashbrown::{DefaultHashBuilder, HashMap}; -use indexmap::{IndexMap, IndexSet}; +use hashbrown::HashMap; +use indexmap_allocator_api::{IndexMap, IndexSet}; use parser::Identifier; use crate::ir::{ @@ -28,7 +29,7 @@ pub struct Interpreter<'a> { program_counter: usize, registers: Registers<'a>, symbols: SymbolTable<'a>, - consts: Consts, + consts: Consts<'a>, compat: ExecMode, } @@ -37,14 +38,14 @@ pub struct Registers<'a>(Vec<'a, Value>); #[derive(Debug)] pub struct SymbolTable<'a> { - user: IndexMap, Value>, + user: IndexMap, Value, RandomState, &'a Bump>, // separate table for cheap invalidation. It's an arena _visibly shrugs_. - records: HashMap, + records: HashMap, // etc } #[derive(Debug)] -pub struct Consts(pub IndexSet); +pub struct Consts<'a>(pub IndexSet); impl<'a> Interpreter<'a> { pub fn new(compat: ExecMode, code: Code<'a>) -> Self { @@ -63,8 +64,8 @@ impl<'a> Interpreter<'a> { impl<'a> SymbolTable<'a> { pub fn new_in(arena: &'a Bump) -> Self { Self { - user: IndexMap::new(), - records: HashMap::new_in(arena), + user: IndexMap::new_in(arena), + records: HashMap::with_hasher_in(RandomState::new(), arena), } } fn lookup_user_var(&self, var: NonLocal) -> &Value { @@ -88,9 +89,9 @@ impl<'a> SymbolTable<'a> { } } -impl Consts { - pub fn new() -> Self { - Self(IndexSet::with_capacity(4)) +impl<'a> Consts<'a> { + pub fn new_in(arena: &'a Bump) -> Self { + Self(IndexSet::with_capacity_in(4, arena)) } } @@ -195,7 +196,7 @@ impl Display for SymbolTable<'_> { } } -impl Display for Consts { +impl Display for Consts<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Consts:")?; fmt_list(f, self.0.iter(), |f, i, e| write!(f, "mem[{i}] = {e:?}")) diff --git a/parser/Cargo.toml b/parser/Cargo.toml index a66e987..958e0f0 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -14,6 +14,7 @@ smallvec.workspace = true bumpalo.workspace = true hashbrown.workspace = true allocator-api2.workspace = true +ahash.workspace = true ariadne = "0.6.0" [lints] diff --git a/parser/src/ast.rs b/parser/src/ast.rs index ce11db7..00e620b 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -5,9 +5,10 @@ use std::fmt::Debug; +use ahash::RandomState; use bumpalo::{Bump, boxed::Box, collections::Vec}; use either::Either; -use hashbrown::{DefaultHashBuilder, HashMap}; +use hashbrown::HashMap; use lexer::{Slice, Span, Token}; use crate::{ParsingError, Result, lex::TokenExt}; @@ -21,7 +22,7 @@ pub struct Ast<'a> { pub end_file: Vec<'a, Body<'a>>, pub rules: Vec<'a, Rule<'a>>, pub concurrent: Vec<'a, Rule<'a>>, - pub functions: HashMap, Function<'a>, DefaultHashBuilder, &'a Bump>, + pub functions: HashMap, Function<'a>, RandomState, &'a Bump>, } #[derive(Debug)] diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 52aa338..0021068 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -16,6 +16,7 @@ mod tests; use std::{fmt::Debug, mem::replace}; +use ahash::RandomState; use bumpalo::{Bump, boxed::Box, collections::Vec, vec}; use either::Either::{Left, Right}; use hashbrown::HashMap; @@ -685,7 +686,7 @@ impl<'a> Ast<'a> { end_file: Vec::new_in(arena), rules: Vec::new_in(arena), concurrent: Vec::new_in(arena), - functions: HashMap::new_in(arena), + functions: HashMap::with_hasher_in(RandomState::new(), arena), } } }