mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Use faster hashing algorithm in sorting code
The default std hasher is DoS-resistant but relatively slow, and DoS-resistance isn't a useful property during sorting. I tested using the rustc-hash, fnv and ahash crates against my 1600-plugin load order, and the results vs the default std hasher were: - AHash: 31% faster - FNV: 33% faster - FxHasher (from rustc-hash): 37% faster With the std hasher the Rust libloot is 35% slower than C++, with FxHasher it's 15% faster.
This commit is contained in:
Generated
+7
@@ -586,6 +586,7 @@ dependencies = [
|
||||
"rayon",
|
||||
"rstest",
|
||||
"rstest_reuse",
|
||||
"rustc-hash",
|
||||
"saphyr",
|
||||
"saphyr-parser",
|
||||
"tempfile",
|
||||
@@ -1076,6 +1077,12 @@ dependencies = [
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
|
||||
@@ -16,6 +16,7 @@ saphyr = "0.0.3"
|
||||
saphyr-parser = "0.0.3"
|
||||
unicase = "2.8.1"
|
||||
rayon = "1.10.0"
|
||||
rustc-hash = "2.1.1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = { version = "0.60.0", features = ["Win32_Storage_FileSystem"] }
|
||||
|
||||
+7
-4
@@ -1,10 +1,11 @@
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use petgraph::{
|
||||
Graph,
|
||||
graph::{EdgeReference, NodeIndex},
|
||||
visit::EdgeRef,
|
||||
};
|
||||
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
|
||||
|
||||
use crate::{EdgeType, Vertex, logging};
|
||||
|
||||
@@ -36,8 +37,10 @@ pub fn bidirectional_bfs<N, E>(
|
||||
) -> bool {
|
||||
let mut forward_queue = VecDeque::from([from_index]);
|
||||
let mut reverse_queue = VecDeque::from([to_index]);
|
||||
let mut forward_visited = HashSet::from([from_index]);
|
||||
let mut reverse_visited = HashSet::from([to_index]);
|
||||
let mut forward_visited = HashSet::default();
|
||||
forward_visited.insert(from_index);
|
||||
let mut reverse_visited = HashSet::default();
|
||||
reverse_visited.insert(to_index);
|
||||
|
||||
while let (Some(forward_current), Some(reverse_current)) =
|
||||
(forward_queue.pop_front(), reverse_queue.pop_front())
|
||||
@@ -81,7 +84,7 @@ pub fn find_cycle<N>(
|
||||
) -> Option<Vec<Vertex>> {
|
||||
let mut cycle_detector = CycleDetector::new(graph, node_mapper);
|
||||
|
||||
let mut colour_map = HashMap::new();
|
||||
let mut colour_map = HashMap::default();
|
||||
|
||||
for node_index in graph.node_indices() {
|
||||
depth_first_search(graph, &mut colour_map, node_index, &mut cycle_detector);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use std::{cmp::Reverse, collections::HashMap};
|
||||
use std::cmp::Reverse;
|
||||
|
||||
use rustc_hash::FxHashMap as HashMap;
|
||||
|
||||
use petgraph::{Graph, algo::bellman_ford, graph::NodeIndex};
|
||||
|
||||
@@ -29,7 +31,7 @@ pub fn build_groups_graph(
|
||||
let userlist_groups = sorted_by_name(userlist_groups);
|
||||
|
||||
let mut graph = GroupsGraph::new();
|
||||
let mut group_nodes: HashMap<&str, NodeIndex> = HashMap::new();
|
||||
let mut group_nodes: HashMap<&str, NodeIndex> = HashMap::default();
|
||||
|
||||
logging::trace!("Adding masterlist groups to groups graph...");
|
||||
add_groups(
|
||||
@@ -206,7 +208,7 @@ pub fn sorted_group_nodes(graph: &GroupsGraph) -> Vec<NodeIndex> {
|
||||
if is_root_node(graph, n) {
|
||||
let mut visitor = GroupsPathLengthVisitor::new();
|
||||
|
||||
depth_first_search(graph, &mut HashMap::new(), n, &mut visitor);
|
||||
depth_first_search(graph, &mut HashMap::default(), n, &mut visitor);
|
||||
|
||||
(n, true, visitor.max_path_length())
|
||||
} else {
|
||||
|
||||
+15
-12
@@ -1,13 +1,11 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
rc::Rc,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
|
||||
use petgraph::{
|
||||
Graph,
|
||||
graph::{EdgeReference, NodeIndex},
|
||||
visit::EdgeRef,
|
||||
};
|
||||
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
|
||||
|
||||
use crate::{
|
||||
EdgeType, LogLevel, Plugin,
|
||||
@@ -308,7 +306,7 @@ impl<'a, T: SortingPlugin> PluginsGraph<'a, T> {
|
||||
|
||||
// Keep a record of which vertices have already been fully explored to avoid
|
||||
// adding edges from their plugins more than once.
|
||||
let mut finished_nodes = HashSet::new();
|
||||
let mut finished_nodes = HashSet::default();
|
||||
// Now loop over the vertices in the groups graph.
|
||||
// The vertex sort order prioritises resolving potential cycles in
|
||||
// favour of earlier-loading groups. It does not guarantee that the
|
||||
@@ -330,7 +328,12 @@ impl<'a, T: SortingPlugin> PluginsGraph<'a, T> {
|
||||
Some(default_group_node),
|
||||
);
|
||||
|
||||
depth_first_search(groups_graph, &mut HashMap::new(), group_node, &mut visitor);
|
||||
depth_first_search(
|
||||
groups_graph,
|
||||
&mut HashMap::default(),
|
||||
group_node,
|
||||
&mut visitor,
|
||||
);
|
||||
}
|
||||
|
||||
// Now do one last DFS starting from the default group and not ignoring its
|
||||
@@ -345,7 +348,7 @@ impl<'a, T: SortingPlugin> PluginsGraph<'a, T> {
|
||||
|
||||
depth_first_search(
|
||||
groups_graph,
|
||||
&mut HashMap::new(),
|
||||
&mut HashMap::default(),
|
||||
default_group_node,
|
||||
&mut visitor,
|
||||
);
|
||||
@@ -496,7 +499,7 @@ impl<'a, T: SortingPlugin> PluginsGraph<'a, T> {
|
||||
let mut new_load_order: Vec<NodeIndex> = Vec::new();
|
||||
|
||||
// Holds nodes that have already been put into new_load_order.
|
||||
let mut processed_nodes = HashSet::new();
|
||||
let mut processed_nodes = HashSet::default();
|
||||
|
||||
// First get the graph vertices and sort them into the current load order.
|
||||
let mut nodes: Vec<_> = self.node_indices().collect();
|
||||
@@ -897,8 +900,8 @@ impl<'a, 'b, T: SortingPlugin> PathFinder<'a, 'b, T> {
|
||||
cache,
|
||||
from_node_index,
|
||||
to_node_index,
|
||||
forward_parents: HashMap::new(),
|
||||
reverse_children: HashMap::new(),
|
||||
forward_parents: HashMap::default(),
|
||||
reverse_children: HashMap::default(),
|
||||
intersection_node: None,
|
||||
}
|
||||
}
|
||||
@@ -985,7 +988,7 @@ struct PathCacher<'a> {
|
||||
fn get_plugins_in_groups<T: SortingPlugin>(
|
||||
graph: &InnerPluginsGraph<T>,
|
||||
) -> HashMap<String, Vec<NodeIndex>> {
|
||||
let mut plugins_in_groups: HashMap<String, Vec<NodeIndex>> = HashMap::new();
|
||||
let mut plugins_in_groups: HashMap<String, Vec<NodeIndex>> = HashMap::default();
|
||||
|
||||
for node in graph.node_indices() {
|
||||
let group_name = graph[node].group.clone();
|
||||
@@ -1066,7 +1069,7 @@ impl<'a, 'b, 'c, 'd, 'e, T: SortingPlugin> GroupsPathVisitor<'a, 'b, 'c, 'd, 'e,
|
||||
finished_group_vertices,
|
||||
group_node_to_ignore_as_source,
|
||||
edge_stack: Vec::new(),
|
||||
unfinishable_nodes: HashSet::new(),
|
||||
unfinishable_nodes: HashSet::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user