summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--tests/benchmark.c309
2 files changed, 298 insertions, 13 deletions
diff --git a/README.md b/README.md
index 65c5920..0cb2eea 100644
--- a/README.md
+++ b/README.md
@@ -250,7 +250,7 @@ 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):
+YASL includes a dedicated performance microbenchmark target (`yasl-benchmark`) evaluating CPU efficiency, memory footprint, cache line pressure, data structure operations, and window lifecycle operations across core hot paths (surface lookups, multi-output refresh rate scans, input queue expansions, ring buffer throughput, timer arm/disarm cycles, cold/warm window creation, single and bulk window teardowns, titlebar property updates, and multi-resolution dynamic resizing):
```sh
# Run via meson test runner
diff --git a/tests/benchmark.c b/tests/benchmark.c
index 7687316..eecccc5 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -22,6 +22,28 @@ static inline uint64_t get_time_ns(void) {
return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
}
+static void format_duration(char *buf, size_t buf_sz, double ns) {
+ if (ns < 1000.0) {
+ snprintf(buf, buf_sz, "%6.2f ns", ns);
+ } else if (ns < 1000000.0) {
+ snprintf(buf, buf_sz, "%6.2f us", ns / 1e3);
+ } else if (ns < 1000000000.0) {
+ snprintf(buf, buf_sz, "%6.2f ms", ns / 1e6);
+ } else {
+ snprintf(buf, buf_sz, "%6.2f s ", ns / 1e9);
+ }
+}
+
+static void print_bench_row(const char *name, double per_cycle_ns, double total_wall_ms, const char *details) {
+ char per_cycle_str[32];
+ format_duration(per_cycle_str, sizeof(per_cycle_str), per_cycle_ns);
+ printf("%-29s | %10s | %9.2f ms | %s\n", name, per_cycle_str, total_wall_ms, details);
+}
+
+static void print_bench_skipped(const char *name, const char *reason) {
+ printf("%-29s | skipped | - | %s\n", name, reason);
+}
+
#if YASL_HAS_WAYLAND
/* Benchmark 1: Public Wayland Surface Handle Lookup */
static void bench_surface_lookup(void) {
@@ -52,8 +74,12 @@ static void bench_surface_lookup(void) {
sum += (uintptr_t)found;
}
uint64_t t1 = get_time_ns();
- printf("[1] wayland_surface_lookup : %7.2f ms (%d lookups over %d surfaces, checksum=%lx)\n",
- (double)(t1 - t0) / 1e6, lookups, num_surfaces, (unsigned long)sum);
+ double ms = (double)(t1 - t0) / 1e6;
+ double per_cycle = (double)(t1 - t0) / lookups;
+ char details[128];
+ snprintf(details, sizeof(details), "%d lookups over %d surfaces (checksum=%lx)",
+ lookups, num_surfaces, (unsigned long)sum);
+ print_bench_row("[1] wayland_surface_lookup", per_cycle, ms, details);
if (d) {
for (int i = 0; i < num_surfaces; i++) {
@@ -89,8 +115,12 @@ static void bench_output_follow(void) {
sum += (uint64_t)dev + timing.refresh_ns;
}
uint64_t t1 = get_time_ns();
- printf("[2] surface_geometry_timing : %7.2f ms (%d iterations, checksum=%llu)\n",
- (double)(t1 - t0) / 1e6, iters, (unsigned long long)sum);
+ double ms = (double)(t1 - t0) / 1e6;
+ double per_cycle = (double)(t1 - t0) / iters;
+ char details[128];
+ snprintf(details, sizeof(details), "%d iterations (checksum=%llu)",
+ iters, (unsigned long long)sum);
+ print_bench_row("[2] surface_geometry_timing", per_cycle, ms, details);
if (s) yasl_surface_destroy(s);
if (d) yasl_display_close(d);
@@ -115,8 +145,11 @@ static void bench_input_queue_resize(void) {
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);
+ double ms = (double)(t1 - t0) / 1e6;
+ double per_cycle = (double)(t1 - t0) / ((double)batches * size);
+ char details[128];
+ snprintf(details, sizeof(details), "%d batches x %d events (102.4M total)", batches, size);
+ print_bench_row("[3] input_queue_resize", per_cycle, ms, details);
}
/* Benchmark 4: Input Queue Push & Pop Throughput */
@@ -141,8 +174,11 @@ static void bench_input_queue_push_pop(void) {
}
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);
+ double ms = (double)(t1 - t0) / 1e6;
+ double per_cycle = (double)(t1 - t0) / ((double)rounds * events_per_round);
+ char details[128];
+ snprintf(details, sizeof(details), "%d rounds x %d events (10.0M total)", rounds, events_per_round);
+ print_bench_row("[4] input_queue_push_pop", per_cycle, ms, details);
}
/* Benchmark 5: Event Loop Timers Add & Delete */
@@ -162,12 +198,255 @@ static void bench_timers(void) {
}
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);
+ double ms = (double)(t1 - t0) / 1e6;
+ double per_cycle = (double)(t1 - t0) / iters;
+ char details[128];
+ snprintf(details, sizeof(details), "%d arm/disarm cycles", iters);
+ print_bench_row("[5] timers_add_del", per_cycle, ms, details);
+}
+
+/* Benchmark 6: Window Cold Create & Teardown */
+static void bench_window_cold_create(void) {
+ const int iters = 200;
+ struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.cold" };
+ struct yasl_surface_spec sspec = {
+ .title = "Benchmark Cold",
+ .size = { .x = 320, .y = 240 },
+ };
+
+ struct yasl_display *probe = yasl_display_open(NULL, &dspec);
+ if (!probe) {
+ print_bench_skipped("[6] window_cold_create", "no display server");
+ return;
+ }
+ yasl_display_close(probe);
+
+ uint64_t t0 = get_time_ns();
+ int count = 0;
+ for (int i = 0; i < iters; i++) {
+ struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ if (d) {
+ struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ if (s) {
+ yasl_surface_destroy(s);
+ count++;
+ }
+ yasl_display_close(d);
+ }
+ }
+ uint64_t t1 = get_time_ns();
+ double ms = (double)(t1 - t0) / 1e6;
+ double per_cycle = count ? (double)(t1 - t0) / count : 0.0;
+ char details[128];
+ snprintf(details, sizeof(details), "%d open/create/destroy/close cycles", count);
+ print_bench_row("[6] window_cold_create", per_cycle, ms, details);
+}
+
+/* Benchmark 7: Window Warm Create */
+static void bench_window_warm_create(void) {
+ const int iters = 2000;
+ struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.warm" };
+ struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ if (!d) {
+ print_bench_skipped("[7] window_warm_create", "no display server");
+ return;
+ }
+
+ struct yasl_surface_spec sspec = {
+ .title = "Benchmark Warm",
+ .size = { .x = 320, .y = 240 },
+ };
+
+ uint64_t t0 = get_time_ns();
+ int count = 0;
+ for (int i = 0; i < iters; i++) {
+ struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ if (s) {
+ yasl_surface_destroy(s);
+ count++;
+ }
+ }
+ uint64_t t1 = get_time_ns();
+ yasl_display_close(d);
+ double ms = (double)(t1 - t0) / 1e6;
+ double per_cycle = count ? (double)(t1 - t0) / count : 0.0;
+ char details[128];
+ snprintf(details, sizeof(details), "%d surface create/destroy cycles", count);
+ print_bench_row("[7] window_warm_create", per_cycle, ms, details);
+}
+
+/* Benchmark 8: Window Shutdown (Single) */
+static void bench_window_shutdown_single(void) {
+ const int iters = 2000;
+ struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.shutdown_single" };
+ struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ if (!d) {
+ print_bench_skipped("[8] window_shutdown_single", "no display server");
+ return;
+ }
+
+ struct yasl_surface_spec sspec = {
+ .title = "Benchmark Shutdown Single",
+ .size = { .x = 320, .y = 240 },
+ };
+
+ uint64_t destroy_time_ns = 0;
+ int count = 0;
+ for (int i = 0; i < iters; i++) {
+ struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ if (s) {
+ uint64_t t0 = get_time_ns();
+ yasl_surface_destroy(s);
+ uint64_t t1 = get_time_ns();
+ destroy_time_ns += (t1 - t0);
+ count++;
+ }
+ }
+ yasl_display_close(d);
+ double ms = (double)destroy_time_ns / 1e6;
+ double per_cycle = count ? (double)destroy_time_ns / count : 0.0;
+ char details[128];
+ snprintf(details, sizeof(details), "%d isolated surface destructions", count);
+ print_bench_row("[8] window_shutdown_single", per_cycle, ms, details);
+}
+
+/* Benchmark 9: Window Shutdown (Bulk / All) */
+static void bench_window_shutdown_all(void) {
+ const int batches = 50;
+ const int batch_size = 64;
+ struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.shutdown_all" };
+ struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ if (!d) {
+ print_bench_skipped("[9] window_shutdown_all", "no display server");
+ return;
+ }
+
+ struct yasl_surface_spec sspec = {
+ .title = "Benchmark Bulk Window",
+ .size = { .x = 320, .y = 240 },
+ };
+
+ struct yasl_surface *surfaces[64];
+ uint64_t destroy_time_ns = 0;
+ int total_destroyed = 0;
+
+ for (int b = 0; b < batches; b++) {
+ for (int i = 0; i < batch_size; i++) {
+ surfaces[i] = yasl_surface_create_toplevel(d, &sspec);
+ }
+ uint64_t t0 = get_time_ns();
+ for (int i = 0; i < batch_size; i++) {
+ if (surfaces[i]) {
+ yasl_surface_destroy(surfaces[i]);
+ surfaces[i] = NULL;
+ total_destroyed++;
+ }
+ }
+ uint64_t t1 = get_time_ns();
+ destroy_time_ns += (t1 - t0);
+ }
+ yasl_display_close(d);
+ double ms = (double)destroy_time_ns / 1e6;
+ double per_cycle = total_destroyed ? (double)destroy_time_ns / total_destroyed : 0.0;
+ double per_batch_ns = batches ? (double)destroy_time_ns / batches : 0.0;
+ char per_batch_str[32];
+ format_duration(per_batch_str, sizeof(per_batch_str), per_batch_ns);
+ char details[128];
+ snprintf(details, sizeof(details), "%d windows (%d batches x %d, %s/batch)",
+ total_destroyed, batches, batch_size, per_batch_str);
+ print_bench_row("[9] window_shutdown_all", per_cycle, ms, details);
+}
+
+/* Benchmark 10: Window Titlebar Update */
+static void bench_window_title_update(void) {
+ const int iters = 200000;
+ struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.title" };
+ struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ if (!d) {
+ print_bench_skipped("[10] window_titlebar_update", "no display server");
+ return;
+ }
+
+ struct yasl_surface_spec sspec = {
+ .title = "Initial Title",
+ .size = { .x = 320, .y = 240 },
+ };
+ struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ if (!s) {
+ yasl_display_close(d);
+ print_bench_skipped("[10] window_titlebar_update", "failed to create surface");
+ return;
+ }
+
+ const char *titles[4] = {
+ "YASL Benchmark - State 1",
+ "YASL Benchmark - State 2",
+ "Document Editor - Untitled",
+ "Terminal - Session Active"
+ };
+
+ uint64_t t0 = get_time_ns();
+ for (int i = 0; i < iters; i++) {
+ yasl_surface_set_title(s, titles[i & 3]);
+ }
+ uint64_t t1 = get_time_ns();
+ yasl_surface_destroy(s);
+ yasl_display_close(d);
+ double ms = (double)(t1 - t0) / 1e6;
+ double per_cycle = (double)(t1 - t0) / iters;
+ char details[128];
+ snprintf(details, sizeof(details), "%d title changes", iters);
+ print_bench_row("[10] window_titlebar_update", per_cycle, ms, details);
+}
+
+/* Benchmark 11: Window Resize / Dimension Reallocation */
+static void bench_window_resize(void) {
+ const int iters = 2000;
+ struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.resize" };
+ struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ if (!d) {
+ print_bench_skipped("[11] window_resize_cycle", "no display server");
+ return;
+ }
+
+ static const struct yasl_vector2i sizes[] = {
+ { 320, 240 },
+ { 640, 480 },
+ { 800, 600 },
+ { 1024, 768 },
+ { 1280, 720 },
+ { 1920, 1080 }
+ };
+ const int num_sizes = sizeof(sizes) / sizeof(sizes[0]);
+
+ uint64_t t0 = get_time_ns();
+ int count = 0;
+ for (int i = 0; i < iters; i++) {
+ struct yasl_surface_spec sspec = {
+ .title = "Resize Bench",
+ .size = sizes[i % num_sizes],
+ };
+ struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ if (s) {
+ struct yasl_geometry geom = yasl_surface_geometry(s);
+ (void)geom;
+ yasl_surface_destroy(s);
+ count++;
+ }
+ }
+ uint64_t t1 = get_time_ns();
+ yasl_display_close(d);
+ double ms = (double)(t1 - t0) / 1e6;
+ double per_cycle = count ? (double)(t1 - t0) / count : 0.0;
+ char details[128];
+ snprintf(details, sizeof(details), "%d dynamic reallocations across 6 resolutions", count);
+ print_bench_row("[11] window_resize_cycle", per_cycle, ms, details);
}
int main(void) {
- printf("==================== YASL Performance Benchmark ====================\n");
+ printf("=================================== YASL Performance Benchmark ===================================\n");
+ printf("%-29s | %10s | %12s | %s\n", "Benchmark", "Per Cycle", "Total Wall", "Operations / Details");
+ printf("------------------------------+------------+---------------+--------------------------------------\n");
#if YASL_HAS_WAYLAND
bench_surface_lookup();
bench_output_follow();
@@ -175,6 +454,12 @@ int main(void) {
bench_input_queue_resize();
bench_input_queue_push_pop();
bench_timers();
- printf("====================================================================\n");
+ bench_window_cold_create();
+ bench_window_warm_create();
+ bench_window_shutdown_single();
+ bench_window_shutdown_all();
+ bench_window_title_update();
+ bench_window_resize();
+ printf("==================================================================================================\n");
return 0;
}