Use `FxHashMap` and `FxHashSet` in hot code paths

Antonio Scandurra created

We can also use these maps and sets in place of `SeaHasher` because
they are also deterministic. Note that we're not swapping std's
`HashMap` and `HashSet` wholesale inside of `collections` because
on the server we need cryptographically secure collections.

Change summary

Cargo.lock                                   |  2 
crates/collections/Cargo.toml                |  4 +-
crates/collections/src/collections.rs        | 18 ++------------
crates/gpui2/src/app.rs                      | 26 +++++++++++-----------
crates/gpui2/src/platform/mac/metal_atlas.rs |  4 +-
crates/gpui2/src/taffy.rs                    | 18 +++++++-------
crates/gpui2/src/text_system.rs              | 10 ++++----
crates/gpui2/src/text_system/line_layout.rs  | 10 ++++----
8 files changed, 40 insertions(+), 52 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1990,7 +1990,7 @@ dependencies = [
 name = "collections"
 version = "0.1.0"
 dependencies = [
- "seahash",
+ "rustc-hash",
 ]
 
 [[package]]

crates/collections/Cargo.toml 🔗

@@ -9,7 +9,7 @@ path = "src/collections.rs"
 doctest = false
 
 [features]
-test-support = ["seahash"]
+test-support = []
 
 [dependencies]
-seahash = { version = "4.1", optional = true }
+rustc-hash = "1.1"

crates/collections/src/collections.rs 🔗

@@ -1,21 +1,8 @@
 #[cfg(feature = "test-support")]
-#[derive(Clone, Default)]
-pub struct DeterministicState;
+pub type HashMap<K, V> = FxHashMap<K, V>;
 
 #[cfg(feature = "test-support")]
-impl std::hash::BuildHasher for DeterministicState {
-    type Hasher = seahash::SeaHasher;
-
-    fn build_hasher(&self) -> Self::Hasher {
-        seahash::SeaHasher::new()
-    }
-}
-
-#[cfg(feature = "test-support")]
-pub type HashMap<K, V> = std::collections::HashMap<K, V, DeterministicState>;
-
-#[cfg(feature = "test-support")]
-pub type HashSet<T> = std::collections::HashSet<T, DeterministicState>;
+pub type HashSet<T> = FxHashSet<T>;
 
 #[cfg(not(feature = "test-support"))]
 pub type HashMap<K, V> = std::collections::HashMap<K, V>;
@@ -23,6 +10,7 @@ pub type HashMap<K, V> = std::collections::HashMap<K, V>;
 #[cfg(not(feature = "test-support"))]
 pub type HashSet<T> = std::collections::HashSet<T>;
 
+pub use rustc_hash::{FxHashMap, FxHashSet};
 use std::any::TypeId;
 pub use std::collections::*;
 

crates/gpui2/src/app.rs 🔗

@@ -24,7 +24,7 @@ use crate::{
     WindowContext, WindowHandle, WindowId,
 };
 use anyhow::{anyhow, Result};
-use collections::{HashMap, HashSet, VecDeque};
+use collections::{FxHashMap, FxHashSet, VecDeque};
 use futures::{channel::oneshot, future::LocalBoxFuture, Future};
 use parking_lot::Mutex;
 use slotmap::SlotMap;
@@ -191,24 +191,24 @@ pub struct AppContext {
     pub(crate) actions: Rc<ActionRegistry>,
     pub(crate) active_drag: Option<AnyDrag>,
     pub(crate) active_tooltip: Option<AnyTooltip>,
-    pub(crate) next_frame_callbacks: HashMap<DisplayId, Vec<FrameCallback>>,
-    pub(crate) frame_consumers: HashMap<DisplayId, Task<()>>,
+    pub(crate) next_frame_callbacks: FxHashMap<DisplayId, Vec<FrameCallback>>,
+    pub(crate) frame_consumers: FxHashMap<DisplayId, Task<()>>,
     pub(crate) background_executor: BackgroundExecutor,
     pub(crate) foreground_executor: ForegroundExecutor,
     pub(crate) svg_renderer: SvgRenderer,
     asset_source: Arc<dyn AssetSource>,
     pub(crate) image_cache: ImageCache,
     pub(crate) text_style_stack: Vec<TextStyleRefinement>,
-    pub(crate) globals_by_type: HashMap<TypeId, Box<dyn Any>>,
+    pub(crate) globals_by_type: FxHashMap<TypeId, Box<dyn Any>>,
     pub(crate) entities: EntityMap,
     pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
     pub(crate) windows: SlotMap<WindowId, Option<Window>>,
     pub(crate) keymap: Arc<Mutex<Keymap>>,
     pub(crate) global_action_listeners:
-        HashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
+        FxHashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
     pending_effects: VecDeque<Effect>,
-    pub(crate) pending_notifications: HashSet<EntityId>,
-    pub(crate) pending_global_notifications: HashSet<TypeId>,
+    pub(crate) pending_notifications: FxHashSet<EntityId>,
+    pub(crate) pending_global_notifications: FxHashSet<TypeId>,
     pub(crate) observers: SubscriberSet<EntityId, Handler>,
     // TypeId is the type of the event that the listener callback expects
     pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,
@@ -253,23 +253,23 @@ impl AppContext {
                 pending_updates: 0,
                 active_drag: None,
                 active_tooltip: None,
-                next_frame_callbacks: HashMap::default(),
-                frame_consumers: HashMap::default(),
+                next_frame_callbacks: FxHashMap::default(),
+                frame_consumers: FxHashMap::default(),
                 background_executor: executor,
                 foreground_executor,
                 svg_renderer: SvgRenderer::new(asset_source.clone()),
                 asset_source,
                 image_cache: ImageCache::new(http_client),
                 text_style_stack: Vec::new(),
-                globals_by_type: HashMap::default(),
+                globals_by_type: FxHashMap::default(),
                 entities,
                 new_view_observers: SubscriberSet::new(),
                 windows: SlotMap::with_key(),
                 keymap: Arc::new(Mutex::new(Keymap::default())),
-                global_action_listeners: HashMap::default(),
+                global_action_listeners: FxHashMap::default(),
                 pending_effects: VecDeque::new(),
-                pending_notifications: HashSet::default(),
-                pending_global_notifications: HashSet::default(),
+                pending_notifications: FxHashSet::default(),
+                pending_global_notifications: FxHashSet::default(),
                 observers: SubscriberSet::new(),
                 event_listeners: SubscriberSet::new(),
                 release_listeners: SubscriberSet::new(),

crates/gpui2/src/platform/mac/metal_atlas.rs 🔗

@@ -3,7 +3,7 @@ use crate::{
     Point, Size,
 };
 use anyhow::Result;
-use collections::HashMap;
+use collections::FxHashMap;
 use derive_more::{Deref, DerefMut};
 use etagere::BucketedAtlasAllocator;
 use metal::Device;
@@ -53,7 +53,7 @@ struct MetalAtlasState {
     monochrome_textures: Vec<MetalAtlasTexture>,
     polychrome_textures: Vec<MetalAtlasTexture>,
     path_textures: Vec<MetalAtlasTexture>,
-    tiles_by_key: HashMap<AtlasKey, AtlasTile>,
+    tiles_by_key: FxHashMap<AtlasKey, AtlasTile>,
 }
 
 impl PlatformAtlas for MetalAtlas {

crates/gpui2/src/taffy.rs 🔗

@@ -2,7 +2,7 @@ use crate::{
     AbsoluteLength, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style,
     WindowContext,
 };
-use collections::{HashMap, HashSet};
+use collections::{FxHashMap, FxHashSet};
 use smallvec::SmallVec;
 use std::fmt::Debug;
 use taffy::{
@@ -14,10 +14,10 @@ use taffy::{
 
 pub struct TaffyLayoutEngine {
     taffy: Taffy,
-    children_to_parents: HashMap<LayoutId, LayoutId>,
-    absolute_layout_bounds: HashMap<LayoutId, Bounds<Pixels>>,
-    computed_layouts: HashSet<LayoutId>,
-    nodes_to_measure: HashMap<
+    children_to_parents: FxHashMap<LayoutId, LayoutId>,
+    absolute_layout_bounds: FxHashMap<LayoutId, Bounds<Pixels>>,
+    computed_layouts: FxHashSet<LayoutId>,
+    nodes_to_measure: FxHashMap<
         LayoutId,
         Box<
             dyn FnMut(
@@ -36,10 +36,10 @@ impl TaffyLayoutEngine {
     pub fn new() -> Self {
         TaffyLayoutEngine {
             taffy: Taffy::new(),
-            children_to_parents: HashMap::default(),
-            absolute_layout_bounds: HashMap::default(),
-            computed_layouts: HashSet::default(),
-            nodes_to_measure: HashMap::default(),
+            children_to_parents: FxHashMap::default(),
+            absolute_layout_bounds: FxHashMap::default(),
+            computed_layouts: FxHashSet::default(),
+            nodes_to_measure: FxHashMap::default(),
         }
     }
 

crates/gpui2/src/text_system.rs 🔗

@@ -13,7 +13,7 @@ use crate::{
     UnderlineStyle,
 };
 use anyhow::anyhow;
-use collections::HashMap;
+use collections::FxHashMap;
 use core::fmt;
 use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
 use smallvec::SmallVec;
@@ -37,10 +37,10 @@ pub const SUBPIXEL_VARIANTS: u8 = 4;
 pub struct TextSystem {
     line_layout_cache: Arc<LineLayoutCache>,
     platform_text_system: Arc<dyn PlatformTextSystem>,
-    font_ids_by_font: RwLock<HashMap<Font, FontId>>,
-    font_metrics: RwLock<HashMap<FontId, FontMetrics>>,
-    raster_bounds: RwLock<HashMap<RenderGlyphParams, Bounds<DevicePixels>>>,
-    wrapper_pool: Mutex<HashMap<FontIdWithSize, Vec<LineWrapper>>>,
+    font_ids_by_font: RwLock<FxHashMap<Font, FontId>>,
+    font_metrics: RwLock<FxHashMap<FontId, FontMetrics>>,
+    raster_bounds: RwLock<FxHashMap<RenderGlyphParams, Bounds<DevicePixels>>>,
+    wrapper_pool: Mutex<FxHashMap<FontIdWithSize, Vec<LineWrapper>>>,
     font_runs_pool: Mutex<Vec<Vec<FontRun>>>,
 }
 

crates/gpui2/src/text_system/line_layout.rs 🔗

@@ -1,9 +1,9 @@
 use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
+use collections::FxHashMap;
 use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
 use smallvec::SmallVec;
 use std::{
     borrow::Borrow,
-    collections::HashMap,
     hash::{Hash, Hasher},
     sync::Arc,
 };
@@ -236,10 +236,10 @@ impl WrappedLineLayout {
 }
 
 pub(crate) struct LineLayoutCache {
-    previous_frame: Mutex<HashMap<CacheKey, Arc<LineLayout>>>,
-    current_frame: RwLock<HashMap<CacheKey, Arc<LineLayout>>>,
-    previous_frame_wrapped: Mutex<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
-    current_frame_wrapped: RwLock<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
+    previous_frame: Mutex<FxHashMap<CacheKey, Arc<LineLayout>>>,
+    current_frame: RwLock<FxHashMap<CacheKey, Arc<LineLayout>>>,
+    previous_frame_wrapped: Mutex<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
+    current_frame_wrapped: RwLock<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
     platform_text_system: Arc<dyn PlatformTextSystem>,
 }