Skip to content

Commit b65f15b

Browse files
committed
gix-blame: implement benchmarks for incremental blame
Compare the performance of the implementation with and without the commit graph cache. gix-blame::incremental/without-commit-graph time: [14.852 s 14.895 s 14.944 s] change: [+0.2968% +0.7623% +1.2529%] (p = 0.00 < 0.05) Change within noise threshold. gix-blame::incremental/with-commit-graph time: [287.55 ms 290.30 ms 292.85 ms] change: [−3.1181% −1.6720% −0.4502%] (p = 0.11 > 0.05) No change in performance detected. Signed-off-by: Vicent Marti <vmg@strn.cat>
1 parent 3e1d8d6 commit b65f15b

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

Cargo.lock

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

gix-blame/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ authors = ["Christoph Rüßler <christoph.ruessler@mailbox.org>", "Sebastian Thi
1010
edition = "2021"
1111
rust-version = "1.82"
1212

13+
[[bench]]
14+
name = "incremental-blame"
15+
harness = false
16+
path = "./benches/incremental_blame.rs"
17+
1318
[features]
1419
## Enable support for the SHA-1 hash by forwarding the feature to dependencies.
1520
sha1 = [
@@ -38,6 +43,8 @@ smallvec = "1.15.1"
3843
thiserror = "2.0.18"
3944

4045
[dev-dependencies]
46+
criterion = "0.8.0"
47+
gix = { path = "../gix", default-features = false, features = ["basic", "sha1"] }
4148
gix-ref = { path = "../gix-ref" }
4249
gix-filter = { path = "../gix-filter" }
4350
gix-fs = { path = "../gix-fs" }
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use std::{
2+
env,
3+
path::{Path, PathBuf},
4+
process::Command,
5+
};
6+
7+
use criterion::{criterion_group, criterion_main, Criterion};
8+
use gix_blame::{BlameEntry, BlameRanges, BlameSink};
9+
use gix_object::bstr::BString;
10+
11+
const DEFAULT_BENCH_PATH: &str = "gix-blame/src/file/function.rs";
12+
13+
struct DiscardSink;
14+
15+
impl BlameSink for DiscardSink {
16+
fn push(&mut self, _entry: BlameEntry) {}
17+
}
18+
19+
fn incremental_options() -> gix_blame::Options {
20+
gix_blame::Options {
21+
diff_algorithm: gix_diff::blob::Algorithm::Histogram,
22+
ranges: BlameRanges::default(),
23+
since: None,
24+
rewrites: Some(gix_diff::Rewrites::default()),
25+
debug_track_path: false,
26+
}
27+
}
28+
29+
fn benchmark_incremental_blame(c: &mut Criterion) {
30+
let repo_path = env::var_os("GIX_BLAME_BENCH_REPO")
31+
.map(PathBuf::from)
32+
.unwrap_or_else(|| {
33+
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
34+
.parent()
35+
.expect("gix-blame crate has a parent directory")
36+
.to_path_buf()
37+
});
38+
let source_file_path: BString = env::var("GIX_BLAME_BENCH_PATH")
39+
.unwrap_or_else(|_| DEFAULT_BENCH_PATH.into())
40+
.into_bytes()
41+
.into();
42+
let commit_spec = env::var("GIX_BLAME_BENCH_COMMIT").unwrap_or_else(|_| "HEAD".into());
43+
let git_dir = repo_path.join(".git");
44+
45+
if !has_commit_graph_cache(&git_dir) {
46+
write_changed_path_commit_graph(&repo_path).expect("commit-graph should be writable for benchmark repository");
47+
}
48+
49+
let repo = gix::open(&repo_path).expect("repository can be opened");
50+
let suspect = repo
51+
.rev_parse_single(commit_spec.as_str())
52+
.expect("commit spec resolves to one object")
53+
.detach();
54+
55+
let mut group = c.benchmark_group("gix-blame::incremental");
56+
group.bench_function("without-commit-graph", |b| {
57+
let mut resource_cache = repo
58+
.diff_resource_cache_for_tree_diff()
59+
.expect("tree-diff resource cache can be created");
60+
61+
b.iter(|| {
62+
let mut sink = DiscardSink;
63+
gix_blame::incremental(
64+
&repo,
65+
suspect,
66+
None,
67+
&mut resource_cache,
68+
source_file_path.as_ref(),
69+
&mut sink,
70+
incremental_options(),
71+
)
72+
.expect("incremental blame should succeed");
73+
});
74+
});
75+
group.bench_function("with-commit-graph", |b| {
76+
let mut resource_cache = repo
77+
.diff_resource_cache_for_tree_diff()
78+
.expect("tree-diff resource cache can be created");
79+
let cache = repo.commit_graph().expect("commit-graph can be loaded from repository");
80+
b.iter(|| {
81+
let mut sink = DiscardSink;
82+
gix_blame::incremental(
83+
&repo,
84+
suspect,
85+
Some(&cache),
86+
&mut resource_cache,
87+
source_file_path.as_ref(),
88+
&mut sink,
89+
incremental_options(),
90+
)
91+
.expect("incremental blame should succeed");
92+
});
93+
});
94+
group.finish();
95+
}
96+
97+
fn has_commit_graph_cache(git_dir: &Path) -> bool {
98+
let info_dir = git_dir.join("objects/info");
99+
info_dir.join("commit-graph").is_file() || info_dir.join("commit-graphs").is_dir()
100+
}
101+
102+
fn write_changed_path_commit_graph(worktree_path: &Path) -> std::io::Result<()> {
103+
let config_status = Command::new("git")
104+
.args(["config", "commitGraph.changedPathsVersion", "2"])
105+
.current_dir(worktree_path)
106+
.status()?;
107+
assert!(
108+
config_status.success(),
109+
"setting commitGraph.changedPathsVersion should succeed"
110+
);
111+
112+
let write_status = Command::new("git")
113+
.args([
114+
"commit-graph",
115+
"write",
116+
"--no-progress",
117+
"--reachable",
118+
"--changed-paths",
119+
])
120+
.current_dir(worktree_path)
121+
.status()?;
122+
assert!(
123+
write_status.success(),
124+
"writing changed-path commit-graph should succeed"
125+
);
126+
Ok(())
127+
}
128+
129+
criterion_group!(benches, benchmark_incremental_blame);
130+
criterion_main!(benches);

0 commit comments

Comments
 (0)