From f7dbade07a1fdeac73ec2c0a4024e4b5e5b76641 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Mon, 27 Mar 2023 09:36:00 -0700 Subject: [PATCH] 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. --- src/commands/bench.rs | 48 +++++++++++++++++++++++++++++++++++ testing/bench-revsets-git.txt | 41 ++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 testing/bench-revsets-git.txt diff --git a/src/commands/bench.rs b/src/commands/bench.rs index a5d5e1b94..06d72010b 100644 --- a/src/commands/bench.rs +++ b/src/commands/bench.rs @@ -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(()) } diff --git a/testing/bench-revsets-git.txt b/testing/bench-revsets-git.txt new file mode 100644 index 000000000..2a66e304a --- /dev/null +++ b/testing/bench-revsets-git.txt @@ -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