Mercurial > public > mercurial-scm > hg-stable
comparison rust/hg-core/src/dirstate_tree/status.rs @ 48750:94e36b230990
status: prefer relative paths in Rust code
? when the repository root is under the current directory,
so the kernel needs to traverse fewer directory in every call
to `read_dir` or `symlink_metadata`.
Better yet would be to use libc functions like `openat` and `fstatat`
to remove such repeated traversals entirely, but the standard library
does not provide APIs based on those.
Maybe with a crate like https://crates.io/crates/openat instead?
Benchmarks of `rhg status` show that this patch is neutral in some configurations,
and makes the command up to ~20% faster in others.
Below is semi-arbitrary subset of results. The four numeric columns are:
time (in seconds) with this changeset?s parent, time with this changeset,
time difference (negative is better), time ratio (less than 1?is better).
```
mercurial-dirstate-v1 | default-plain-clean.no-iu.pbr | 0.0061 -> 0.0059: -0.0002 (0.97)
mercurial-dirstate-v2 | default-plain-clean.no-iu.pbr | 0.0029 -> 0.0028: -0.0001 (0.97)
mozilla-dirstate-v1 | default-plain-clean.no-iu.pbr | 0.2110 -> 0.2102: -0.0007 (1.00)
mozilla-dirstate-v2 | default-copies-clean.ignored.pbr | 0.0489 -> 0.0401: -0.0088 (0.82)
mozilla-dirstate-v2 | default-copies-clean.no-iu.pbr | 0.0479 -> 0.0393: -0.0085 (0.82)
mozilla-dirstate-v2 | default-copies-large.all.pbr | 0.1262 -> 0.1210: -0.0051 (0.96)
mozilla-dirstate-v2 | default-copies-small.ignored-unknown.pbr | 0.1262 -> 0.1200: -0.0062 (0.95)
mozilla-dirstate-v2 | default-copies-small.ignored.pbr | 0.0536 -> 0.0417: -0.0119 (0.78)
mozilla-dirstate-v2 | default-copies-small.no-iu.pbr | 0.0482 -> 0.0393: -0.0089 (0.81)
mozilla-dirstate-v2 | default-plain-clean.ignored.pbr | 0.0518 -> 0.0402: -0.0116 (0.78)
mozilla-dirstate-v2 | default-plain-clean.no-iu.pbr | 0.0481 -> 0.0392: -0.0088 (0.82)
mozilla-dirstate-v2 | default-plain-large.all.pbr | 0.1271 -> 0.1218: -0.0052 (0.96)
mozilla-dirstate-v2 | default-plain-small.ignored-unknown.pbr | 0.1225 -> 0.1202: -0.0022 (0.98)
mozilla-dirstate-v2 | default-plain-small.ignored.pbr | 0.0510 -> 0.0418: -0.0092 (0.82)
mozilla-dirstate-v2 | default-plain-small.no-iu.pbr | 0.0480 -> 0.0394: -0.0086 (0.82)
netbeans-dirstate-v1 | default-plain-clean.no-iu.pbr | 0.1442 -> 0.1422: -0.0020 (0.99)
netbeans-dirstate-v2 | default-plain-clean.no-iu.pbr | 0.0325 -> 0.0282: -0.0043 (0.87)
```
Differential Revision: https://phab.mercurial-scm.org/D12175
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Fri, 21 Jan 2022 17:54:03 +0100 |
parents | 4afb9627dc77 |
children | 834c938227c6 |
comparison
equal
deleted
inserted
replaced
48749:6e930bc45aeb | 48750:94e36b230990 |
---|---|
63 (Box::new(|&_| true), vec![], None) | 63 (Box::new(|&_| true), vec![], None) |
64 }; | 64 }; |
65 | 65 |
66 let filesystem_time_at_status_start = | 66 let filesystem_time_at_status_start = |
67 filesystem_now(&root_dir).ok().map(TruncatedTimestamp::from); | 67 filesystem_now(&root_dir).ok().map(TruncatedTimestamp::from); |
68 | |
69 // If the repository is under the current directory, prefer using a | |
70 // relative path, so the kernel needs to traverse fewer directory in every | |
71 // call to `read_dir` or `symlink_metadata`. | |
72 // This is effective in the common case where the current directory is the | |
73 // repository root. | |
74 | |
75 // TODO: Better yet would be to use libc functions like `openat` and | |
76 // `fstatat` to remove such repeated traversals entirely, but the standard | |
77 // library does not provide APIs based on those. | |
78 // Maybe with a crate like https://crates.io/crates/openat instead? | |
79 let root_dir = if let Some(relative) = std::env::current_dir() | |
80 .ok() | |
81 .and_then(|cwd| root_dir.strip_prefix(cwd).ok()) | |
82 { | |
83 relative | |
84 } else { | |
85 &root_dir | |
86 }; | |
87 | |
68 let outcome = DirstateStatus { | 88 let outcome = DirstateStatus { |
69 filesystem_time_at_status_start, | 89 filesystem_time_at_status_start, |
70 ..Default::default() | 90 ..Default::default() |
71 }; | 91 }; |
72 let common = StatusCommon { | 92 let common = StatusCommon { |
750 /// | 770 /// |
751 /// * At the repository root, ignore that sub-directory | 771 /// * At the repository root, ignore that sub-directory |
752 /// * Elsewhere, we’re listing the content of a sub-repo. Return an empty | 772 /// * Elsewhere, we’re listing the content of a sub-repo. Return an empty |
753 /// list instead. | 773 /// list instead. |
754 fn read_dir(path: &Path, is_at_repo_root: bool) -> io::Result<Vec<Self>> { | 774 fn read_dir(path: &Path, is_at_repo_root: bool) -> io::Result<Vec<Self>> { |
775 // `read_dir` returns a "not found" error for the empty path | |
776 let at_cwd = path == Path::new(""); | |
777 let read_dir_path = if at_cwd { Path::new(".") } else { path }; | |
755 let mut results = Vec::new(); | 778 let mut results = Vec::new(); |
756 for entry in path.read_dir()? { | 779 for entry in read_dir_path.read_dir()? { |
757 let entry = entry?; | 780 let entry = entry?; |
758 let metadata = entry.metadata()?; | 781 let metadata = entry.metadata()?; |
759 let name = get_bytes_from_os_string(entry.file_name()); | 782 let file_name = entry.file_name(); |
760 // FIXME don't do this when cached | 783 // FIXME don't do this when cached |
761 if name == b".hg" { | 784 if file_name == ".hg" { |
762 if is_at_repo_root { | 785 if is_at_repo_root { |
763 // Skip the repo’s own .hg (might be a symlink) | 786 // Skip the repo’s own .hg (might be a symlink) |
764 continue; | 787 continue; |
765 } else if metadata.is_dir() { | 788 } else if metadata.is_dir() { |
766 // A .hg sub-directory at another location means a subrepo, | 789 // A .hg sub-directory at another location means a subrepo, |
767 // skip it entirely. | 790 // skip it entirely. |
768 return Ok(Vec::new()); | 791 return Ok(Vec::new()); |
769 } | 792 } |
770 } | 793 } |
794 let full_path = if at_cwd { | |
795 file_name.clone().into() | |
796 } else { | |
797 entry.path() | |
798 }; | |
799 let base_name = get_bytes_from_os_string(file_name).into(); | |
771 results.push(DirEntry { | 800 results.push(DirEntry { |
772 base_name: name.into(), | 801 base_name, |
773 full_path: entry.path(), | 802 full_path, |
774 metadata, | 803 metadata, |
775 }) | 804 }) |
776 } | 805 } |
777 Ok(results) | 806 Ok(results) |
778 } | 807 } |