microbenchmarks: benchmark the oid code

At present, the library's oid manipulation functions are slower when
built in SHA256 mode than when not. Add some microbenchmarks around the
oid compare and copy functions to understand this better.
This commit is contained in:
Edward Thomson
2026-01-10 13:53:35 +00:00
parent 1f2f1f74a1
commit ca56f26039
9 changed files with 280 additions and 3 deletions

View File

@@ -20,7 +20,8 @@ option(EXPERIMENTAL_SHA256 "Enable experimental SHA256 support (for R&D/test
# Optional subsystems
option(BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON)
option(BUILD_TESTS "Build Tests using the Clar suite" ON)
option(BUILD_TESTS "Build the test suite" ON)
option(BUILD_BENCHMARKS "Build the benchmark suite" OFF)
option(BUILD_CLI "Build the command-line interface" ON)
option(BUILD_EXAMPLES "Build library usage example apps" OFF)
option(BUILD_FUZZERS "Build the fuzz targets" OFF)
@@ -112,6 +113,10 @@ if(BUILD_TESTS)
add_subdirectory(tests)
endif()
if(BUILD_BENCHMARKS)
add_subdirectory(benchmarks)
endif()
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()

View File

@@ -0,0 +1 @@
add_subdirectory(libgit2)

View File

@@ -0,0 +1,78 @@
# util: the unit tests for libgit2's utility library
if(NOT "${CMAKE_VERSION}" VERSION_LESS 3.27)
cmake_policy(SET CMP0148 OLD)
endif()
set(Python_ADDITIONAL_VERSIONS 3 2.7)
find_package(PythonInterp)
if(NOT PYTHONINTERP_FOUND)
message(FATAL_ERROR "Could not find a python interpeter, which is needed to build the tests. "
"Make sure python is available, or pass -DBUILD_TESTS=OFF to skip building the tests")
endif()
set(CLAR_PATH "${PROJECT_SOURCE_DIR}/deps/clar")
set(BENCHMARK_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
add_definitions(-DCLAR_TMPDIR=\"libgit2_bench\")
add_definitions(-DCLAR_WIN32_LONGPATHS)
add_definitions(-DCLAR_HAS_REALPATH)
add_definitions(-D_FILE_OFFSET_BITS=64)
file(GLOB BENCHMARK_SRC *.c *.h)
list(SORT BENCHMARK_SRC)
set(CLAR_SRC
"${CLAR_PATH}/clar.c"
"${CLAR_PATH}/clar.h"
"${CLAR_PATH}/clar/fixtures.h"
"${CLAR_PATH}/clar/print.h"
"${CLAR_PATH}/clar/summary.h"
"${CLAR_PATH}/clar/sandbox.h"
"${CLAR_PATH}/clar/fs.h")
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/clar.suite ${CMAKE_CURRENT_BINARY_DIR}/clar_suite.h
COMMAND ${PYTHON_EXECUTABLE} ${CLAR_PATH}/generate.py -p benchmark -o "${CMAKE_CURRENT_BINARY_DIR}" -f .
DEPENDS ${BENCHMARK_SRC}
WORKING_DIRECTORY ${BENCHMARK_PATH}
)
set_source_files_properties(
${CLAR_PATH}/clar.c
PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clar.suite)
add_executable(libgit2_benchmarks ${CLAR_SRC}
${BENCHMARK_SRC}
$<TARGET_OBJECTS:util>
${LIBGIT2_DEPENDENCY_OBJECTS})
target_link_libraries(libgit2_benchmarks libgit2package ${LIBGIT2_SYSTEM_LIBS})
if(NOT MSVC_IDE)
target_link_libraries(libgit2_benchmarks m)
endif()
ide_split_sources(libgit2_benchmarks)
target_include_directories(libgit2_benchmarks PRIVATE
"${CLAR_PATH}"
"${libgit2_BINARY_DIR}/src/util"
"${libgit2_BINARY_DIR}/include"
"${libgit2_SOURCE_DIR}/src/util"
"${libgit2_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}"
"${LIBGIT2_DEPENDENCY_INCLUDES}"
"${LIBGIT2_SYSTEM_INCLUDES}")
#
# Old versions of gcc require us to declare our test functions; don't do
# this on newer compilers to avoid unnecessary recompilation.
#
if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
target_compile_options(libgit2_benchmarks PRIVATE -include "clar_suite.h")
endif()
if(MSVC_IDE)
set_target_properties(libgit2_benchmarks PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
set_source_files_properties("precompiled.c" COMPILE_FLAGS "/Ycprecompiled.h")
endif()

31
benchmarks/libgit2/main.c Normal file
View File

@@ -0,0 +1,31 @@
#include <stdio.h>
#include "clar.h"
#include <git2.h>
#ifdef _WIN32
int __cdecl main(int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{
int res;
clar_test_set_mode(CL_TEST_BENCHMARK);
clar_test_init(argc, argv);
res = git_libgit2_init();
if (res < 0) {
const git_error *err = git_error_last();
const char *msg = err ? err->message : "unknown failure";
fprintf(stderr, "failed to init libgit2: %s\n", msg);
return res;
}
/* Run the test suite */
res = clar_test_run();
clar_test_shutdown();
return res;
}

151
benchmarks/libgit2/oid.c Normal file
View File

@@ -0,0 +1,151 @@
#include "clar.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <git2.h>
#define BENCHMARK_OID_COUNT 256
static git_oid sha1_one[BENCHMARK_OID_COUNT];
static git_oid *sha1_two;
#ifdef GIT_EXPERIMENTAL_SHA256
static git_oid sha256_one[BENCHMARK_OID_COUNT];
static git_oid *sha256_two;
#endif
static void update_data_to_val(git_oid *out, git_oid_t type, uint32_t val)
{
unsigned char data[GIT_OID_MAX_SIZE] = {0};
size_t size;
#ifdef GIT_EXPERIMENTAL_SHA256
size = (type == GIT_OID_SHA256) ? GIT_OID_SHA256_SIZE : GIT_OID_SHA1_SIZE;
#else
size = GIT_OID_SHA1_SIZE;
((void)(type));
#endif
memset(data, 0, GIT_OID_MAX_SIZE);
data[size - 1] = (unsigned char)(val & 0x000000ff);
data[size - 2] = (unsigned char)((val & 0x0000ff00) >> 8);
data[size - 3] = (unsigned char)((val & 0x00ff0000) >> 16);
data[size - 4] = (unsigned char)((val & 0x00ff0000) >> 24);
#ifdef GIT_EXPERIMENTAL_SHA256
cl_assert(git_oid_from_raw(out, data, type) == 0);
#else
cl_assert(git_oid_fromraw(out, data) == 0);
#endif
}
void benchmark_oid__initialize(void)
{
uint32_t accum = 0;
size_t i;
sha1_two = calloc(BENCHMARK_OID_COUNT, sizeof(git_oid));
cl_assert(sha1_two != NULL);
#ifdef GIT_EXPERIMENTAL_SHA256
sha256_two = calloc(BENCHMARK_OID_COUNT, sizeof(git_oid));
cl_assert(sha256_two != NULL);
#endif
for (i = 0; i < BENCHMARK_OID_COUNT; i++) {
update_data_to_val(&sha1_one[i], GIT_OID_SHA1, accum++);
update_data_to_val(&sha1_two[i], GIT_OID_SHA1, accum++);
}
#ifdef GIT_EXPERIMENTAL_SHA256
for (i = 0; i < BENCHMARK_OID_COUNT; i++) {
update_data_to_val(&sha256_one[i], GIT_OID_SHA256, accum++);
update_data_to_val(&sha256_two[i], GIT_OID_SHA256, accum++);
}
#endif
}
void benchmark_oid__reset(void)
{
}
void benchmark_oid__cleanup(void)
{
free(sha1_two);
#ifdef GIT_EXPERIMENTAL_SHA256
free(sha256_two);
#endif
}
void benchmark_oid__cmp_sha1(void)
{
size_t i, j;
for (i = 0; i < 1024 * 16; i++)
for (j = 0; j < BENCHMARK_OID_COUNT; j++)
git_oid_cmp(&sha1_one[j], &sha1_two[j]);
}
void benchmark_oid__cmp_sha256(void)
{
#ifdef GIT_EXPERIMENTAL_SHA256
size_t i, j;
for (i = 0; i < 1024 * 16; i++)
for (j = 0; j < BENCHMARK_OID_COUNT; j++)
git_oid_cmp(&sha256_one[j], &sha256_two[j]);
#else
clar__skip();
#endif
}
void benchmark_oid__cpy_sha1(void)
{
git_oid dest;
size_t i, j;
for (i = 0; i < 1024 * 16; i++)
for (j = 0; j < BENCHMARK_OID_COUNT; j++)
git_oid_cpy(&dest, &sha1_one[j]);
}
void benchmark_oid__cpy_sha256(void)
{
#ifdef GIT_EXPERIMENTAL_SHA256
git_oid dest;
size_t i, j;
for (i = 0; i < 1024 * 16; i++)
for (j = 0; j < BENCHMARK_OID_COUNT; j++)
git_oid_cpy(&dest, &sha256_one[j]);
#else
clar__skip();
#endif
}
void benchmark_oid__zero_sha1(void)
{
size_t i, j;
for (i = 0; i < 1024 * 16; i++)
for (j = 0; j < BENCHMARK_OID_COUNT; j++)
git_oid_is_zero(&sha1_one[j]);
}
void benchmark_oid__zero_sha256(void)
{
#ifdef GIT_EXPERIMENTAL_SHA256
size_t i, j;
for (i = 0; i < 1024 * 16; i++)
for (j = 0; j < BENCHMARK_OID_COUNT; j++)
git_oid_is_zero(&sha256_one[j]);
#else
clar__skip();
#endif
}

View File

@@ -0,0 +1 @@
#include "precompiled.h"

View File

@@ -0,0 +1,2 @@
#include "git2.h"
#include "clar.h"

View File

@@ -10,7 +10,7 @@ find_package(PythonInterp)
if(NOT PYTHONINTERP_FOUND)
message(FATAL_ERROR "Could not find a python interpreter, which is needed to build the tests. "
"Make sure python is available, or pass -DBUILD_TESTS=OFF to skip building the tests")
ENDIF()
endif()
set(CLAR_PATH "${PROJECT_SOURCE_DIR}/deps/clar")
set(CLAR_FIXTURES "${PROJECT_SOURCE_DIR}/tests/resources/")
@@ -48,7 +48,11 @@ add_executable(libgit2_tests ${SRC_CLAR} ${SRC_TEST} ${LIBGIT2_OBJECTS})
set_target_properties(libgit2_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
target_include_directories(libgit2_tests PRIVATE ${TEST_INCLUDES} ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES})
target_include_directories(libgit2_tests SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
target_link_libraries(libgit2_tests ${LIBGIT2_SYSTEM_LIBS})
if(NOT MSVC_IDE)
target_link_libraries(libgit2_tests m)
endif()
ide_split_sources(libgit2_tests)

View File

@@ -10,7 +10,7 @@ find_package(PythonInterp)
if(NOT PYTHONINTERP_FOUND)
message(FATAL_ERROR "Could not find a python interpeter, which is needed to build the tests. "
"Make sure python is available, or pass -DBUILD_TESTS=OFF to skip building the tests")
ENDIF()
endif()
set(CLAR_PATH "${libgit2_SOURCE_DIR}/deps/clar")
set(CLAR_FIXTURES "${libgit2_SOURCE_DIR}/tests/resources/")
@@ -48,7 +48,11 @@ set_target_properties(util_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_B
target_include_directories(util_tests PRIVATE ${TEST_INCLUDES} ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES})
target_include_directories(util_tests SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
target_link_libraries(util_tests ${LIBGIT2_SYSTEM_LIBS})
if(NOT MSVC_IDE)
target_link_libraries(util_tests m)
endif()
ide_split_sources(util_tests)