perf: use rustc-hash for HashMap and HashSet

Since we already depend on `rustc-hash` transiently, this doesn't add
any more dependencies. As long as DOS attacks aren't a concern (which I
don't think they are?), this should be free performance.

In my (admittedly naive) testing, this really improved CPU usage in some
cases, which is pretty nice to get for free.
This commit is contained in:
Cheong Lau 2025-10-19 08:46:12 +10:00
parent 4be92ae8ca
commit 43a9fca4ec
11 changed files with 68 additions and 68 deletions

View file

@ -47,10 +47,11 @@ use notify_debouncer_full::{
DebouncedEvent, Debouncer, RecommendedCache, new_debouncer,
notify::{self, RecommendedWatcher},
};
use rustc_hash::{FxHashMap, FxHashSet};
use slotmap::Key as SlotMapKey;
use std::{
any::TypeId,
collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque},
collections::{BTreeMap, BTreeSet, HashMap, VecDeque},
env, fmt, fs,
future::Future,
io,
@ -67,6 +68,7 @@ use trash::TrashItem;
use wayland_client::{Proxy, protocol::wl_output::WlOutput};
use crate::{
FxOrderMap,
clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste},
config::{
AppTheme, Config, DesktopConfig, Favorite, IconSizes, TIME_CONFIG_ID, TabConfig,
@ -654,16 +656,16 @@ pub struct App {
dialog_pages: DialogPages,
dialog_text_input: widget::Id,
key_binds: HashMap<KeyBind, Action>,
margin: HashMap<window::Id, (f32, f32, f32, f32)>,
margin: FxHashMap<window::Id, (f32, f32, f32, f32)>,
mime_app_cache: MimeAppCache,
modifiers: Modifiers,
mounter_items: HashMap<MounterKey, MounterItems>,
mounter_items: FxHashMap<MounterKey, MounterItems>,
must_save_sort_names: bool,
network_drive_connecting: Option<(MounterKey, String)>,
network_drive_input: String,
#[cfg(feature = "notify")]
notification_opt: Option<Arc<Mutex<notify_rust::NotificationHandle>>>,
overlap: HashMap<String, (window::Id, Rectangle)>,
overlap: FxHashMap<String, (window::Id, Rectangle)>,
pending_operation_id: u64,
pending_operations: BTreeMap<u64, (Operation, Controller)>,
progress_operations: BTreeSet<u64>,
@ -673,17 +675,17 @@ pub struct App {
search_id: widget::Id,
size: Option<Size>,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
layer_sizes: HashMap<window::Id, Size>,
layer_sizes: FxHashMap<window::Id, Size>,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
surface_ids: HashMap<WlOutput, WindowId>,
surface_ids: FxHashMap<WlOutput, WindowId>,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
surface_names: HashMap<WindowId, String>,
surface_names: FxHashMap<WindowId, String>,
toasts: widget::toaster::Toasts<Message>,
watcher_opt: Option<(
Debouncer<RecommendedWatcher, RecommendedCache>,
HashSet<PathBuf>,
FxHashSet<PathBuf>,
)>,
windows: HashMap<window::Id, WindowKind>,
windows: FxHashMap<window::Id, WindowKind>,
nav_dnd_hover: Option<(Location, Instant)>,
tab_dnd_hover: Option<(Entity, Instant)>,
nav_drag_id: DragId,
@ -699,7 +701,7 @@ impl App {
// Associate all paths to its MIME type
// This allows handling paths as groups if possible, such as launching a single video
// player that is passed every path.
let mut groups: HashMap<Mime, Vec<PathBuf>> = HashMap::new();
let mut groups: FxHashMap<Mime, Vec<PathBuf>> = FxHashMap::default();
let mut all_archives = true;
let supported_archive_types = crate::archive::SUPPORTED_ARCHIVE_TYPES
.iter()
@ -915,7 +917,7 @@ impl App {
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
fn handle_overlap(&mut self) {
let mut overlaps: HashMap<_, _> = self
let mut overlaps: FxHashMap<_, _> = self
.windows
.keys()
.map(|k| (*k, (0., 0., 0., 0.)))
@ -1573,7 +1575,7 @@ impl App {
fn update_watcher(&mut self) -> Task<Message> {
if let Some((mut watcher, old_paths)) = self.watcher_opt.take() {
let mut new_paths = HashSet::new();
let mut new_paths = FxHashSet::default();
for entity in self.tab_model.iter() {
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
if let Some(path) = tab.location.path_opt() {
@ -1942,7 +1944,7 @@ impl App {
fn get_apps_for_mime(&self, mime_type: &Mime) -> Vec<(&MimeApp, MimeAppMatch)> {
let mut results = Vec::new();
let mut dedupe = HashSet::new();
let mut dedupe = FxHashSet::default();
// start with exact matches
for mime_app in self.mime_app_cache.get(mime_type) {
@ -2124,16 +2126,16 @@ impl Application for App {
dialog_pages: DialogPages::new(),
dialog_text_input: widget::Id::unique(),
key_binds,
margin: HashMap::new(),
margin: FxHashMap::default(),
mime_app_cache: MimeAppCache::new(),
modifiers: Modifiers::empty(),
mounter_items: HashMap::new(),
mounter_items: FxHashMap::default(),
must_save_sort_names: false,
network_drive_connecting: None,
network_drive_input: String::new(),
#[cfg(feature = "notify")]
notification_opt: None,
overlap: HashMap::new(),
overlap: FxHashMap::default(),
pending_operation_id: 0,
pending_operations: BTreeMap::new(),
progress_operations: BTreeSet::new(),
@ -2143,12 +2145,12 @@ impl Application for App {
search_id: widget::Id::unique(),
size: None,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
surface_ids: HashMap::new(),
surface_ids: FxHashMap::default(),
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
surface_names: HashMap::new(),
surface_names: FxHashMap::default(),
toasts: widget::toaster::Toasts::new(Message::CloseToast),
watcher_opt: None,
windows: HashMap::new(),
windows: FxHashMap::default(),
nav_dnd_hover: None,
tab_dnd_hover: None,
nav_drag_id: DragId::new(),
@ -2156,7 +2158,7 @@ impl Application for App {
auto_scroll_speed: None,
file_dialog_opt: None,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
layer_sizes: HashMap::new(),
layer_sizes: FxHashMap::default(),
};
let mut commands = vec![app.update_config()];
@ -3211,7 +3213,7 @@ impl Application for App {
Message::NotifyWatcher(mut watcher_wrapper) => match watcher_wrapper.watcher_opt.take()
{
Some(watcher) => {
self.watcher_opt = Some((watcher, HashSet::new()));
self.watcher_opt = Some((watcher, FxHashSet::default()));
return self.update_watcher();
}
None => {
@ -4678,8 +4680,8 @@ impl Application for App {
Message::SaveSortNames => {
self.must_save_sort_names = false;
if let Some(state_handler) = self.state_handler.as_ref() {
if let Err(err) =
state_handler.set::<ordermap::OrderMap<String, (HeadingOptions, bool)>>(
if let Err(err) = state_handler
.set::<FxOrderMap<String, (HeadingOptions, bool)>>(
"sort_names",
self.state.sort_names.clone(),
)