// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ #include #include #include #include "bench.h" #include #include #include #include #include "libarena/libarena_bench.skel.h" static struct { __u64 alloc_size; __u64 nallocs; } args = { .alloc_size = 64, .nallocs = 10000, }; static struct { struct libarena_bench *skel; int bench_fd; int reset_fd; } ctx; enum { ARG_LIBARENA_ALLOC_SIZE = 12000, ARG_LIBARENA_NALLOCS, }; static const struct argp_option opts[] = { { "alloc_size", ARG_LIBARENA_ALLOC_SIZE, "BYTES", 0, "Size of each arena allocation" }, { "nallocs", ARG_LIBARENA_NALLOCS, "ITERS", 0, "Number of allocation per measurement" }, {}, }; static error_t parse_arg(int key, char *arg, struct argp_state *state) { unsigned long value; switch (key) { case ARG_LIBARENA_ALLOC_SIZE: value = strtoull(arg, NULL, 10); if (!value || value >= UINT_MAX) { fprintf(stderr, "invalid alloc_size: %ld", value); argp_usage(state); } args.alloc_size = value; break; case ARG_LIBARENA_NALLOCS: args.nallocs = strtoull(arg, NULL, 10); break; default: return ARGP_ERR_UNKNOWN; } return 0; } const struct argp bench_libarena_argp = { .options = opts, .parser = parse_arg, }; static void validate(void) { if (env.consumer_cnt != 0) { fprintf(stderr, "benchmark doesn't support consumers\n"); exit(1); } if (env.producer_cnt != 1) { fprintf(stderr, "benchmark supports exactly one producer\n"); exit(1); } } static void setup_common(void) { struct arena_alloc_reserve_args reserve_args = { .nr_pages = ARENA_RESERVE_PAGES_DFL, }; int err; setup_libbpf(); ctx.skel = libarena_bench__open_and_load(); if (!ctx.skel) { fprintf(stderr, "failed to open and load skeleton\n"); exit(1); } err = libarena_run_prog_args( bpf_program__fd(ctx.skel->progs.arena_alloc_reserve), &reserve_args, sizeof(reserve_args)); if (err) { fprintf(stderr, "failed to reserve arena pages: %d\n", err); exit(1); } err = libarena_run_prog( bpf_program__fd(ctx.skel->progs.arena_buddy_reset)); if (err) { fprintf(stderr, "failed to initialize arena allocator: %d\n", err); exit(1); } ctx.skel->bss->bench_alloc_size = args.alloc_size; ctx.skel->bss->bench_nallocs = args.nallocs; ctx.reset_fd = bpf_program__fd(ctx.skel->progs.arena_buddy_reset); } static void malloc_setup(void) { setup_common(); ctx.bench_fd = bpf_program__fd(ctx.skel->progs.bench_malloc); } static void calloc_setup(void) { setup_common(); ctx.bench_fd = bpf_program__fd(ctx.skel->progs.bench_calloc); } static void *producer(void *input) { int err; while (true) { err = libarena_run_prog(ctx.bench_fd); if (err) { fprintf(stderr, "libarena benchmark failed: %d\n", err); exit(1); } err = libarena_run_prog(ctx.reset_fd); if (err) { fprintf(stderr, "libarena alloc reset failed: %d\n", err); exit(1); } } return NULL; } static void measure(struct bench_res *res) { res->duration_ns = atomic_swap(&ctx.skel->bss->bench_duration_ns, 0); res->hits = atomic_swap(&ctx.skel->bss->bench_hits, 0); } static void report_progress(int iter, struct bench_res *res, long delta_ns) { double latency_ns = 0.0; if (res->hits) latency_ns = res->duration_ns / (double)res->hits; printf("Iter %3d (%7.3lfus): latency %8.3lf ns/op (%ld allocations)\n", iter, (delta_ns - 1000000000) / 1000.0, latency_ns, res->hits); } static void report_final(struct bench_res res[], int res_cnt) { unsigned long duration_ns = 0; long hits = 0; int i; for (i = 0; i < res_cnt; i++) { duration_ns += res[i].duration_ns; hits += res[i].hits; } if (!hits || !res_cnt) { printf("Summary: no runs measured\n"); return; } printf("Summary: %.3lf ns/op, %.0lf invocations for %u allocations/invocation)\n", duration_ns / (double)hits, hits / (double)res_cnt, ctx.skel->bss->bench_nallocs); } const struct bench bench_libarena_malloc = { .name = "libarena-malloc", .argp = &bench_libarena_argp, .validate = validate, .setup = malloc_setup, .producer_thread = producer, .measure = measure, .report_progress = report_progress, .report_final = report_final, }; const struct bench bench_libarena_calloc = { .name = "libarena-calloc", .argp = &bench_libarena_argp, .validate = validate, .setup = calloc_setup, .producer_thread = producer, .measure = measure, .report_progress = report_progress, .report_final = report_final, };