Merge pull request #6920 from libgit2/ethomson/blame_benchmarks

Add benchmarks for blame
This commit is contained in:
Edward Thomson
2024-10-21 22:22:41 +01:00
committed by GitHub
17 changed files with 213 additions and 73 deletions

View File

@@ -3,6 +3,12 @@ name: Benchmark
on:
workflow_dispatch:
inputs:
suite:
description: Benchmark suite to run
debug:
type: boolean
description: Debugging output
schedule:
- cron: '15 4 * * *'
@@ -62,6 +68,11 @@ jobs:
run: source/ci/setup-${{ matrix.platform.setup-script }}-benchmark.sh
shell: bash
if: matrix.platform.setup-script != ''
- name: Clone resource repositories
run: |
mkdir resources
git clone --bare https://github.com/git/git resources/git
git clone --bare https://github.com/torvalds/linux resources/linux
- name: Build
run: |
mkdir build && cd build
@@ -69,14 +80,30 @@ jobs:
shell: bash
- name: Benchmark
run: |
export BENCHMARK_GIT_REPOSITORY="$(pwd)/resources/git"
# avoid linux temporarily; the linux blame benchmarks are simply
# too slow to use
# export BENCHMARK_LINUX_REPOSITORY="$(pwd)/resources/linux"
if [[ "$(uname -s)" == MINGW* ]]; then
GIT2_CLI="$(cygpath -w $(pwd))\\build\\Release\\git2"
else
GIT2_CLI="$(pwd)/build/git2"
fi
if [ "${{ github.event.inputs.suite }}" != "" ]; then
SUITE_FLAG="--suite ${{ github.event.inputs.suite }}"
fi
if [ "${{ github.event.inputs.debug }}" = "true" ]; then
DEBUG_FLAG="--debug"
fi
mkdir benchmark && cd benchmark
../source/tests/benchmarks/benchmark.sh --baseline-cli "git" --cli "${GIT2_CLI}" --name libgit2 --json benchmarks.json --zip benchmarks.zip
../source/tests/benchmarks/benchmark.sh \
${SUITE_FLAG} ${DEBUG_FLAG} \
--baseline-cli "git" --cli "${GIT2_CLI}" --name libgit2 \
--json benchmarks.json --zip benchmarks.zip
shell: bash
- name: Upload results
uses: actions/upload-artifact@v4
@@ -89,7 +116,7 @@ jobs:
publish:
name: Publish results
needs: [ build ]
if: always() && github.repository == 'libgit2/libgit2'
if: always() && github.repository == 'libgit2/libgit2' && github.event_name == 'schedule'
runs-on: ubuntu-latest
steps:
- name: Check out benchmark repository

View File

@@ -22,7 +22,6 @@
static char *file;
static int porcelain, line_porcelain;
static int show_help;
static const cli_opt_spec opts[] = {
CLI_COMMON_OPT,
@@ -40,7 +39,7 @@ static const cli_opt_spec opts[] = {
static void print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts, 0);
printf("\n");
printf("Show the origin of each line of a file.\n");
@@ -254,7 +253,7 @@ int cmd_blame(int argc, char **argv)
if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
if (show_help) {
if (cli_opt__show_help) {
print_help();
return 0;
}

View File

@@ -19,7 +19,6 @@ typedef enum {
DISPLAY_TYPE
} display_t;
static int show_help;
static int display = DISPLAY_CONTENT;
static char *type_name, *object_spec;
@@ -43,7 +42,7 @@ static const cli_opt_spec opts[] = {
static void print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts, 0);
printf("\n");
printf("Display the content for the given object in the repository.\n");
@@ -147,7 +146,7 @@ int cmd_cat_file(int argc, char **argv)
if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
if (show_help) {
if (cli_opt__show_help) {
print_help();
return 0;
}

View File

@@ -19,7 +19,7 @@
#define COMMAND_NAME "clone"
static char *branch, *remote_path, *local_path, *depth;
static int show_help, quiet, checkout = 1, bare;
static int quiet, checkout = 1, bare;
static bool local_path_exists;
static cli_progress progress = CLI_PROGRESS_INIT;
@@ -46,7 +46,7 @@ static const cli_opt_spec opts[] = {
static void print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts, 0);
printf("\n");
printf("Clone a repository into a new directory.\n");
@@ -133,7 +133,7 @@ int cmd_clone(int argc, char **argv)
if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
if (show_help) {
if (cli_opt__show_help) {
print_help();
return 0;
}

View File

@@ -23,7 +23,6 @@ typedef enum {
static action_t action = ACTION_NONE;
static int show_origin;
static int show_scope;
static int show_help;
static int null_separator;
static int config_level;
static char *config_filename;
@@ -68,7 +67,7 @@ static const cli_opt_spec opts[] = {
static void print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts, 0);
printf("\n");
printf("Query and set configuration options.\n");
@@ -180,7 +179,7 @@ int cmd_config(int argc, char **argv)
if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
if (show_help) {
if (cli_opt__show_help) {
print_help();
return 0;
}

View File

@@ -13,7 +13,6 @@
#define COMMAND_NAME "hash-object"
static int show_help;
static char *type_name;
static int write_object, read_stdin, literally;
static char **filenames;
@@ -36,7 +35,7 @@ static const cli_opt_spec opts[] = {
static void print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts, 0);
printf("\n");
printf("Compute the object ID for a given file and optionally write that file\nto the object database.\n");
@@ -103,7 +102,7 @@ int cmd_hash_object(int argc, char **argv)
if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
if (show_help) {
if (cli_opt__show_help) {
print_help();
return 0;
}

View File

@@ -13,7 +13,6 @@
#define COMMAND_NAME "help"
static char *command;
static int show_help;
static const cli_opt_spec opts[] = {
CLI_COMMON_OPT,
@@ -25,7 +24,7 @@ static const cli_opt_spec opts[] = {
static int print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts, CLI_OPT_USAGE_SHOW_HIDDEN);
printf("\n");
printf("Display help information about %s. If a command is specified, help\n", PROGRAM_NAME);
@@ -39,7 +38,7 @@ static int print_commands(void)
{
const cli_cmd_spec *cmd;
cli_opt_usage_fprint(stdout, PROGRAM_NAME, NULL, cli_common_opts);
cli_opt_usage_fprint(stdout, PROGRAM_NAME, NULL, cli_common_opts, CLI_OPT_USAGE_SHOW_HIDDEN);
printf("\n");
printf("These are the %s commands available:\n\n", PROGRAM_NAME);
@@ -62,7 +61,7 @@ int cmd_help(int argc, char **argv)
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
/* Show the meta-help */
if (show_help)
if (cli_opt__show_help)
return print_help();
/* We were not asked to show help for a specific command. */

View File

@@ -14,14 +14,12 @@
#define BUFFER_SIZE (1024 * 1024)
static int show_help, verbose, read_stdin;
static int verbose, read_stdin;
static char *filename;
static cli_progress progress = CLI_PROGRESS_INIT;
static const cli_opt_spec opts[] = {
{ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
"display help about the " COMMAND_NAME " command" },
CLI_COMMON_OPT,
{ CLI_OPT_TYPE_SWITCH, "verbose", 'v', &verbose, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "display progress output" },
@@ -38,7 +36,7 @@ static const cli_opt_spec opts[] = {
static void print_help(void)
{
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts, 0);
printf("\n");
printf("Indexes a packfile and writes the index to disk.\n");
@@ -62,7 +60,7 @@ int cmd_index_pack(int argc, char **argv)
if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
if (show_help) {
if (cli_opt__show_help) {
print_help();
return 0;
}

View File

@@ -20,20 +20,31 @@
* Common command arguments.
*/
extern int cli_opt__show_help;
extern int cli_opt__use_pager;
#define CLI_COMMON_OPT_HELP \
CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1, \
CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING
CLI_OPT_TYPE_SWITCH, "help", 0, &cli_opt__show_help, 1, \
CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, \
NULL, "display help information"
#define CLI_COMMON_OPT_CONFIG \
CLI_OPT_TYPE_VALUE, NULL, 'c', NULL, 0, \
CLI_OPT_USAGE_HIDDEN
CLI_OPT_TYPE_VALUE, NULL, 'c', NULL, 0, \
CLI_OPT_USAGE_HIDDEN, \
"key=value", "add configuration value"
#define CLI_COMMON_OPT_CONFIG_ENV \
CLI_OPT_TYPE_VALUE, "config-env", 0, NULL, 0, \
CLI_OPT_USAGE_HIDDEN
CLI_OPT_TYPE_VALUE, "config-env", 0, NULL, 0, \
CLI_OPT_USAGE_HIDDEN, \
"key=value", "set configuration value to environment variable"
#define CLI_COMMON_OPT_NO_PAGER \
CLI_OPT_TYPE_SWITCH, "no-pager", 0, &cli_opt__use_pager, 0, \
CLI_OPT_USAGE_HIDDEN, \
NULL, "don't paginate multi-page output"
#define CLI_COMMON_OPT \
{ CLI_COMMON_OPT_HELP }, \
{ CLI_COMMON_OPT_CONFIG }, \
{ CLI_COMMON_OPT_CONFIG_ENV }
{ CLI_COMMON_OPT_CONFIG_ENV }, \
{ CLI_COMMON_OPT_NO_PAGER }
typedef struct {
char **args;
@@ -49,23 +60,4 @@ extern int cli_resolve_path(
git_repository *repo,
const char *given_path);
/*
* Common command arguments.
*/
#define CLI_COMMON_OPT_HELP \
CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1, \
CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING
#define CLI_COMMON_OPT_CONFIG \
CLI_OPT_TYPE_VALUE, NULL, 'c', NULL, 0, \
CLI_OPT_USAGE_HIDDEN
#define CLI_COMMON_OPT_CONFIG_ENV \
CLI_OPT_TYPE_VALUE, "config-env", 0, NULL, 0, \
CLI_OPT_USAGE_HIDDEN
#define CLI_COMMON_OPT \
{ CLI_COMMON_OPT_HELP }, \
{ CLI_COMMON_OPT_CONFIG }, \
{ CLI_COMMON_OPT_CONFIG_ENV }
#endif /* CLI_common_h__ */

View File

@@ -10,18 +10,16 @@
#include "common.h"
#include "cmd.h"
static int show_help = 0;
int cli_opt__show_help = 0;
int cli_opt__use_pager = 1;
static int show_version = 0;
static char *command = NULL;
static char **args = NULL;
const cli_opt_spec cli_common_opts[] = {
{ CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "display help information" },
{ CLI_OPT_TYPE_VALUE, NULL, 'c', NULL, 0,
CLI_OPT_USAGE_DEFAULT, "key=value", "add configuration value" },
{ CLI_OPT_TYPE_VALUE, "config-env", 0, NULL, 0,
CLI_OPT_USAGE_DEFAULT, "key=value", "set configuration value to environment variable" },
CLI_COMMON_OPT,
{ CLI_OPT_TYPE_SWITCH, "version", 0, &show_version, 1,
CLI_OPT_USAGE_DEFAULT, NULL, "display the version" },
{ CLI_OPT_TYPE_ARG, "command", 0, &command, 0,
@@ -64,6 +62,19 @@ static void reorder_args(char **argv, size_t first)
argv[1] = tmp;
}
/*
* When invoked without a command, or just with `--help`, we invoke
* the help command; but we want to preserve only arguments that would
* be useful for that.
*/
static void help_args(int *argc, char **argv)
{
cli_opt__show_help = 0;
argv[0] = "help";
*argc = 1;
}
int main(int argc, char **argv)
{
const cli_cmd_spec *cmd;
@@ -82,7 +93,7 @@ int main(int argc, char **argv)
while (cli_opt_parser_next(&opt, &optparser)) {
if (!opt.spec) {
cli_opt_status_fprint(stderr, PROGRAM_NAME, &opt);
cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, cli_common_opts);
cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, cli_common_opts, CLI_OPT_USAGE_SHOW_HIDDEN);
ret = CLI_EXIT_USAGE;
goto done;
}
@@ -103,6 +114,7 @@ int main(int argc, char **argv)
}
if (!command) {
help_args(&argc, argv);
ret = cmd_help(argc, argv);
goto done;
}

View File

@@ -46,7 +46,8 @@ int cli_opt_usage_fprint(
FILE *file,
const char *command,
const char *subcommand,
const cli_opt_spec specs[])
const cli_opt_spec specs[],
unsigned int print_flags)
{
git_str usage = GIT_BUF_INIT, opt = GIT_BUF_INIT;
const cli_opt_spec *spec;
@@ -73,7 +74,8 @@ int cli_opt_usage_fprint(
next_choice = !!((spec + 1)->usage & CLI_OPT_USAGE_CHOICE);
if (spec->usage & CLI_OPT_USAGE_HIDDEN)
if ((spec->usage & CLI_OPT_USAGE_HIDDEN) &&
!(print_flags & CLI_OPT_USAGE_SHOW_HIDDEN))
continue;
if (choice)
@@ -140,7 +142,7 @@ int cli_opt_usage_error(
const cli_opt *invalid_opt)
{
cli_opt_status_fprint(stderr, PROGRAM_NAME, invalid_opt);
cli_opt_usage_fprint(stderr, PROGRAM_NAME, subcommand, specs);
cli_opt_usage_fprint(stderr, PROGRAM_NAME, subcommand, specs, 0);
return CLI_EXIT_USAGE;
}
@@ -150,12 +152,19 @@ int cli_opt_help_fprint(
{
git_str help = GIT_BUF_INIT;
const cli_opt_spec *spec;
bool required;
int error = 0;
/* Display required arguments first */
for (spec = specs; spec->type; ++spec) {
if (! (spec->usage & CLI_OPT_USAGE_REQUIRED) ||
(spec->usage & CLI_OPT_USAGE_HIDDEN))
if ((spec->usage & CLI_OPT_USAGE_HIDDEN) ||
(spec->type == CLI_OPT_TYPE_LITERAL))
continue;
required = ((spec->usage & CLI_OPT_USAGE_REQUIRED) ||
((spec->usage & CLI_OPT_USAGE_CHOICE) && required));
if (!required)
continue;
git_str_printf(&help, " ");
@@ -163,13 +172,22 @@ int cli_opt_help_fprint(
if ((error = print_spec_name(&help, spec)) < 0)
goto done;
git_str_printf(&help, ": %s\n", spec->help);
if (spec->help)
git_str_printf(&help, ": %s", spec->help);
git_str_printf(&help, "\n");
}
/* Display the remaining arguments */
for (spec = specs; spec->type; ++spec) {
if ((spec->usage & CLI_OPT_USAGE_REQUIRED) ||
(spec->usage & CLI_OPT_USAGE_HIDDEN))
if ((spec->usage & CLI_OPT_USAGE_HIDDEN) ||
(spec->type == CLI_OPT_TYPE_LITERAL))
continue;
required = ((spec->usage & CLI_OPT_USAGE_REQUIRED) ||
((spec->usage & CLI_OPT_USAGE_CHOICE) && required));
if (required)
continue;
git_str_printf(&help, " ");
@@ -177,8 +195,10 @@ int cli_opt_help_fprint(
if ((error = print_spec_name(&help, spec)) < 0)
goto done;
git_str_printf(&help, ": %s\n", spec->help);
if (spec->help)
git_str_printf(&help, ": %s", spec->help);
git_str_printf(&help, "\n");
}
if (git_str_oom(&help) ||

View File

@@ -8,6 +8,10 @@
#ifndef CLI_opt_usage_h__
#define CLI_opt_usage_h__
typedef enum {
CLI_OPT_USAGE_SHOW_HIDDEN = (1 << 0),
} cli_opt_usage_flags;
/**
* Prints usage information to the given file handle.
*
@@ -21,7 +25,8 @@ int cli_opt_usage_fprint(
FILE *file,
const char *command,
const char *subcommand,
const cli_opt_spec specs[]);
const cli_opt_spec specs[],
unsigned int print_flags);
int cli_opt_usage_error(
const char *subcommand,

View File

@@ -213,9 +213,19 @@ for TEST_PATH in "${BENCHMARK_DIR}"/*; do
ERROR_FILE="${OUTPUT_DIR}/${TEST_FILE}.err"
FAILED=
${TEST_PATH} --cli "${TEST_CLI}" --baseline-cli "${BASELINE_CLI}" --json "${JSON_FILE}" ${SHOW_OUTPUT} >"${OUTPUT_FILE}" 2>"${ERROR_FILE}" || FAILED=1
{
${TEST_PATH} --cli "${TEST_CLI}" --baseline-cli "${BASELINE_CLI}" --json "${JSON_FILE}" ${SHOW_OUTPUT} >"${OUTPUT_FILE}" 2>"${ERROR_FILE}";
FAILED=$?
} || true
if [ "${FAILED}" = "1" ]; then
if [ "${FAILED}" = "2" ]; then
if [ "${VERBOSE}" != "1" ]; then
echo "skipped!"
fi
indent < "${ERROR_FILE}"
continue
elif [ "${FAILED}" != "0" ]; then
if [ "${VERBOSE}" != "1" ]; then
echo "failed!"
fi

View File

@@ -21,6 +21,9 @@ else
OUTPUT_STYLE="auto"
fi
HELP_GIT_REMOTE="https://github.com/git/git"
HELP_LINUX_REMOTE="https://github.com/torvalds/linux"
#
# parse the arguments to the outer script that's including us; these are arguments that
# the `benchmark.sh` passes (or that a user could specify when running an individual test)
@@ -218,6 +221,26 @@ create_preparescript() {
fi
}
clone_repo() {
REPO="\${1}"
if [ "\${REPO}" = "" ]; then
echo "usage: clone_repo <repo>" 1>&2
exit 1
fi
REPO_UPPER=\$(echo "\${1}" | tr '[:lower:]' '[:upper:]')
REPO_URL=\$(eval echo "\\\${BENCHMARK_\${REPO_UPPER}_REPOSITORY}")
if [ "\${REPO_URL}" = "" ]; then
echo "\$0: unknown repository '\${REPO}'" 1>&2
exit 1
fi
rm -rf "\${SANDBOX_DIR:?}/\${REPO}"
git clone "\${REPO_URL}" "\${SANDBOX_DIR}/\${REPO}"
}
cd "\${SANDBOX_DIR}"
EOF
@@ -293,6 +316,9 @@ gitbench() {
NEXT="prepare"
elif [ "${a}" = "--chdir" ]; then
NEXT="chdir"
elif [[ "${a}" == "--" ]]; then
shift
break
elif [[ "${a}" == "--"* ]]; then
echo "unknown argument: \"${a}\"" 1>&2
gitbench_usage 1>&2
@@ -361,3 +387,27 @@ gitbench() {
hyperfine "${ARGUMENTS[@]}"
rm -rf "${SANDBOX_DIR:?}"
}
# helper script to give useful error messages about configuration
needs_repo() {
REPO="${1}"
if [ "${REPO}" = "" ]; then
echo "usage: needs_repo <repo>" 1>&2
exit 1
fi
REPO_UPPER=$(echo "${1}" | tr '[:lower:]' '[:upper:]')
REPO_URL=$(eval echo "\${BENCHMARK_${REPO_UPPER}_REPOSITORY}")
REPO_REMOTE_URL=$(eval echo "\${HELP_${REPO_UPPER}_REMOTE}")
if [ "${REPO_URL}" = "" ]; then
echo "$0: '${REPO}' repository not configured" 1>&2
echo "" 1>&2
echo "This benchmark needs an on-disk '${REPO}' repository. First, clone the" 1>&2
echo "remote repository ('${REPO_REMOTE_URL}') locally then set," 1>&2
echo "the 'BENCHMARK_${REPO_UPPER}_REPOSITORY' environment variable to the path that" 1>&2
echo "contains the repository locally, then run this benchmark again." 1>&2
exit 2
fi
}

View File

@@ -0,0 +1,11 @@
#!/bin/bash -e
. "$(dirname "$0")/benchmark_helpers.sh"
needs_repo git
gitbench --prepare "clone_repo git && cd git && git reset --hard v2.45.0" \
--warmup 5 \
--chdir "git" \
-- \
--no-pager blame "git.c"

View File

@@ -0,0 +1,11 @@
#!/bin/bash -e
. "$(dirname "$0")/benchmark_helpers.sh"
needs_repo linux
gitbench --prepare "clone_repo linux && cd linux && git reset --hard v6.9" \
--warmup 5 \
--chdir "linux" \
-- \
--no-pager blame "Makefile"

View File

@@ -0,0 +1,9 @@
#!/bin/bash -e
. "$(dirname "$0")/benchmark_helpers.sh"
gitbench --prepare "sandbox_repo testrepo && cd testrepo && git reset --hard HEAD" \
--warmup 5 \
--chdir "testrepo" \
-- \
--no-pager blame "branch_file.txt"