Merge pull request #636 from quake/quake/StraightHashMap

perf: change BTreeMap to StraightHashMap
This commit is contained in:
Stephen Chung 2022-09-05 21:08:32 +08:00 committed by GitHub
commit 0f4df9c4e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 59 additions and 44 deletions

View File

@ -27,6 +27,7 @@ rhai_codegen = { version = "1.4.1", path = "codegen", default-features = false }
no-std-compat = { version = "0.4", default-features = false, features = ["alloc"], optional = true } no-std-compat = { version = "0.4", default-features = false, features = ["alloc"], optional = true }
libm = { version = "0.2", default-features = false, optional = true } libm = { version = "0.2", default-features = false, optional = true }
hashbrown = { version = "0.12", optional = true }
core-error = { version = "0.0", default-features = false, features = ["alloc"], optional = true } core-error = { version = "0.0", default-features = false, features = ["alloc"], optional = true }
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true } serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true }
serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true } serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true }
@ -63,7 +64,7 @@ debugging = ["internals"] # enable debugging
serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types
# compiling for no-std # compiling for no-std
no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"] no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng", "hashbrown"]
# compiling for WASM # compiling for WASM
wasm-bindgen = ["instant/wasm-bindgen"] wasm-bindgen = ["instant/wasm-bindgen"]

View File

