mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
parallelize log search
* will consume all cores now and got faster in all my benchmarks * setting progress via asyncjob now makes sure to only set it if it has changed and return whether that is the case to simplify sending progress notifications only in case progress actually changed
This commit is contained in:
parent
5be397b335
commit
ebe41e8a75
6 changed files with 86 additions and 34 deletions
|
|
@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
* fix commit log not updating after branch switch ([#1862](https://github.com/extrawurst/gitui/issues/1862))
|
* fix commit log not updating after branch switch ([#1862](https://github.com/extrawurst/gitui/issues/1862))
|
||||||
* fix stashlist not updating after pop/drop ([#1864](https://github.com/extrawurst/gitui/issues/1864))
|
* fix stashlist not updating after pop/drop ([#1864](https://github.com/extrawurst/gitui/issues/1864))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* log search consumes all cores now and got even faster
|
||||||
|
|
||||||
## [0.24.1] - 2023-08-30
|
## [0.24.1] - 2023-08-30
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
|
||||||
11
Cargo.lock
generated
11
Cargo.lock
generated
|
|
@ -61,6 +61,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"openssl-sys",
|
"openssl-sys",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
|
"rayon",
|
||||||
"rayon-core",
|
"rayon-core",
|
||||||
"scopetime",
|
"scopetime",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
@ -1258,6 +1259,16 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon-core"
|
name = "rayon-core"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
|
|
|
||||||
1
Makefile
1
Makefile
|
|
@ -2,6 +2,7 @@
|
||||||
.PHONY: debug build-release release-linux-musl test clippy clippy-pedantic install install-debug
|
.PHONY: debug build-release release-linux-musl test clippy clippy-pedantic install install-debug
|
||||||
|
|
||||||
ARGS=-l
|
ARGS=-l
|
||||||
|
# ARGS=-l -d ~/code/extern/kubernetes
|
||||||
# ARGS=-l -d ~/code/extern/linux
|
# ARGS=-l -d ~/code/extern/linux
|
||||||
# ARGS=-l -d ~/code/git-bare-test.git -w ~/code/git-bare-test
|
# ARGS=-l -d ~/code/git-bare-test.git -w ~/code/git-bare-test
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ log = "0.4"
|
||||||
# git2 = { git="https://github.com/extrawurst/git2-rs.git", rev="fc13dcc", features = ["vendored-openssl"]}
|
# git2 = { git="https://github.com/extrawurst/git2-rs.git", rev="fc13dcc", features = ["vendored-openssl"]}
|
||||||
# pinning to vendored openssl, using the git2 feature this gets lost with new resolver
|
# pinning to vendored openssl, using the git2 feature this gets lost with new resolver
|
||||||
openssl-sys = { version = '0.9', features = ["vendored"], optional = true }
|
openssl-sys = { version = '0.9', features = ["vendored"], optional = true }
|
||||||
|
rayon = "1.7"
|
||||||
rayon-core = "1.11"
|
rayon-core = "1.11"
|
||||||
scopetime = { path = "../scopetime", version = "0.1" }
|
scopetime = { path = "../scopetime", version = "0.1" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,17 @@ use crossbeam_channel::Sender;
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
/// Passed to `AsyncJob::run` allowing sending intermediate progress notifications
|
/// Passed to `AsyncJob::run` allowing sending intermediate progress notifications
|
||||||
pub struct RunParams<T: Copy + Send, P: Clone + Send + Sync> {
|
pub struct RunParams<
|
||||||
|
T: Copy + Send,
|
||||||
|
P: Clone + Send + Sync + PartialEq,
|
||||||
|
> {
|
||||||
sender: Sender<T>,
|
sender: Sender<T>,
|
||||||
progress: Arc<RwLock<P>>,
|
progress: Arc<RwLock<P>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy + Send, P: Clone + Send + Sync> RunParams<T, P> {
|
impl<T: Copy + Send, P: Clone + Send + Sync + PartialEq>
|
||||||
|
RunParams<T, P>
|
||||||
|
{
|
||||||
/// send an intermediate update notification.
|
/// send an intermediate update notification.
|
||||||
/// do not confuse this with the return value of `run`.
|
/// do not confuse this with the return value of `run`.
|
||||||
/// `send` should only be used about progress notifications
|
/// `send` should only be used about progress notifications
|
||||||
|
|
@ -24,9 +29,13 @@ impl<T: Copy + Send, P: Clone + Send + Sync> RunParams<T, P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// set the current progress
|
/// set the current progress
|
||||||
pub fn set_progress(&self, p: P) -> Result<()> {
|
pub fn set_progress(&self, p: P) -> Result<bool> {
|
||||||
*(self.progress.write()?) = p;
|
Ok(if *self.progress.read()? == p {
|
||||||
Ok(())
|
false
|
||||||
|
} else {
|
||||||
|
*(self.progress.write()?) = p;
|
||||||
|
true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,7 +44,7 @@ pub trait AsyncJob: Send + Sync + Clone {
|
||||||
/// defines what notification type is used to communicate outside
|
/// defines what notification type is used to communicate outside
|
||||||
type Notification: Copy + Send;
|
type Notification: Copy + Send;
|
||||||
/// type of progress
|
/// type of progress
|
||||||
type Progress: Clone + Default + Send + Sync;
|
type Progress: Clone + Default + Send + Sync + PartialEq;
|
||||||
|
|
||||||
/// can run a synchronous time intensive task.
|
/// can run a synchronous time intensive task.
|
||||||
/// the returned notification is used to tell interested parties
|
/// the returned notification is used to tell interested parties
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
use rayon::{
|
||||||
|
prelude::ParallelIterator,
|
||||||
|
slice::{ParallelSlice, ParallelSliceMut},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
asyncjob::{AsyncJob, RunParams},
|
asyncjob::{AsyncJob, RunParams},
|
||||||
error::Result,
|
error::Result,
|
||||||
|
|
@ -5,7 +10,7 @@ use crate::{
|
||||||
AsyncGitNotification, ProgressPercent,
|
AsyncGitNotification, ProgressPercent,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
sync::{Arc, Mutex},
|
sync::{atomic::AtomicUsize, Arc, Mutex},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -69,45 +74,63 @@ impl AsyncCommitFilterJob {
|
||||||
commits: Vec<CommitId>,
|
commits: Vec<CommitId>,
|
||||||
params: &RunParams<AsyncGitNotification, ProgressPercent>,
|
params: &RunParams<AsyncGitNotification, ProgressPercent>,
|
||||||
) -> JobState {
|
) -> JobState {
|
||||||
let response = sync::repo(repo_path)
|
let (start, result) =
|
||||||
.map(|repo| self.filter_commits(&repo, commits, params))
|
self.filter_commits(repo_path, commits, params);
|
||||||
.map(|(start, result)| CommitFilterResult {
|
|
||||||
result,
|
|
||||||
duration: start.elapsed(),
|
|
||||||
});
|
|
||||||
|
|
||||||
JobState::Response(response)
|
//TODO: still need this to be a result?
|
||||||
|
JobState::Response(Ok(CommitFilterResult {
|
||||||
|
result,
|
||||||
|
duration: start.elapsed(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_commits(
|
fn filter_commits(
|
||||||
&self,
|
&self,
|
||||||
repo: &git2::Repository,
|
repo_path: &RepoPath,
|
||||||
commits: Vec<CommitId>,
|
commits: Vec<CommitId>,
|
||||||
params: &RunParams<AsyncGitNotification, ProgressPercent>,
|
params: &RunParams<AsyncGitNotification, ProgressPercent>,
|
||||||
) -> (Instant, Vec<CommitId>) {
|
) -> (Instant, Vec<CommitId>) {
|
||||||
let total_amount = commits.len();
|
let total_amount = commits.len();
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
let mut progress = ProgressPercent::new(0, total_amount);
|
let idx = AtomicUsize::new(0);
|
||||||
|
let mut result = commits
|
||||||
let result = commits
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(idx, c)| {
|
.collect::<Vec<(usize, CommitId)>>()
|
||||||
let new_progress =
|
.par_chunks(1000)
|
||||||
ProgressPercent::new(idx, total_amount);
|
.filter_map(|c| {
|
||||||
|
//TODO: error log repo open errors
|
||||||
|
sync::repo(repo_path).ok().map(|repo| {
|
||||||
|
c.iter()
|
||||||
|
.filter_map(|(e, c)| {
|
||||||
|
let idx = idx.fetch_add(
|
||||||
|
1,
|
||||||
|
std::sync::atomic::Ordering::Relaxed,
|
||||||
|
);
|
||||||
|
|
||||||
if new_progress != progress {
|
Self::update_progress(
|
||||||
Self::update_progress(params, new_progress);
|
params,
|
||||||
progress = new_progress;
|
ProgressPercent::new(
|
||||||
}
|
idx,
|
||||||
|
total_amount,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
(*self.filter)(repo, &c)
|
(*self.filter)(&repo, c).ok().and_then(
|
||||||
.ok()
|
|res| res.then_some((*e, *c)),
|
||||||
.and_then(|res| res.then_some(c))
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
.flatten()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
result.par_sort_by(|a, b| a.0.cmp(&b.0));
|
||||||
|
|
||||||
|
let result = result.into_iter().map(|c| c.1).collect();
|
||||||
|
|
||||||
(start, result)
|
(start, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,12 +138,16 @@ impl AsyncCommitFilterJob {
|
||||||
params: &RunParams<AsyncGitNotification, ProgressPercent>,
|
params: &RunParams<AsyncGitNotification, ProgressPercent>,
|
||||||
new_progress: ProgressPercent,
|
new_progress: ProgressPercent,
|
||||||
) {
|
) {
|
||||||
if let Err(e) = params.set_progress(new_progress) {
|
match params.set_progress(new_progress) {
|
||||||
log::error!("progress error: {e}");
|
Err(e) => log::error!("progress error: {e}"),
|
||||||
} else if let Err(e) =
|
Ok(result) if result => {
|
||||||
params.send(AsyncGitNotification::CommitFilter)
|
if let Err(e) =
|
||||||
{
|
params.send(AsyncGitNotification::CommitFilter)
|
||||||
log::error!("send error: {e}");
|
{
|
||||||
|
log::error!("send error: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue