Mercurial > public > mercurial-scm > hg
view rust/hg-core/src/operations/dirstate_status.rs @ 47119:15395fd8ab28
dirstate-tree: Use HashMap instead of BTreeMap
BTreeMap has the advantage of its "natural" iteration order being the one we need
in the status algorithm. With HashMap however, iteration order is undefined so
we need to allocate a Vec and sort it explicitly.
Unfortunately many BTreeMap operations are slower than in HashMap, and skipping
that extra allocation and sort is not enough to compensate.
Switching to HashMap + sort makes `hg status` 17% faster in one test case,
as measure with hyperfine:
```
Benchmark #1: ../hg2/hg status -R $REPO --config=experimental.dirstate-tree.in-memory=1
Time (mean ? ?): 765.0 ms ? 8.8 ms [User: 1.352 s, System: 0.747 s]
Range (min ? max): 751.8 ms ? 778.7 ms 10 runs
Benchmark #2: ./hg status -R $REPO --config=experimental.dirstate-tree.in-memory=1
Time (mean ? ?): 651.8 ms ? 9.9 ms [User: 1.251 s, System: 0.799 s]
Range (min ? max): 642.2 ms ? 671.8 ms 10 runs
Summary
'./hg status -R $REPO --config=experimental.dirstate-tree.in-memory=1' ran
1.17 ? 0.02 times faster than '../hg2/hg status -R $REPO --config=experimental.dirstate-tree.in-memory=1'
```
* ./hg is this revision
* ../hg2/hg is its parent
* $REPO is an old snapshot of mozilla-central
Differential Revision: https://phab.mercurial-scm.org/D10553
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Thu, 29 Apr 2021 11:32:57 +0200 |
parents | 9c6b458a08e1 |
children | 73ddcedeaadf |
line wrap: on
line source
// dirstate_status.rs // // Copyright 2019, Raphaël Gomès <rgomes@octobus.net> // // This software may be used and distributed according to the terms of the // GNU General Public License version 2 or any later version. use crate::dirstate::status::{build_response, Dispatch, Status}; use crate::matchers::Matcher; use crate::{DirstateStatus, StatusError}; impl<'a, M: ?Sized + Matcher + Sync> Status<'a, M> { pub(crate) fn run(&self) -> Result<DirstateStatus<'a>, StatusError> { let (traversed_sender, traversed_receiver) = crossbeam_channel::unbounded(); // Step 1: check the files explicitly mentioned by the user let (work, mut results) = self.walk_explicit(traversed_sender.clone()); if !work.is_empty() { // Hashmaps are quite a bit slower to build than vecs, so only // build it if needed. let old_results = results.iter().cloned().collect(); // Step 2: recursively check the working directory for changes if // needed for (dir, dispatch) in work { match dispatch { Dispatch::Directory { was_file } => { if was_file { results.push((dir.to_owned(), Dispatch::Removed)); } if self.options.list_ignored || self.options.list_unknown && !self.dir_ignore(&dir) { self.traverse( &dir, &old_results, &mut results, traversed_sender.clone(), ); } } _ => { unreachable!("There can only be directories in `work`") } } } } if !self.matcher.is_exact() { if self.options.list_unknown { self.handle_unknowns(&mut results); } else { // TODO this is incorrect, see issue6335 // This requires a fix in both Python and Rust that can happen // with other pending changes to `status`. self.extend_from_dmap(&mut results); } } drop(traversed_sender); let traversed = traversed_receiver.into_iter().collect(); Ok(build_response(results, traversed)) } }