From 65358fc76694362080907ca67c6b41c82ab050cd Mon Sep 17 00:00:00 2001 From: Marco Cetica Date: Tue, 25 Nov 2025 16:46:20 +0100 Subject: [PATCH] Added benchmark program --- .github/workflows/clang-build.yml | 6 +- .github/workflows/gcc-build.yml | 6 +- Makefile | 25 ++++++++- README.md | 11 +++- benchmark/benchmark.c | 92 +++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 benchmark/benchmark.c diff --git a/.github/workflows/clang-build.yml b/.github/workflows/clang-build.yml index 1e7b286..aea82b1 100644 --- a/.github/workflows/clang-build.yml +++ b/.github/workflows/clang-build.yml @@ -16,4 +16,8 @@ jobs: - name: Run unit tests run: | - ./test_vector && ./test_map && ./test_bigint \ No newline at end of file + ./test_vector && ./test_map && ./test_bigint + + - name: Run benchmarks + run: | + ./benchmark_datum \ No newline at end of file diff --git a/.github/workflows/gcc-build.yml b/.github/workflows/gcc-build.yml index b277691..cad9781 100644 --- a/.github/workflows/gcc-build.yml +++ b/.github/workflows/gcc-build.yml @@ -13,4 +13,8 @@ jobs: - name: Run unit tests run: | - ./test_vector && ./test_map && ./test_bigint \ No newline at end of file + ./test_vector && ./test_map && ./test_bigint + + - name: Run benchmarks + run: | + ./benchmark_datum \ No newline at end of file diff --git a/Makefile b/Makefile index b0152f4..01e5c2d 100644 --- a/Makefile +++ b/Makefile @@ -3,21 +3,29 @@ CFLAGS = -Wall -Wextra -Werror -pedantic-errors -fstack-protector-strong \ -fsanitize=address -fsanitize=undefined -fstack-clash-protection \ -Wwrite-strings -g -std=c99 +BENCH_FLAGS = -Wall -Wextra -Werror -O3 + SRC_DIR = src +BENCH_SRC = benchmark + OBJ_DIR = obj +BENCH_OBJ_DIR = bench_obj + TESTS_SRC = tests TARGET = usage TEST_V_TARGET = test_vector TEST_M_TARGET = test_map TEST_B_TARGET = test_bigint +BENCH_TARGET = benchmark_datum LIB_OBJS = $(OBJ_DIR)/vector.o $(OBJ_DIR)/map.o $(OBJ_DIR)/bigint.o PROG_OBJS = $(OBJ_DIR)/usage.o .PHONY: all clean -all: $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) +all: $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(BENCH_TARGET) +bench: $(BENCH_TARGET) $(TARGET): $(PROG_OBJS) $(LIB_OBJS) $(CC) $(CFLAGS) -o $@ $^ @@ -43,5 +51,18 @@ $(OBJ_DIR)/%.o: $(TESTS_SRC)/%.c | $(OBJ_DIR) $(OBJ_DIR): mkdir -p $(OBJ_DIR) +# Benchmark rules +$(BENCH_TARGET): $(BENCH_OBJ_DIR)/bench.o $(BENCH_OBJ_DIR)/vector.o $(BENCH_OBJ_DIR)/map.o + $(CC) $(BENCH_FLAGS) -o $@ $^ + +$(BENCH_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(BENCH_OBJ_DIR) + $(CC) $(BENCH_FLAGS) -c -o $@ $< + +$(BENCH_OBJ_DIR)/bench.o: $(BENCH_SRC)/benchmark.c | $(BENCH_OBJ_DIR) + $(CC) $(BENCH_FLAGS) -c -o $@ $< + +$(BENCH_OBJ_DIR): + mkdir -p $(BENCH_OBJ_DIR) + clean: - rm -rf $(OBJ_DIR) $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) + rm -rf $(OBJ_DIR) $(BENCH_OBJ_DIR) $(TARGET) $(TEST_V_TARGET) $(TEST_M_TARGET) $(TEST_B_TARGET) $(BENCH_TARGET) diff --git a/README.md b/README.md index 95b1dc5..44cb523 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ and a sample usage for every available method. To run it, first issue the follow $ make clean all ``` -This will compile the library as well as the `usage.c` file and the unit tests. After that, you can run it by typing `./usage`. +This will compile the library as well as the `usage.c` file, the unit tests and the benchmark. After that, you can run it by typing `./usage`. ## Documentation For additional details about this library (internal design, memory @@ -149,6 +149,15 @@ $ ./test_map $ ./test_bigint ``` +## Benchmark +Under the [`benchmark/`](/benchmark/) folder, you can find a simple benchmark program that stress the `Vector` and the `Map` data structures. You can run it by issuing the following command: + +```sh +$ ./benchmark_datum +Computing Vector average time...average time: 18 ms +Computing Map average time...average time: 31 ms +``` + ## License This library is released under the GPLv3 license. You can find a copy of the license with this repository or by visiting [the following link](https://choosealicense.com/licenses/gpl-3.0/). diff --git a/benchmark/benchmark.c b/benchmark/benchmark.c new file mode 100644 index 0000000..913f4cd --- /dev/null +++ b/benchmark/benchmark.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include + +#include "../src/vector.h" +#include "../src/map.h" + +typedef void (*test_fn_t)(size_t iterations); + +void test_vector(size_t iterations) { + vector_t *vec = vector_new(16, sizeof(int)).value.vector; + + for (size_t idx = 0; idx < iterations; idx++) { + vector_push(vec, &idx); + } + + volatile uint64_t sum = 0; // prevent the compiler from optimizing away the sum + for (size_t idx = 0; idx < iterations; idx++) { + const int *val = (int*)vector_get(vec, idx).value.element; + sum += *val; + } + + // Another trick to prevent compiler optimization + if (sum == 0xB00B5) { + printf("sum = %llu\n", (unsigned long long)sum); + } + + vector_destroy(vec); +} + +void test_map(size_t iterations) { + map_t *map = map_new().value.map; + char key[64]; + + for (size_t idx = 0; idx < iterations; idx++) { + snprintf(key, sizeof(key), "key_%zu", idx); + + int *value = malloc(sizeof(int)); + *value = (int)idx; + + map_add(map, key, (void*)value); + } + + volatile uint64_t sum = 0; // prevent the compiler from optimizing away the sum + for (size_t idx = 0; idx < iterations; idx++) { + snprintf(key, sizeof(key), "key_%zu", idx); + + const int *val = (const int*)map_get(map, key).value.element; + sum += *val; + } + + // Cleanup values + for (size_t idx = 0; idx < map->capacity; idx++) { + if (map->elements[idx].state == ENTRY_OCCUPIED) { + int *val = (int*)map->elements[idx].value; + free(val); + } + } + + map_destroy(map); +} + +long long benchmark(test_fn_t fun, size_t iterations, size_t runs) { + long long total = 0; + for (size_t idx = 0; idx < runs; idx++) { + clock_t start = clock(); + fun(iterations); + clock_t end = clock(); + + total += (long long)((end - start) * 1000 / CLOCKS_PER_SEC); + } + + return total / runs; +} + +int main(void) { + // Do a warmup run + test_vector(1000); + test_map(1000); + + printf("Computing Vector average time..."); + fflush(stdout); + printf("average time: %lld ms\n", benchmark(test_vector, 1e6, 30)); + + printf("Computing Map average time..."); + fflush(stdout); + printf("average time: %lld ms\n", benchmark(test_map, 1e5, 30)); + + return 0; +} \ No newline at end of file