diff --git a/tests/Makefile.am b/tests/Makefile.am index 68383d6..7a96f8e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -17,7 +17,9 @@ check_PROGRAMS = test_etcp_crypto$(EXEEXT) \ test_u_async_comprehensive$(EXEEXT) \ test_u_async_performance$(EXEEXT) \ test_debug_categories$(EXEEXT) \ - test_config_debug$(EXEEXT) + test_config_debug$(EXEEXT) \ + bench_timeout_heap$(EXEEXT) \ + bench_uasync_timeouts$(EXEEXT) # Basic includes AM_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source @@ -102,5 +104,15 @@ test_config_debug_SOURCES = test_config_debug.c test_config_debug_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_config_debug_LDADD = $(top_builddir)/src/utun-config_parser.o $(top_builddir)/lib/libuasync.a -lpthread -lcrypto +# Timeout heap benchmark +bench_timeout_heap_SOURCES = bench_timeout_heap.c +bench_timeout_heap_CFLAGS = -I$(top_srcdir)/lib +bench_timeout_heap_LDADD = $(top_builddir)/lib/libuasync.a -lpthread -lcrypto + +# uasync zero timeout benchmark +bench_uasync_timeouts_SOURCES = bench_uasync_timeouts.c +bench_uasync_timeouts_CFLAGS = -I$(top_srcdir)/lib +bench_uasync_timeouts_LDADD = $(top_builddir)/lib/libuasync.a -lpthread -lcrypto + # Register tests TESTS = $(check_PROGRAMS) \ No newline at end of file diff --git a/tests/bench_timeout_heap.c b/tests/bench_timeout_heap.c new file mode 100644 index 0000000..1ed7648 --- /dev/null +++ b/tests/bench_timeout_heap.c @@ -0,0 +1,114 @@ +// bench_timeout_heap.c - Benchmark for timeout_heap operations + +#include +#include +#include +#include "timeout_heap.h" + +#define NUM_OPERATIONS 100 +#define MAX_TIMEOUT_VALUE 1000000ULL + +static double timespec_to_ms(struct timespec *ts) { + return ts->tv_sec * 1000.0 + ts->tv_nsec / 1000000.0; +} + +static double measure_time_diff(struct timespec *start, struct timespec *end) { + struct timespec diff; + if (end->tv_nsec < start->tv_nsec) { + diff.tv_sec = end->tv_sec - start->tv_sec - 1; + diff.tv_nsec = end->tv_nsec + 1000000000L - start->tv_nsec; + } else { + diff.tv_sec = end->tv_sec - start->tv_sec; + diff.tv_nsec = end->tv_nsec - start->tv_nsec; + } + return timespec_to_ms(&diff); +} + +int main(void) { + TimeoutHeap *heap; + struct timespec start, end; + double push_time, cancel_time, pop_time; + TimeoutTime timeouts[NUM_OPERATIONS]; + void *data[NUM_OPERATIONS]; + int i; + + printf("=== timeout_heap benchmark ===\n"); + printf("Operations: %d each\n\n", NUM_OPERATIONS); + + // Seed random + srand((unsigned int)time(NULL)); + + // Create heap + heap = timeout_heap_create(16); + if (!heap) { + fprintf(stderr, "Failed to create heap\n"); + return 1; + } + + // Generate random timeouts and data pointers + for (i = 0; i < NUM_OPERATIONS; i++) { + timeouts[i] = (TimeoutTime)(rand() % MAX_TIMEOUT_VALUE); + data[i] = (void *)(uintptr_t)(i + 1); + } + + // Benchmark 1: 100 random pushes + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_OPERATIONS; i++) { + if (timeout_heap_push(heap, timeouts[i], data[i]) != 0) { + fprintf(stderr, "Push failed at iteration %d\n", i); + timeout_heap_destroy(heap); + return 1; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + push_time = measure_time_diff(&start, &end); + + printf("Push %d items:\n", NUM_OPERATIONS); + printf(" Total time: %.3f ms\n", push_time); + printf(" Average per push: %.3f us\n", (push_time * 1000.0) / NUM_OPERATIONS); + printf(" Heap size after: %zu\n\n", heap->size); + + // Benchmark 2: 100 random cancels + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_OPERATIONS; i++) { + // Cancel every other item (50% of items) + if (i % 2 == 0) { + timeout_heap_cancel(heap, timeouts[i], data[i]); + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + cancel_time = measure_time_diff(&start, &end); + + printf("Cancel %d items:\n", NUM_OPERATIONS / 2); + printf(" Total time: %.3f ms\n", cancel_time); + printf(" Average per cancel: %.3f us\n", (cancel_time * 1000.0) / (NUM_OPERATIONS / 2)); + printf(" Heap size: %zu (deleted marked)\n\n", heap->size); + + // Benchmark 3: 100 pops (extract all remaining) + int pop_count = 0; + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_OPERATIONS; i++) { + TimeoutEntry entry; + if (timeout_heap_pop(heap, &entry) != 0) { + break; // Heap empty + } + pop_count++; + } + clock_gettime(CLOCK_MONOTONIC, &end); + pop_time = measure_time_diff(&start, &end); + + printf("Pop %d items:\n", pop_count); + printf(" Total time: %.3f ms\n", pop_time); + printf(" Average per pop: %.3f us\n", (pop_time * 1000.0) / pop_count); + printf(" Heap size after: %zu\n\n", heap->size); + + // Summary + printf("=== Summary ===\n"); + printf("Push: %.3f ms total, %.3f us/op\n", push_time, (push_time * 1000.0) / NUM_OPERATIONS); + printf("Cancel: %.3f ms total, %.3f us/op\n", cancel_time, (cancel_time * 1000.0) / (NUM_OPERATIONS / 2)); + printf("Pop: %.3f ms total, %.3f us/op\n", pop_time, (pop_time * 1000.0) / pop_count); + printf("\nTotal operations: %d\n", NUM_OPERATIONS * 2 + pop_count); + + timeout_heap_destroy(heap); + return 0; +} diff --git a/tests/bench_uasync_timeouts.c b/tests/bench_uasync_timeouts.c new file mode 100644 index 0000000..4daf5a6 --- /dev/null +++ b/tests/bench_uasync_timeouts.c @@ -0,0 +1,312 @@ +// bench_uasync_timeouts.c - Benchmark for uasync zero timeouts with and without sockets + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "u_async.h" + +#define NUM_TIMEOUTS 100 +#define NUM_SOCKETS 100 + +static double timespec_to_ms(struct timespec *ts) { + return ts->tv_sec * 1000.0 + ts->tv_nsec / 1000000.0; +} + +static double measure_time_diff(struct timespec *start, struct timespec *end) { + struct timespec diff; + if (end->tv_nsec < start->tv_nsec) { + diff.tv_sec = end->tv_sec - start->tv_sec - 1; + diff.tv_nsec = end->tv_nsec + 1000000000L - start->tv_nsec; + } else { + diff.tv_sec = end->tv_sec - start->tv_sec; + diff.tv_nsec = end->tv_nsec - start->tv_nsec; + } + return timespec_to_ms(&diff); +} + +static void timeout_callback(void* user_arg) { + (void)user_arg; +} + +static void socket_callback(int fd, void* user_arg) { + (void)fd; + (void)user_arg; +} + +static void benchmark_without_sockets(void) { + uasync_t* ua; + struct timespec start, end; + double set_time, mainloop_time; + void* timeout_ids[NUM_TIMEOUTS]; + int i; + + printf("=== Test 1: Zero timeouts WITHOUT sockets ===\n\n"); + + ua = uasync_create(); + if (!ua) { + fprintf(stderr, "Failed to create uasync\n"); + return; + } + + // Set 100 zero timeouts + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_TIMEOUTS; i++) { + timeout_ids[i] = uasync_set_timeout(ua, 0, (void*)(uintptr_t)(i + 1), timeout_callback); + if (!timeout_ids[i]) { + fprintf(stderr, "Failed to set timeout at iteration %d\n", i); + uasync_destroy(ua, 0); + return; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + set_time = measure_time_diff(&start, &end); + + printf("Set %d zero timeouts:\n", NUM_TIMEOUTS); + printf(" Total time: %.3f ms\n", set_time); + printf(" Average per timeout: %.3f us\n", (set_time * 1000.0) / NUM_TIMEOUTS); + printf(" Heap size: %zu\n\n", ua->timeout_heap->size); + + // Run mainloop 100 times + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_TIMEOUTS; i++) { + uasync_poll(ua, 0); + } + clock_gettime(CLOCK_MONOTONIC, &end); + mainloop_time = measure_time_diff(&start, &end); + + printf("Run mainloop (uasync_poll) %d times:\n", NUM_TIMEOUTS); + printf(" Total time: %.3f ms\n", mainloop_time); + printf(" Average per iteration: %.3f us\n", (mainloop_time * 1000.0) / NUM_TIMEOUTS); + printf(" Heap size after: %zu\n\n", ua->timeout_heap->size); + + printf("--- Results WITHOUT sockets ---\n"); + printf("Set timeouts: %.3f ms (%.3f us/op)\n", set_time, (set_time * 1000.0) / NUM_TIMEOUTS); + printf("Mainloop: %.3f ms (%.3f us/iter)\n\n", mainloop_time, (mainloop_time * 1000.0) / NUM_TIMEOUTS); + + uasync_destroy(ua, 0); +} + +static void benchmark_with_sockets(void) { + uasync_t* ua; + struct timespec start, end; + double set_time, mainloop_time, socket_time; + void* timeout_ids[NUM_TIMEOUTS]; + int sockets[NUM_SOCKETS]; + void* socket_ids[NUM_SOCKETS]; + int i; + + printf("=== Test 2: Zero timeouts WITH %d sockets ===\n\n", NUM_SOCKETS); + + ua = uasync_create(); + if (!ua) { + fprintf(stderr, "Failed to create uasync\n"); + return; + } + + // Create and add 100 sockets FIRST + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_SOCKETS; i++) { + sockets[i] = socket(AF_INET, SOCK_DGRAM, 0); + if (sockets[i] < 0) { + fprintf(stderr, "Failed to create socket %d\n", i); + for (int j = 0; j < i; j++) { + close(sockets[j]); + } + uasync_destroy(ua, 0); + return; + } + int flags = fcntl(sockets[i], F_GETFL, 0); + fcntl(sockets[i], F_SETFL, flags | O_NONBLOCK); + + socket_ids[i] = uasync_add_socket(ua, sockets[i], socket_callback, NULL, NULL, NULL); + if (!socket_ids[i]) { + fprintf(stderr, "Failed to add socket %d to uasync\n", i); + for (int j = 0; j <= i; j++) { + close(sockets[j]); + } + uasync_destroy(ua, 0); + return; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + socket_time = measure_time_diff(&start, &end); + + printf("Create and add %d sockets:\n", NUM_SOCKETS); + printf(" Total time: %.3f ms\n", socket_time); + printf(" Average per socket: %.3f us\n", (socket_time * 1000.0) / NUM_SOCKETS); + printf(" Active sockets: %zu\n\n", ua->socket_alloc_count - ua->socket_free_count); + + // Set 100 zero timeouts WITH sockets already open + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_TIMEOUTS; i++) { + timeout_ids[i] = uasync_set_timeout(ua, 0, (void*)(uintptr_t)(i + 1), timeout_callback); + if (!timeout_ids[i]) { + fprintf(stderr, "Failed to set timeout at iteration %d\n", i); + for (int j = 0; j < NUM_SOCKETS; j++) { + close(sockets[j]); + } + uasync_destroy(ua, 0); + return; + } + } + clock_gettime(CLOCK_MONOTONIC, &end); + set_time = measure_time_diff(&start, &end); + + printf("Set %d zero timeouts (with %d sockets open):\n", NUM_TIMEOUTS, NUM_SOCKETS); + printf(" Total time: %.3f ms\n", set_time); + printf(" Average per timeout: %.3f us\n", (set_time * 1000.0) / NUM_TIMEOUTS); + printf(" Heap size: %zu\n\n", ua->timeout_heap->size); + + // Run mainloop 100 times WITH sockets + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_TIMEOUTS; i++) { + uasync_poll(ua, 0); + } + clock_gettime(CLOCK_MONOTONIC, &end); + mainloop_time = measure_time_diff(&start, &end); + + printf("Run mainloop (uasync_poll) %d times (with sockets):\n", NUM_TIMEOUTS); + printf(" Total time: %.3f ms\n", mainloop_time); + printf(" Average per iteration: %.3f us\n", (mainloop_time * 1000.0) / NUM_TIMEOUTS); + printf(" Heap size after: %zu\n\n", ua->timeout_heap->size); + + printf("--- Results WITH %d sockets ---\n", NUM_SOCKETS); + printf("Set timeouts: %.3f ms (%.3f us/op)\n", set_time, (set_time * 1000.0) / NUM_TIMEOUTS); + printf("Mainloop: %.3f ms (%.3f us/iter)\n\n", mainloop_time, (mainloop_time * 1000.0) / NUM_TIMEOUTS); + + for (i = 0; i < NUM_SOCKETS; i++) { + close(sockets[i]); + } + uasync_destroy(ua, 0); +} + +int main(void) { + double set_time_no_sockets = 0, mainloop_time_no_sockets = 0; + double set_time_with_sockets = 0, mainloop_time_with_sockets = 0; + + printf("=================================================\n"); + printf("uasync timeout benchmark: with vs without sockets\n"); + printf("=================================================\n\n"); + + // Test 1: Without sockets + { + uasync_t* ua; + struct timespec start, end; + void* timeout_ids[NUM_TIMEOUTS]; + int i; + + printf("=== Test 1: Zero timeouts WITHOUT sockets ===\n\n"); + + ua = uasync_create(); + if (!ua) { + fprintf(stderr, "Failed to create uasync\n"); + return 1; + } + + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_TIMEOUTS; i++) { + timeout_ids[i] = uasync_set_timeout(ua, 0, (void*)(uintptr_t)(i + 1), timeout_callback); + } + clock_gettime(CLOCK_MONOTONIC, &end); + set_time_no_sockets = measure_time_diff(&start, &end); + + printf("Set %d zero timeouts: %.3f ms (%.3f us/op)\n", + NUM_TIMEOUTS, set_time_no_sockets, (set_time_no_sockets * 1000.0) / NUM_TIMEOUTS); + + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_TIMEOUTS; i++) { + uasync_poll(ua, 0); + } + clock_gettime(CLOCK_MONOTONIC, &end); + mainloop_time_no_sockets = measure_time_diff(&start, &end); + + printf("Mainloop %d iterations: %.3f ms (%.3f us/iter)\n\n", + NUM_TIMEOUTS, mainloop_time_no_sockets, (mainloop_time_no_sockets * 1000.0) / NUM_TIMEOUTS); + + uasync_destroy(ua, 0); + } + + // Test 2: With sockets + { + uasync_t* ua; + struct timespec start, end; + void* timeout_ids[NUM_TIMEOUTS]; + int sockets[NUM_SOCKETS]; + void* socket_ids[NUM_SOCKETS]; + int i; + + printf("=== Test 2: Zero timeouts WITH %d sockets ===\n\n", NUM_SOCKETS); + + ua = uasync_create(); + if (!ua) { + fprintf(stderr, "Failed to create uasync\n"); + return 1; + } + + // Create sockets FIRST + for (i = 0; i < NUM_SOCKETS; i++) { + sockets[i] = socket(AF_INET, SOCK_DGRAM, 0); + int flags = fcntl(sockets[i], F_GETFL, 0); + fcntl(sockets[i], F_SETFL, flags | O_NONBLOCK); + socket_ids[i] = uasync_add_socket(ua, sockets[i], socket_callback, NULL, NULL, NULL); + } + printf("Created %d sockets\n\n", NUM_SOCKETS); + + // Set timeouts WITH sockets open + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_TIMEOUTS; i++) { + timeout_ids[i] = uasync_set_timeout(ua, 0, (void*)(uintptr_t)(i + 1), timeout_callback); + } + clock_gettime(CLOCK_MONOTONIC, &end); + set_time_with_sockets = measure_time_diff(&start, &end); + + printf("Set %d zero timeouts: %.3f ms (%.3f us/op)\n", + NUM_TIMEOUTS, set_time_with_sockets, (set_time_with_sockets * 1000.0) / NUM_TIMEOUTS); + + // Mainloop WITH sockets + clock_gettime(CLOCK_MONOTONIC, &start); + for (i = 0; i < NUM_TIMEOUTS; i++) { + uasync_poll(ua, 0); + } + clock_gettime(CLOCK_MONOTONIC, &end); + mainloop_time_with_sockets = measure_time_diff(&start, &end); + + printf("Mainloop %d iterations: %.3f ms (%.3f us/iter)\n\n", + NUM_TIMEOUTS, mainloop_time_with_sockets, (mainloop_time_with_sockets * 1000.0) / NUM_TIMEOUTS); + + for (i = 0; i < NUM_SOCKETS; i++) { + close(sockets[i]); + } + uasync_destroy(ua, 0); + } + + // Comparison + printf("=================================================\n"); + printf("COMPARISON\n"); + printf("=================================================\n"); + printf("\nSet %d timeouts:\n", NUM_TIMEOUTS); + printf(" Without sockets: %.3f ms (%.3f us/op)\n", + set_time_no_sockets, (set_time_no_sockets * 1000.0) / NUM_TIMEOUTS); + printf(" With %d sockets: %.3f ms (%.3f us/op)\n", + NUM_SOCKETS, set_time_with_sockets, (set_time_with_sockets * 1000.0) / NUM_TIMEOUTS); + printf(" Difference: %.2f%%\n", + ((set_time_with_sockets - set_time_no_sockets) / set_time_no_sockets) * 100.0); + + printf("\nMainloop %d iterations:\n", NUM_TIMEOUTS); + printf(" Without sockets: %.3f ms (%.3f us/iter)\n", + mainloop_time_no_sockets, (mainloop_time_no_sockets * 1000.0) / NUM_TIMEOUTS); + printf(" With %d sockets: %.3f ms (%.3f us/iter)\n", + NUM_SOCKETS, mainloop_time_with_sockets, (mainloop_time_with_sockets * 1000.0) / NUM_TIMEOUTS); + printf(" Difference: %.2f%%\n", + ((mainloop_time_with_sockets - mainloop_time_no_sockets) / mainloop_time_no_sockets) * 100.0); + + printf("\n=================================================\n"); + + return 0; +}