Skip to content

Commit 52dd978

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 4736a61 commit 52dd978

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-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: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
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").map_or_else(
31+
|| {
32+
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
33+
.parent()
34+
.expect("gix-blame crate has a parent directory")
35+
.to_path_buf()
36+
},
37+
PathBuf::from,
38+
);
39+
let source_file_path: BString = env::var("GIX_BLAME_BENCH_PATH")
40+
.unwrap_or_else(|_| DEFAULT_BENCH_PATH.into())
41+
.into_bytes()
42+
.into();
43+
let commit_spec = env::var("GIX_BLAME_BENCH_COMMIT").unwrap_or_else(|_| "HEAD".into());
44+
let git_dir = repo_path.join(".git");
45+
46+
if !has_commit_graph_cache(&git_dir) {
47+
write_changed_path_commit_graph(&repo_path).expect("commit-graph should be writable for benchmark repository");
48+
}
49+
50+
let repo = gix::open(&repo_path).expect("repository can be opened");
51+
let suspect = repo
52+
.rev_parse_single(commit_spec.as_str())
53+
.expect("commit spec resolves to one object")
54+
.detach();
55+
56+
let mut group = c.benchmark_group("gix-blame::incremental");
57+
group.bench_function("without-commit-graph", |b| {
58+
let mut resource_cache = repo
59+
.diff_resource_cache_for_tree_diff()
60+
.expect("tree-diff resource cache can be created");
61+
62+
b.iter(|| {
63+
let mut sink = DiscardSink;
64+
gix_blame::incremental(
65+
&repo,
66+
suspect,
67+
None,
68+
&mut resource_cache,
69+
source_file_path.as_ref(),
70+
&mut sink,
71+
incremental_options(),
72+
)
73+
.expect("incremental blame should succeed");
74+
});
75+
});
76+
group.bench_function("with-commit-graph", |b| {
77+
let mut resource_cache = repo
78+
.diff_resource_cache_for_tree_diff()
79+
.expect("tree-diff resource cache can be created");
80+
let cache = repo.commit_graph().expect("commit-graph can be loaded from repository");
81+
b.iter(|| {
82+
let mut sink = DiscardSink;
83+
gix_blame::incremental(
84+
&repo,
85+
suspect,
86+
Some(&cache),
87+
&mut resource_cache,
88+
source_file_path.as_ref(),
89+
&mut sink,
90+
incremental_options(),
91+
)
92+
.expect("incremental blame should succeed");
93+
});
94+
});
95+
group.finish();
96+
}
97+
98+
fn has_commit_graph_cache(git_dir: &Path) -> bool {
99+
let info_dir = git_dir.join("objects/info");
100+
info_dir.join("commit-graph").is_file() || info_dir.join("commit-graphs").is_dir()
101+
}
102+
103+
fn write_changed_path_commit_graph(worktree_path: &Path) -> std::io::Result<()> {
104+
let config_status = Command::new("git")
105+
.args(["config", "commitGraph.changedPathsVersion", "2"])
106+
.current_dir(worktree_path)
107+
.status()?;
108+
assert!(
109+
config_status.success(),
110+
"setting commitGraph.changedPathsVersion should succeed"
111+
);
112+
113+
let write_status = Command::new("git")
114+
.args([
115+
"commit-graph",
116+
"write",
117+
"--no-progress",
118+
"--reachable",
119+
"--changed-paths",
120+
])
121+
.current_dir(worktree_path)
122+
.status()?;
123+
assert!(
124+
write_status.success(),
125+
"writing changed-path commit-graph should succeed"
126+
);
127+
Ok(())
128+
}
129+
130+
criterion_group!(benches, benchmark_incremental_blame);
131+
criterion_main!(benches);

0 commit comments

Comments
 (0)