Skip to content

Commit 0e1d83f

Browse files
authored
Parallelize log search (#1874)
1 parent 5808515 commit 0e1d83f

File tree

5 files changed

+78
-22
lines changed

5 files changed

+78
-22
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
### Changed
11+
* parallelise log search - performance gain ~100% ([#1869](https://github.com/extrawurst/gitui/issues/1869))
12+
1013
## [0.24.2] - 2023-09-03
1114

1215
### Fixes

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

asyncgit/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ log = "0.4"
2222
# git2 = { git="https://github.com/extrawurst/git2-rs.git", rev="fc13dcc", features = ["vendored-openssl"]}
2323
# pinning to vendored openssl, using the git2 feature this gets lost with new resolver
2424
openssl-sys = { version = '0.9', features = ["vendored"], optional = true }
25+
rayon = "1.7"
2526
rayon-core = "1.11"
2627
scopetime = { path = "../scopetime", version = "0.1" }
2728
serde = { version = "1.0", features = ["derive"] }

asyncgit/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ pub enum Error {
8484
///
8585
#[error("not on a branch")]
8686
NoBranch,
87+
88+
///
89+
#[error("rayon error: {0}")]
90+
ThreadPool(#[from] rayon_core::ThreadPoolBuildError),
8791
}
8892

8993
///

asyncgit/src/filter_commits.rs

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
use rayon::{
2+
prelude::ParallelIterator,
3+
slice::{ParallelSlice, ParallelSliceMut},
4+
};
5+
16
use crate::{
27
asyncjob::{AsyncJob, RunParams},
38
error::Result,
49
sync::{self, CommitId, RepoPath, SharedCommitFilterFn},
510
AsyncGitNotification, ProgressPercent,
611
};
712
use std::{
8-
sync::{Arc, Mutex},
13+
sync::{atomic::AtomicUsize, Arc, Mutex},
914
time::{Duration, Instant},
1015
};
1116

@@ -69,41 +74,73 @@ impl AsyncCommitFilterJob {
6974
commits: Vec<CommitId>,
7075
params: &RunParams<AsyncGitNotification, ProgressPercent>,
7176
) -> JobState {
72-
let response = sync::repo(repo_path)
73-
.map(|repo| self.filter_commits(&repo, commits, params))
77+
let result = self
78+
.filter_commits(repo_path, commits, params)
7479
.map(|(start, result)| CommitFilterResult {
7580
result,
7681
duration: start.elapsed(),
7782
});
7883

79-
JobState::Response(response)
84+
JobState::Response(result)
8085
}
8186

8287
fn filter_commits(
8388
&self,
84-
repo: &git2::Repository,
89+
repo_path: &RepoPath,
8590
commits: Vec<CommitId>,
8691
params: &RunParams<AsyncGitNotification, ProgressPercent>,
87-
) -> (Instant, Vec<CommitId>) {
92+
) -> Result<(Instant, Vec<CommitId>)> {
8893
let total_amount = commits.len();
8994
let start = Instant::now();
9095

91-
let result = commits
92-
.into_iter()
93-
.enumerate()
94-
.filter_map(|(idx, c)| {
95-
Self::update_progress(
96-
params,
97-
ProgressPercent::new(idx, total_amount),
98-
);
99-
100-
(*self.filter)(repo, &c)
101-
.ok()
102-
.and_then(|res| res.then_some(c))
103-
})
104-
.collect::<Vec<_>>();
105-
106-
(start, result)
96+
//note: for some reason >4 threads degrades search performance
97+
let pool =
98+
rayon::ThreadPoolBuilder::new().num_threads(4).build()?;
99+
100+
let idx = AtomicUsize::new(0);
101+
102+
let mut result = pool.install(|| {
103+
commits
104+
.into_iter()
105+
.enumerate()
106+
.collect::<Vec<(usize, CommitId)>>()
107+
.par_chunks(1000)
108+
.filter_map(|c| {
109+
//TODO: error log repo open errors
110+
sync::repo(repo_path).ok().map(|repo| {
111+
c.iter()
112+
.filter_map(|(e, c)| {
113+
let idx = idx.fetch_add(
114+
1,
115+
std::sync::atomic::Ordering::Relaxed,
116+
);
117+
118+
Self::update_progress(
119+
params,
120+
ProgressPercent::new(
121+
idx,
122+
total_amount,
123+
),
124+
);
125+
126+
(*self.filter)(&repo, c)
127+
.ok()
128+
.and_then(|res| {
129+
res.then_some((*e, *c))
130+
})
131+
})
132+
.collect::<Vec<_>>()
133+
})
134+
})
135+
.flatten()
136+
.collect::<Vec<_>>()
137+
});
138+
139+
result.par_sort_by(|a, b| a.0.cmp(&b.0));
140+
141+
let result = result.into_iter().map(|c| c.1).collect();
142+
143+
Ok((start, result))
107144
}
108145

109146
fn update_progress(

0 commit comments

Comments
 (0)