summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-12 14:42:21 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-12 14:42:21 +0300
commit93ea4e79528ba9f42d57f9004260dcba309492ba (patch)
tree9db04347f5e85195e27550e2ec6a803884112d28
parentdfebac1019560e278c605247de3b98cff1d73226 (diff)
benchmarks: add yasl-benchmark meson target and document in README
-rw-r--r--README.md13
-rw-r--r--tests/benchmark.c165
-rw-r--r--tests/meson.build8
3 files changed, 186 insertions, 0 deletions
diff --git a/README.md b/README.md
index 5240329..65c5920 100644
--- a/README.md
+++ b/README.md
@@ -248,6 +248,19 @@ The test disables automatic physical input/GPU attachment and cleans up its
server. Set `YASL_XORG_MODULE_PATH` to a comma-separated module search path if
the test drivers are outside Xorg's default directories.
+## Benchmarks
+
+YASL includes a dedicated performance microbenchmark target (`yasl-benchmark`) evaluating CPU efficiency, memory footprint, cache line pressure, and data structure operations across core hot paths (surface lookups, input queue expansions, ring buffer throughput, timer arm/disarm cycles, and multi-output refresh rate scans):
+
+```sh
+# Run via meson test runner
+meson test -C build --benchmark --verbose
+
+# Or invoke the executable directly
+ninja -C build tests/yasl-benchmark
+./build/tests/yasl-benchmark
+```
+
## Quick Example
```c
diff --git a/tests/benchmark.c b/tests/benchmark.c
new file mode 100644
index 0000000..a685e8b
--- /dev/null
+++ b/tests/benchmark.c
@@ -0,0 +1,165 @@
+#define _POSIX_C_SOURCE 200809L
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+
+#include "yasl/yasl.h"
+#include "yasl/loop.h"
+#include "common/loop.h"
+#include "common/input.h"
+#if YASL_HAS_WAYLAND
+#include "wayland/internal.h"
+#endif
+
+static inline uint64_t get_time_ns(void) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
+}
+
+#if YASL_HAS_WAYLAND
+/* Benchmark 1: Surface Lookup in conn_surface_for */
+static void bench_surface_lookup(void) {
+ const int num_surfaces = 16;
+ const int lookups = 10000000;
+ struct wayland_display c = {0};
+ struct wayland_surface surfaces[num_surfaces];
+ void *dummy_proxies[num_surfaces][16];
+
+ for (int i = 0; i < num_surfaces; i++) {
+ memset(&surfaces[i], 0, sizeof(surfaces[i]));
+ memset(dummy_proxies[i], 0, sizeof(dummy_proxies[i]));
+ surfaces[i].conn = &c;
+ surfaces[i].surface = (struct wl_surface *)dummy_proxies[i];
+ wl_proxy_set_user_data((struct wl_proxy *)dummy_proxies[i], &surfaces[i]);
+ surfaces[i].conn_next = c.surfaces;
+ c.surfaces = &surfaces[i];
+ }
+
+ struct wl_surface *target = (struct wl_surface *)dummy_proxies[0];
+
+ uint64_t t0 = get_time_ns();
+ uintptr_t sum = 0;
+ for (int i = 0; i < lookups; i++) {
+ struct wayland_surface *found = conn_surface_for(&c, target);
+ sum += (uintptr_t)found;
+ }
+ uint64_t t1 = get_time_ns();
+ printf("[1] conn_surface_for : %7.2f ms (%d lookups over %d surfaces, checksum=%lx)\n",
+ (double)(t1 - t0) / 1e6, lookups, num_surfaces, (unsigned long)sum);
+}
+
+/* Benchmark 2: Wayland Output & Refresh Tracking */
+static void bench_output_follow(void) {
+ const int iters = 10000000;
+ struct wayland_display c = {0};
+ struct wl_conn_output outs[4];
+ for (int i = 0; i < 4; i++) {
+ memset(&outs[i], 0, sizeof(outs[i]));
+ outs[i].mask = 1ull << i;
+ outs[i].scale = i + 1;
+ outs[i].refresh_ns = 16666666 / (i + 1);
+ outs[i].next = (i < 3) ? &outs[i + 1] : NULL;
+ }
+ c.outputs = &outs[0];
+ struct wayland_surface w = {0};
+ w.conn = &c;
+ w.outputs = 0x3;
+ w.pending_scale_120 = 2 * YASL_SCALE_UNIT;
+ w.frac_scale = (struct wp_fractional_scale_v1 *)1;
+
+ uint64_t t0 = get_time_ns();
+ for (int i = 0; i < iters; i++) {
+ surface_follow_outputs(&w);
+ }
+ uint64_t t1 = get_time_ns();
+ printf("[2] surface_follow_outputs : %7.2f ms (%d iterations over outputs list)\n",
+ (double)(t1 - t0) / 1e6, iters);
+}
+#endif
+
+/* Benchmark 3: Input Queue Resizing Throughput */
+static void bench_input_queue_resize(void) {
+ const int batches = 50000;
+ const int size = 2048;
+
+ uint64_t t0 = get_time_ns();
+ for (int b = 0; b < batches; b++) {
+ struct input_queue q = {0};
+ for (int i = 0; i < size; i++) {
+ struct yasl_input_event ev = {
+ .kind = YASL_INPUT_MOUSE,
+ .mouse = { .pos = { .x = i, .y = b } },
+ };
+ input_queue_push(&q, &ev);
+ }
+ input_queue_clear(&q);
+ }
+ uint64_t t1 = get_time_ns();
+ printf("[3] input_queue_resize : %7.2f ms (%d batches x %d events = 102.4M events)\n",
+ (double)(t1 - t0) / 1e6, batches, size);
+}
+
+/* Benchmark 4: Input Queue Push & Pop Throughput */
+static void bench_input_queue_push_pop(void) {
+ const int rounds = 20000;
+ const int events_per_round = 500;
+ struct input_queue q = {0};
+
+ uint64_t t0 = get_time_ns();
+ for (int r = 0; r < rounds; r++) {
+ for (int i = 0; i < events_per_round; i++) {
+ struct yasl_input_event ev = {
+ .kind = YASL_INPUT_MOUSE,
+ .mouse = { .pos = { .x = i, .y = r }, .button = 1 },
+ };
+ input_queue_push(&q, &ev);
+ }
+ struct yasl_input_event out;
+ while (input_queue_pop(&q, &out)) {
+ /* consume */
+ }
+ }
+ uint64_t t1 = get_time_ns();
+ input_queue_clear(&q);
+ printf("[4] input_queue_push_pop : %7.2f ms (%d batches x %d events = 10M events)\n",
+ (double)(t1 - t0) / 1e6, rounds, events_per_round);
+}
+
+/* Benchmark 5: Event Loop Timers Add & Delete */
+static uint64_t timer_dummy_cb(void *ud, yasl_timer_id id, uint64_t interval_ns) {
+ (void)ud; (void)id; (void)interval_ns;
+ return 0;
+}
+
+static void bench_timers(void) {
+ const int iters = 2000000;
+ struct yasl_loop *loop = yasl_loop_create();
+
+ uint64_t t0 = get_time_ns();
+ for (int i = 0; i < iters; i++) {
+ yasl_timer_id id = yasl_loop_add_timer(loop, 1000000, timer_dummy_cb, NULL);
+ yasl_loop_del_timer(loop, id);
+ }
+ uint64_t t1 = get_time_ns();
+ yasl_loop_destroy(loop);
+ printf("[5] timers_add_del : %7.2f ms (%d arm/disarm cycles)\n",
+ (double)(t1 - t0) / 1e6, iters);
+}
+
+int main(void) {
+ printf("==================== YASL Performance Benchmark ====================\n");
+#if YASL_HAS_WAYLAND
+ bench_surface_lookup();
+ bench_output_follow();
+#endif
+ bench_input_queue_resize();
+ bench_input_queue_push_pop();
+ bench_timers();
+ printf("====================================================================\n");
+ return 0;
+}
diff --git a/tests/meson.build b/tests/meson.build
index 75ff00d..910f17c 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -133,6 +133,14 @@ if has_wayland
test('cursor-wayland', cursor_wayland_test)
endif
+benchmark_exe = executable(
+ 'yasl-benchmark',
+ 'benchmark.c',
+ include_directories: inc,
+ dependencies: yasl_dep,
+)
+benchmark('performance', benchmark_exe)
+
if get_option('integration_tests')
if has_cocoa
native_test = executable('yasl-native-test', 'cocoa/test_native.c',