Faster compilation of bevy_diagnostic (#1235)

* 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
```
This commit is contained in:
bjorn3 2021-01-12 22:21:45 +01:00 committed by GitHub
parent c434f57de1
commit fbf08ac545
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 42 additions and 92 deletions

View file

@ -1,5 +1,5 @@
use bevy_utils::{Duration, HashMap, Instant, Uuid}; use bevy_utils::{Duration, Instant, StableHashMap, Uuid};
use std::collections::{BTreeSet, VecDeque}; use std::collections::VecDeque;
/// Unique identifier for a [Diagnostic] /// Unique identifier for a [Diagnostic]
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
@ -101,13 +101,13 @@ impl Diagnostic {
/// A collection of [Diagnostic]s /// A collection of [Diagnostic]s
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Diagnostics { pub struct Diagnostics {
diagnostics: HashMap<DiagnosticId, Diagnostic>, // This uses a [`StableHashMap`] to ensure that the iteration order is deterministic between
ordered_diagnostics: BTreeSet<DiagnosticId>, // runs when all diagnostics are inserted in the same order.
diagnostics: StableHashMap<DiagnosticId, Diagnostic>,
} }
impl Diagnostics { impl Diagnostics {
pub fn add(&mut self, diagnostic: Diagnostic) { pub fn add(&mut self, diagnostic: Diagnostic) {
self.ordered_diagnostics.insert(diagnostic.id);
self.diagnostics.insert(diagnostic.id, diagnostic); self.diagnostics.insert(diagnostic.id, diagnostic);
} }
@ -134,10 +134,4 @@ impl Diagnostics {
pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> { pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
self.diagnostics.values() self.diagnostics.values()
} }
pub fn ordered_iter(&self) -> impl Iterator<Item = &Diagnostic> {
self.ordered_diagnostics
.iter()
.filter_map(move |k| self.diagnostics.get(k))
}
} }

View file

@ -78,7 +78,7 @@ impl LogDiagnosticsPlugin {
Self::log_diagnostic(diagnostic); Self::log_diagnostic(diagnostic);
} }
} else { } else {
for diagnostic in diagnostics.ordered_iter() { for diagnostic in diagnostics.iter() {
Self::log_diagnostic(diagnostic); Self::log_diagnostic(diagnostic);
} }
} }
@ -96,7 +96,7 @@ impl LogDiagnosticsPlugin {
debug!("{:#?}\n", diagnostic); debug!("{:#?}\n", diagnostic);
} }
} else { } else {
for diagnostic in diagnostics.ordered_iter() { for diagnostic in diagnostics.iter() {
debug!("{:#?}\n", diagnostic); debug!("{:#?}\n", diagnostic);
} }
} }

View file

@ -1,4 +1,4 @@
use bevy_utils::{AHashExt, HashMap}; use bevy_utils::HashMap;
use std::hash::Hash; use std::hash::Hash;
#[derive(Debug)] #[derive(Debug)]
@ -12,7 +12,7 @@ where
{ {
fn default() -> Self { fn default() -> Self {
Axis { Axis {
axis_data: HashMap::new(), axis_data: HashMap::default(),
} }
} }
} }

View file

@ -6,7 +6,7 @@ use crate::{
use bevy_app::prelude::{EventReader, Events}; use bevy_app::prelude::{EventReader, Events};
use bevy_asset::{AssetEvent, Assets}; use bevy_asset::{AssetEvent, Assets};
use bevy_ecs::{Resources, World}; use bevy_ecs::{Resources, World};
use bevy_utils::{AHashExt, HashSet}; use bevy_utils::HashSet;
#[derive(Default)] #[derive(Default)]
pub struct TextureCopyNode { pub struct TextureCopyNode {
@ -24,7 +24,7 @@ impl Node for TextureCopyNode {
) { ) {
let texture_events = resources.get::<Events<AssetEvent<Texture>>>().unwrap(); let texture_events = resources.get::<Events<AssetEvent<Texture>>>().unwrap();
let textures = resources.get::<Assets<Texture>>().unwrap(); let textures = resources.get::<Assets<Texture>>().unwrap();
let mut copied_textures = HashSet::new(); let mut copied_textures = HashSet::default();
for event in self.texture_event_reader.iter(&texture_events) { for event in self.texture_event_reader.iter(&texture_events) {
match event { match event {
AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {

View file

@ -6,7 +6,7 @@ use bevy_math::Vec2;
use bevy_reflect::TypeUuid; use bevy_reflect::TypeUuid;
use bevy_render::texture::Texture; use bevy_render::texture::Texture;
use bevy_sprite::TextureAtlas; use bevy_sprite::TextureAtlas;
use bevy_utils::{AHashExt, HashMap}; use bevy_utils::HashMap;
type FontSizeKey = FloatOrd; type FontSizeKey = FloatOrd;
@ -25,7 +25,7 @@ pub struct GlyphAtlasInfo {
impl Default for FontAtlasSet { impl Default for FontAtlasSet {
fn default() -> Self { fn default() -> Self {
FontAtlasSet { FontAtlasSet {
font_atlases: HashMap::with_capacity(1), font_atlases: HashMap::with_capacity_and_hasher(1, Default::default()),
} }
} }
} }

View file

@ -11,51 +11,33 @@ pub type BoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub type BoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>; pub type BoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + '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 /// A std hash map implementing AHash, a high speed keyed hashing algorithm
/// intended for use in in-memory hashmaps. /// intended for use in in-memory hashmaps.
/// ///
/// AHash is designed for performance and is NOT cryptographically secure. /// AHash is designed for performance and is NOT cryptographically secure.
pub type HashMap<K, V> = std::collections::HashMap<K, V, RandomState>; pub type HashMap<K, V> = std::collections::HashMap<K, V, RandomState>;
pub trait AHashExt { /// A stable std hash map implementing AHash, a high speed keyed hashing algorithm
fn new() -> Self; /// intended for use in in-memory hashmaps.
///
fn with_capacity(capacity: usize) -> Self; /// Unlike [`HashMap`] this has an iteration order that only depends on the order
} /// of insertions and deletions and not a random source.
///
impl<K, V> AHashExt for HashMap<K, V> { /// AHash is designed for performance and is NOT cryptographically secure.
/// Creates an empty `HashMap` with AHash. pub type StableHashMap<K, V> = std::collections::HashMap<K, V, FixedState>;
///
/// 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 std hash set implementing AHash, a high speed keyed hashing algorithm /// A std hash set implementing AHash, a high speed keyed hashing algorithm
/// intended for use in in-memory hashmaps. /// intended for use in in-memory hashmaps.
@ -63,37 +45,11 @@ impl<K, V> AHashExt for HashMap<K, V> {
/// AHash is designed for performance and is NOT cryptographically secure. /// AHash is designed for performance and is NOT cryptographically secure.
pub type HashSet<K> = std::collections::HashSet<K, RandomState>; pub type HashSet<K> = std::collections::HashSet<K, RandomState>;
impl<K> AHashExt for HashSet<K> { /// A stable std hash set implementing AHash, a high speed keyed hashing algorithm
/// Creates an empty `HashSet` with AHash. /// intended for use in in-memory hashmaps.
/// ///
/// The hash set is initially created with a capacity of 0, so it will not /// Unlike [`HashSet`] this has an iteration order that only depends on the order
/// allocate until it is first inserted into. /// of insertions and deletions and not a random source.
/// ///
/// # Examples /// AHash is designed for performance and is NOT cryptographically secure.
/// pub type StableHashSet<K> = std::collections::HashSet<K, FixedState>;
/// ```
/// use bevy_utils::{HashSet, AHashExt};
/// let set: HashSet<i32> = 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<i32> = HashSet::with_capacity(10);
/// assert!(set.capacity() >= 10);
/// ```
#[inline]
fn with_capacity(capacity: usize) -> Self {
HashSet::with_capacity_and_hasher(capacity, RandomState::default())
}
}