@ -1,10 +1,10 @@
//! System caches. //! System caches.
use crate::func::CallableFunction; use crate::func::{CallableFunction, StraightHashMap};
use crate::{Identifier, StaticVec}; use crate::{Identifier, StaticVec};
use std::marker::PhantomData;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{collections::BTreeMap, marker::PhantomData};
/// _(internals)_ An entry in a function resolution cache. /// _(internals)_ An entry in a function resolution cache.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
@ -21,7 +21,7 @@ pub struct FnResolutionCacheEntry {
/// ///
/// [`FnResolutionCacheEntry`] is [`Box`]ed in order to pack as many entries inside a single B-Tree /// [`FnResolutionCacheEntry`] is [`Box`]ed in order to pack as many entries inside a single B-Tree
/// level as possible. /// level as possible.
pub type FnResolutionCache = BTreeMap<u64, Option<FnResolutionCacheEntry>>; pub type FnResolutionCache = StraightHashMap<u64, Option<FnResolutionCacheEntry>>;
/// _(internals)_ A type containing system-wide caches. /// _(internals)_ A type containing system-wide caches.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
@ -66,7 +66,7 @@ impl Caches<'_> {
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
pub fn push_fn_resolution_cache(&mut self) { pub fn push_fn_resolution_cache(&mut self) {
self.fn_resolution.push(BTreeMap::new()); self.fn_resolution.push(StraightHashMap::default());
} }
/// Rewind the function resolution caches stack to a particular size. /// Rewind the function resolution caches stack to a particular size.
#[inline(always)] #[inline(always)]

View File

@ -11,8 +11,12 @@ use crate::func::{
use crate::types::dynamic::AccessMode; use crate::types::dynamic::AccessMode;
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR}; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use hashbrown::hash_map::Entry;
#[cfg(not(feature = "no_std"))]
use std::collections::hash_map::Entry;
use std::num::NonZeroUsize;
#[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{collections::btree_map::Entry, num::NonZeroUsize};
impl Engine { impl Engine {
/// Search for a module within an imports stack. /// Search for a module within an imports stack.

View File

@ -7,6 +7,16 @@ use std::{
hash::{BuildHasher, Hash, Hasher}, hash::{BuildHasher, Hash, Hasher},
}; };
#[cfg(feature = "no_std")]
pub type StraightHashMap<K, V> = hashbrown::HashMap<K, V, StraightHasherBuilder>;
#[cfg(feature = "no_std")]
pub type StraightHashSet<K> = hashbrown::HashSet<K, StraightHasherBuilder>;
#[cfg(not(feature = "no_std"))]
pub type StraightHashMap<K, V> = std::collections::HashMap<K, V, StraightHasherBuilder>;
#[cfg(not(feature = "no_std"))]
pub type StraightHashSet<K> = std::collections::HashSet<K, StraightHasherBuilder>;
/// Dummy hash value to map zeros to. This value can be anything. /// Dummy hash value to map zeros to. This value can be anything.
/// ///
/// # Notes /// # Notes
@ -30,7 +40,7 @@ pub const ALT_ZERO_HASH: u64 = 42;
/// ///
/// Panics when hashing any data type other than a [`u64`]. /// Panics when hashing any data type other than a [`u64`].
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
struct StraightHasher(u64); pub struct StraightHasher(u64);
impl Hasher for StraightHasher { impl Hasher for StraightHasher {
#[inline(always)] #[inline(always)]
@ -38,23 +48,22 @@ impl Hasher for StraightHasher {
self.0 self.0
} }
#[inline] #[inline]
fn write(&mut self, bytes: &[u8]) { fn write(&mut self, _bytes: &[u8]) {
assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values"); panic!("StraightHasher can only hash u64 values");
}
let mut key = [0_u8; 8]; fn write_u64(&mut self, i: u64) {
key.copy_from_slice(bytes); if i == 0 {
self.0 = u64::from_ne_bytes(key);
if self.0 == 0 {
self.0 = ALT_ZERO_HASH; self.0 = ALT_ZERO_HASH;
} else {
self.0 = i;
} }
} }
} }
/// A hash builder for `StraightHasher`. /// A hash builder for `StraightHasher`.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
struct StraightHasherBuilder; pub struct StraightHasherBuilder;
impl BuildHasher for StraightHasherBuilder { impl BuildHasher for StraightHasherBuilder {
type Hasher = StraightHasher; type Hasher = StraightHasher;

View File

@ -21,7 +21,7 @@ pub use callable_function::CallableFunction;
pub use func::Func; pub use func::Func;
pub use hashing::{ pub use hashing::{
calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, calc_qualified_var_hash, calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, calc_qualified_var_hash,
combine_hashes, get_hasher, combine_hashes, get_hasher, StraightHashMap, StraightHashSet,
}; };
pub use native::{ pub use native::{
locked_read, locked_write, shared_get_mut, shared_make_mut, shared_take, shared_take_or_clone, locked_read, locked_write, shared_get_mut, shared_make_mut, shared_take, shared_take_or_clone,

View File

@ -23,6 +23,8 @@ use std::{
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
use crate::func::register::Mut; use crate::func::register::Mut;
use crate::func::{StraightHashMap, StraightHashSet};
/// A type representing the namespace of a function. /// A type representing the namespace of a function.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "metadata", derive(serde::Serialize))] #[cfg_attr(feature = "metadata", derive(serde::Serialize))]
@ -267,18 +269,18 @@ pub struct Module {
/// [`Module`] variables. /// [`Module`] variables.
variables: BTreeMap<Identifier, Dynamic>, variables: BTreeMap<Identifier, Dynamic>,
/// Flattened collection of all [`Module`] variables, including those in sub-modules. /// Flattened collection of all [`Module`] variables, including those in sub-modules.
all_variables: BTreeMap<u64, Dynamic>, all_variables: StraightHashMap<u64, Dynamic>,
/// Functions (both native Rust and scripted). /// Functions (both native Rust and scripted).
functions: BTreeMap<u64, Box<FuncInfo>>, functions: StraightHashMap<u64, Box<FuncInfo>>,
/// Flattened collection of all functions, native Rust and scripted. /// Flattened collection of all functions, native Rust and scripted.
/// including those in sub-modules. /// including those in sub-modules.
all_functions: BTreeMap<u64, CallableFunction>, all_functions: StraightHashMap<u64, CallableFunction>,
/// Native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters. /// Native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters.
dynamic_functions: BTreeSet<u64>, dynamic_functions: StraightHashSet<u64>,
/// Iterator functions, keyed by the type producing the iterator. /// Iterator functions, keyed by the type producing the iterator.
type_iterators: BTreeMap<TypeId, Shared<IteratorFn>>, type_iterators: StraightHashMap<TypeId, Shared<IteratorFn>>,
/// Flattened collection of iterator functions, including those in sub-modules. /// Flattened collection of iterator functions, including those in sub-modules.
all_type_iterators: BTreeMap<TypeId, Shared<IteratorFn>>, all_type_iterators: StraightHashMap<TypeId, Shared<IteratorFn>>,
/// Is the [`Module`] indexed? /// Is the [`Module`] indexed?
indexed: bool, indexed: bool,
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace? /// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
@ -372,12 +374,12 @@ impl Module {
custom_types: CustomTypesCollection::new(), custom_types: CustomTypesCollection::new(),
modules: BTreeMap::new(), modules: BTreeMap::new(),
variables: BTreeMap::new(), variables: BTreeMap::new(),
all_variables: BTreeMap::new(), all_variables: StraightHashMap::default(),
functions: BTreeMap::new(), functions: StraightHashMap::default(),
all_functions: BTreeMap::new(), all_functions: StraightHashMap::default(),
dynamic_functions: BTreeSet::new(), dynamic_functions: StraightHashSet::default(),
type_iterators: BTreeMap::new(), type_iterators: StraightHashMap::default(),
all_type_iterators: BTreeMap::new(), all_type_iterators: StraightHashMap::default(),
indexed: true, indexed: true,
contains_indexed_global_functions: false, contains_indexed_global_functions: false,
} }
@ -2137,9 +2139,9 @@ impl Module {
fn index_module<'a>( fn index_module<'a>(
module: &'a Module, module: &'a Module,
path: &mut Vec<&'a str>, path: &mut Vec<&'a str>,
variables: &mut BTreeMap<u64, Dynamic>, variables: &mut StraightHashMap<u64, Dynamic>,
functions: &mut BTreeMap<u64, CallableFunction>, functions: &mut StraightHashMap<u64, CallableFunction>,
type_iterators: &mut BTreeMap<TypeId, Shared<IteratorFn>>, type_iterators: &mut StraightHashMap<TypeId, Shared<IteratorFn>>,
) -> bool { ) -> bool {
let mut contains_indexed_global_functions = false; let mut contains_indexed_global_functions = false;
@ -2201,9 +2203,9 @@ impl Module {
if !self.indexed { if !self.indexed {
let mut path = Vec::with_capacity(4); let mut path = Vec::with_capacity(4);
let mut variables = BTreeMap::new(); let mut variables = StraightHashMap::default();
let mut functions = BTreeMap::new(); let mut functions = StraightHashMap::default();
let mut type_iterators = BTreeMap::new(); let mut type_iterators = StraightHashMap::default();
path.push(""); path.push("");

View File

@ -9,7 +9,7 @@ use crate::ast::{
}; };
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
use crate::eval::GlobalRuntimeState; use crate::eval::GlobalRuntimeState;
use crate::func::hashing::get_hasher; use crate::func::{hashing::get_hasher, StraightHashMap};
use crate::tokenizer::{ use crate::tokenizer::{
is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream, is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream,
TokenizerControl, TokenizerControl,
@ -32,7 +32,7 @@ use std::{
pub type ParseResult<T> = Result<T, ParseError>; pub type ParseResult<T> = Result<T, ParseError>;
type FnLib = BTreeMap<u64, Shared<ScriptFnDef>>; type FnLib = StraightHashMap<u64, Shared<ScriptFnDef>>;
const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax(); const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax();
@ -3790,7 +3790,7 @@ impl Engine {
state: &mut ParseState, state: &mut ParseState,
_optimization_level: OptimizationLevel, _optimization_level: OptimizationLevel,
) -> ParseResult<AST> { ) -> ParseResult<AST> {
let mut functions = BTreeMap::new(); let mut functions = StraightHashMap::default();
let mut options = self.options; let mut options = self.options;
options.remove(LangOptions::STMT_EXPR); options.remove(LangOptions::STMT_EXPR);
@ -3850,7 +3850,7 @@ impl Engine {
state: &mut ParseState, state: &mut ParseState,
) -> ParseResult<(StmtBlockContainer, StaticVec<Shared<ScriptFnDef>>)> { ) -> ParseResult<(StmtBlockContainer, StaticVec<Shared<ScriptFnDef>>)> {
let mut statements = StmtBlockContainer::new_const(); let mut statements = StmtBlockContainer::new_const();
let mut functions = BTreeMap::new(); let mut functions = StraightHashMap::default();
while !input.peek().expect(NEVER_ENDS).0.is_eof() { while !input.peek().expect(NEVER_ENDS).0.is_eof() {
let settings = ParseSettings { let settings = ParseSettings {

View File

@ -1,10 +1,9 @@
use crate::func::hashing::get_hasher; use crate::func::{hashing::get_hasher, StraightHashMap};
use crate::ImmutableString; use crate::ImmutableString;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{ use std::{
collections::BTreeMap,
fmt, fmt,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
marker::PhantomData, marker::PhantomData,
@ -21,14 +20,14 @@ pub const MAX_STRING_LEN: usize = 24;
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
/// Normal identifiers, property getters and setters are interned separately. /// Normal identifiers, property getters and setters are interned separately.
#[derive(Clone, Hash)] #[derive(Clone)]
pub struct StringsInterner<'a> { pub struct StringsInterner<'a> {
/// Maximum number of strings interned. /// Maximum number of strings interned.
pub capacity: usize, pub capacity: usize,
/// Maximum string length. /// Maximum string length.
pub max_string_len: usize, pub max_string_len: usize,
/// Normal strings. /// Normal strings.
strings: BTreeMap<u64, ImmutableString>, strings: StraightHashMap<u64, ImmutableString>,
/// Take care of the lifetime parameter. /// Take care of the lifetime parameter.
dummy: PhantomData<&'a ()>, dummy: PhantomData<&'a ()>,
} }
@ -55,7 +54,7 @@ impl StringsInterner<'_> {
Self { Self {
capacity: MAX_INTERNED_STRINGS, capacity: MAX_INTERNED_STRINGS,
max_string_len: MAX_STRING_LEN, max_string_len: MAX_STRING_LEN,
strings: BTreeMap::new(), strings: StraightHashMap::default(),
dummy: PhantomData, dummy: PhantomData,
} }
} }