cli: add a command for benchmarking many revsets

This command is similar to Mercurial's revset benchmarking command. It
lets you pass in a file containing revsets. I also included a file
with some revsets to test on the git.git repo. I put it in `testing/`,
which doesn't seem perfect. I'm happy to hear suggestions for better
places, or we can move it later if we find a better place.

Note that these tests don't clear caches between each run (or even
between tests), so revsets that rely on filtering commit data that's
not indexed appear faster than they typically are in reality.
This commit is contained in:
Martin von Zweigbergk 2023-03-27 09:36:00 -07:00 committed by Martin von Zweigbergk
parent 7249be33b0
commit f7dbade07a
2 changed files with 89 additions and 0 deletions

View file

@ -36,6 +36,8 @@ pub enum BenchCommands {
ResolvePrefix(BenchResolvePrefixArgs),
#[command(name = "revset")]
Revset(BenchRevsetArgs),
#[command(name = "revsets")]
Revsets(BenchRevsetsArgs),
}
/// Find the common ancestor(s) of a set of commits
@ -58,6 +60,12 @@ pub struct BenchRevsetArgs {
revisions: String,
}
/// Benchmark multiple revsets specified in a file
#[derive(clap::Args, Clone, Debug)]
pub struct BenchRevsetsArgs {
file: String,
}
/// Resolve a commit ID prefix
#[derive(clap::Args, Clone, Debug)]
pub struct BenchResolvePrefixArgs {
@ -151,6 +159,46 @@ pub(crate) fn cmd_bench(
routine,
)?;
}
BenchCommands::Revsets(command_matches) => {
let workspace_command = command.workspace_helper(ui)?;
let file_path = command.cwd().join(&command_matches.file);
let revsets = std::fs::read_to_string(&file_path)?;
let mut criterion = Criterion::default();
let mut group = criterion.benchmark_group("revsets");
for revset in revsets.lines() {
let revset = revset.trim();
if revset.starts_with('#') || revset.is_empty() {
continue;
}
writeln!(ui, "----------Testing revset: {revset}----------\n")?;
let expression = workspace_command.parse_revset(revset)?;
// Time both evaluation and iteration. Note that we don't clear caches (such as
// commit objects in `Store`) between each run (`criterion`
// doesn't seem to support that).
let routine = || {
workspace_command
.evaluate_revset(expression.clone())
.unwrap()
.iter()
.count()
};
let before = Instant::now();
let result = routine();
let after = Instant::now();
writeln!(
ui,
"First run took {:?} and produced {result} commits",
after.duration_since(before),
)?;
group.bench_function(&format!("revset {}", &revset), |bencher| {
bencher.iter(routine);
});
}
// Neither of these seem to report anything...
group.finish();
criterion.final_summary();
}
}
Ok(())
}

View file

@ -0,0 +1,41 @@
# Revsets to pass to `jj bench revsets` on the Git
# Single tags
v1.0.0
v2.40.0
# Old history
:v1.0.0
..v1.0.0
# More history
:v2.40.0
..v2.40.0
# Only recent history
v2.39.0..v2.40.0
:v2.40.0 ~ :v2.39.0
v2.39.0:v2.40.0
# Tags and branches
tags()
branches()
# Intersection of range with a small subset
tags() & :v2.40.0
v2.39.0 & :v2.40.0
# Author and committer
author(peff)
committer(gitster)
# Intersection and union of large subsets
author(peff) & committer(gitster)
author(peff) | committer(gitster)
# Roots and heads of small subsets
roots(tags())
heads(tags())
# Roots and heads of large subsets
roots(author(peff))
heads(author(peff))
# Roots and heads of range
roots(:v2.40.0)
heads(:v2.40.0)
# Parents and children of small subset
tags()-
tags()+
# Files are unbearably slow, so only filter within small set
file(Makefile) & v1.0.0..v1.2.0