From fbf08ac54512f8b3b1b10e4796c1c553db88e76d Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Tue, 12 Jan 2021 22:21:45 +0100 Subject: [PATCH] Faster compilation of bevy_diagnostic (#1235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove AHashExt There is little benefit of Hash*::new() over Hash*::default(), but it does require more code that needs to be duplicated for every Hash* in bevy_utils. It may also slightly increase compile times. * Add StableHash* to bevy_utils * Use StableHashMap instead of HashMap + BTreeSet for diagnostics This is a significant reduction in the release mode compile times of bevy_diagnostics ``` Benchmark #1: touch crates/bevy_diagnostic/src/lib.rs && cargo build --release -p bevy_diagnostic -j1 Time (mean ± σ): 3.645 s ± 0.009 s [User: 3.551 s, System: 0.094 s] Range (min … max): 3.632 s … 3.658 s 20 runs ``` ``` Benchmark #1: touch crates/bevy_diagnostic/src/lib.rs && cargo build --release -p bevy_diagnostic -j1 Time (mean ± σ): 2.938 s ± 0.012 s [User: 2.850 s, System: 0.090 s] Range (min … max): 2.919 s … 2.969 s 20 runs ``` --- crates/bevy_diagnostic/src/diagnostic.rs | 16 +-- .../src/log_diagnostics_plugin.rs | 4 +- crates/bevy_input/src/axis.rs | 4 +- .../render_graph/nodes/texture_copy_node.rs | 4 +- crates/bevy_text/src/font_atlas_set.rs | 4 +- crates/bevy_utils/src/lib.rs | 102 +++++------------- 6 files changed, 42 insertions(+), 92 deletions(-) diff --git a/crates/bevy_diagnostic/src/diagnostic.rs b/crates/bevy_diagnostic/src/diagnostic.rs index 549ecdb20f755..416bb8ac45394 100644 --- a/crates/bevy_diagnostic/src/diagnostic.rs +++ b/crates/bevy_diagnostic/src/diagnostic.rs @@ -1,5 +1,5 @@ -use bevy_utils::{Duration, HashMap, Instant, Uuid}; -use std::collections::{BTreeSet, VecDeque}; +use bevy_utils::{Duration, Instant, StableHashMap, Uuid}; +use std::collections::VecDeque; /// Unique identifier for a [Diagnostic] #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)] @@ -101,13 +101,13 @@ impl Diagnostic { /// A collection of [Diagnostic]s #[derive(Debug, Default)] pub struct Diagnostics { - diagnostics: HashMap, - ordered_diagnostics: BTreeSet, + // This uses a [`StableHashMap`] to ensure that the iteration order is deterministic between + // runs when all diagnostics are inserted in the same order. + diagnostics: StableHashMap, } impl Diagnostics { pub fn add(&mut self, diagnostic: Diagnostic) { - self.ordered_diagnostics.insert(diagnostic.id); self.diagnostics.insert(diagnostic.id, diagnostic); } @@ -134,10 +134,4 @@ impl Diagnostics { pub fn iter(&self) -> impl Iterator { self.diagnostics.values() } - - pub fn ordered_iter(&self) -> impl Iterator { - self.ordered_diagnostics - .iter() - .filter_map(move |k| self.diagnostics.get(k)) - } } diff --git a/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs index 276480786f611..6e5d2636d7670 100644 --- a/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs @@ -78,7 +78,7 @@ impl LogDiagnosticsPlugin { Self::log_diagnostic(diagnostic); } } else { - for diagnostic in diagnostics.ordered_iter() { + for diagnostic in diagnostics.iter() { Self::log_diagnostic(diagnostic); } } @@ -96,7 +96,7 @@ impl LogDiagnosticsPlugin { debug!("{:#?}\n", diagnostic); } } else { - for diagnostic in diagnostics.ordered_iter() { + for diagnostic in diagnostics.iter() { debug!("{:#?}\n", diagnostic); } } diff --git a/crates/bevy_input/src/axis.rs b/crates/bevy_input/src/axis.rs index 56a2b35c0bca5..529237c3f44a5 100644 --- a/crates/bevy_input/src/axis.rs +++ b/crates/bevy_input/src/axis.rs @@ -1,4 +1,4 @@ -use bevy_utils::{AHashExt, HashMap}; +use bevy_utils::HashMap; use std::hash::Hash; #[derive(Debug)] @@ -12,7 +12,7 @@ where { fn default() -> Self { Axis { - axis_data: HashMap::new(), + axis_data: HashMap::default(), } } } diff --git a/crates/bevy_render/src/render_graph/nodes/texture_copy_node.rs b/crates/bevy_render/src/render_graph/nodes/texture_copy_node.rs index f2f0d7500429c..8959a8496dc85 100644 --- a/crates/bevy_render/src/render_graph/nodes/texture_copy_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/texture_copy_node.rs @@ -6,7 +6,7 @@ use crate::{ use bevy_app::prelude::{EventReader, Events}; use bevy_asset::{AssetEvent, Assets}; use bevy_ecs::{Resources, World}; -use bevy_utils::{AHashExt, HashSet}; +use bevy_utils::HashSet; #[derive(Default)] pub struct TextureCopyNode { @@ -24,7 +24,7 @@ impl Node for TextureCopyNode { ) { let texture_events = resources.get::>>().unwrap(); let textures = resources.get::>().unwrap(); - let mut copied_textures = HashSet::new(); + let mut copied_textures = HashSet::default(); for event in self.texture_event_reader.iter(&texture_events) { match event { AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { diff --git a/crates/bevy_text/src/font_atlas_set.rs b/crates/bevy_text/src/font_atlas_set.rs index 3ccca9b5e77ae..c073ab31ca3c3 100644 --- a/crates/bevy_text/src/font_atlas_set.rs +++ b/crates/bevy_text/src/font_atlas_set.rs @@ -6,7 +6,7 @@ use bevy_math::Vec2; use bevy_reflect::TypeUuid; use bevy_render::texture::Texture; use bevy_sprite::TextureAtlas; -use bevy_utils::{AHashExt, HashMap}; +use bevy_utils::HashMap; type FontSizeKey = FloatOrd; @@ -25,7 +25,7 @@ pub struct GlyphAtlasInfo { impl Default for FontAtlasSet { fn default() -> Self { FontAtlasSet { - font_atlases: HashMap::with_capacity(1), + font_atlases: HashMap::with_capacity_and_hasher(1, Default::default()), } } } diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index c1afc20bd365b..86668cab71cec 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -11,51 +11,33 @@ pub type BoxedFuture<'a, T> = Pin + Send + 'a>>; #[cfg(target_arch = "wasm32")] pub type BoxedFuture<'a, T> = Pin + 'a>>; +/// A hasher builder that will create a fixed hasher. +#[derive(Default)] +pub struct FixedState; + +impl std::hash::BuildHasher for FixedState { + type Hasher = AHasher; + + #[inline] + fn build_hasher(&self) -> AHasher { + AHasher::new_with_keys(0, 0) + } +} + /// A std hash map implementing AHash, a high speed keyed hashing algorithm /// intended for use in in-memory hashmaps. /// /// AHash is designed for performance and is NOT cryptographically secure. pub type HashMap = std::collections::HashMap; -pub trait AHashExt { - fn new() -> Self; - - fn with_capacity(capacity: usize) -> Self; -} - -impl AHashExt for HashMap { - /// Creates an empty `HashMap` with AHash. - /// - /// The hash map is initially created with a capacity of 0, so it will not - /// allocate until it is first inserted into. - /// - /// # Examples - /// - /// ``` - /// use bevy_utils::{HashMap, AHashExt}; - /// let mut map: HashMap<&str, i32> = HashMap::new(); - /// ``` - #[inline] - fn new() -> Self { - Default::default() - } - - /// Creates an empty `HashMap` with the specified capacity with AHash. - /// - /// The hash map will be able to hold at least `capacity` elements without - /// reallocating. If `capacity` is 0, the hash map will not allocate. - /// - /// # Examples - /// - /// ``` - /// use bevy_utils::{HashMap, AHashExt}; - /// let mut map: HashMap<&str, i32> = HashMap::with_capacity(10); - /// ``` - #[inline] - fn with_capacity(capacity: usize) -> Self { - HashMap::with_capacity_and_hasher(capacity, RandomState::default()) - } -} +/// A stable std hash map implementing AHash, a high speed keyed hashing algorithm +/// intended for use in in-memory hashmaps. +/// +/// Unlike [`HashMap`] this has an iteration order that only depends on the order +/// of insertions and deletions and not a random source. +/// +/// AHash is designed for performance and is NOT cryptographically secure. +pub type StableHashMap = std::collections::HashMap; /// A std hash set implementing AHash, a high speed keyed hashing algorithm /// intended for use in in-memory hashmaps. @@ -63,37 +45,11 @@ impl AHashExt for HashMap { /// AHash is designed for performance and is NOT cryptographically secure. pub type HashSet = std::collections::HashSet; -impl AHashExt for HashSet { - /// Creates an empty `HashSet` with AHash. - /// - /// The hash set is initially created with a capacity of 0, so it will not - /// allocate until it is first inserted into. - /// - /// # Examples - /// - /// ``` - /// use bevy_utils::{HashSet, AHashExt}; - /// let set: HashSet = HashSet::new(); - /// ``` - #[inline] - fn new() -> Self { - Default::default() - } - - /// Creates an empty `HashSet` with the specified capacity with AHash. - /// - /// The hash set will be able to hold at least `capacity` elements without - /// reallocating. If `capacity` is 0, the hash set will not allocate. - /// - /// # Examples - /// - /// ``` - /// use bevy_utils::{HashSet, AHashExt}; - /// let set: HashSet = HashSet::with_capacity(10); - /// assert!(set.capacity() >= 10); - /// ``` - #[inline] - fn with_capacity(capacity: usize) -> Self { - HashSet::with_capacity_and_hasher(capacity, RandomState::default()) - } -} +/// A stable std hash set implementing AHash, a high speed keyed hashing algorithm +/// intended for use in in-memory hashmaps. +/// +/// Unlike [`HashSet`] this has an iteration order that only depends on the order +/// of insertions and deletions and not a random source. +/// +/// AHash is designed for performance and is NOT cryptographically secure. +pub type StableHashSet = std::collections::HashSet;