| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright (C) 2023. Huawei Technologies Co., Ltd */
- #include <argp.h>
- #include <stdbool.h>
- #include <pthread.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/param.h>
- #include <fcntl.h>
- #include "bench.h"
- #include "bpf_util.h"
- #include "cgroup_helpers.h"
- #include "htab_mem_bench.skel.h"
- struct htab_mem_use_case {
- const char *name;
- const char **progs;
- /* Do synchronization between addition thread and deletion thread */
- bool need_sync;
- };
- static struct htab_mem_ctx {
- const struct htab_mem_use_case *uc;
- struct htab_mem_bench *skel;
- pthread_barrier_t *notify;
- int fd;
- } ctx;
- const char *ow_progs[] = {"overwrite", NULL};
- const char *batch_progs[] = {"batch_add_batch_del", NULL};
- const char *add_del_progs[] = {"add_only", "del_only", NULL};
- const static struct htab_mem_use_case use_cases[] = {
- { .name = "overwrite", .progs = ow_progs },
- { .name = "batch_add_batch_del", .progs = batch_progs },
- { .name = "add_del_on_diff_cpu", .progs = add_del_progs, .need_sync = true },
- };
- static struct htab_mem_args {
- u32 value_size;
- const char *use_case;
- bool preallocated;
- } args = {
- .value_size = 8,
- .use_case = "overwrite",
- .preallocated = false,
- };
- enum {
- ARG_VALUE_SIZE = 10000,
- ARG_USE_CASE = 10001,
- ARG_PREALLOCATED = 10002,
- };
- static const struct argp_option opts[] = {
- { "value-size", ARG_VALUE_SIZE, "VALUE_SIZE", 0,
- "Set the value size of hash map (default 8)" },
- { "use-case", ARG_USE_CASE, "USE_CASE", 0,
- "Set the use case of hash map: overwrite|batch_add_batch_del|add_del_on_diff_cpu" },
- { "preallocated", ARG_PREALLOCATED, NULL, 0, "use preallocated hash map" },
- {},
- };
- static error_t htab_mem_parse_arg(int key, char *arg, struct argp_state *state)
- {
- switch (key) {
- case ARG_VALUE_SIZE:
- args.value_size = strtoul(arg, NULL, 10);
- if (args.value_size > 4096) {
- fprintf(stderr, "too big value size %u\n", args.value_size);
- argp_usage(state);
- }
- break;
- case ARG_USE_CASE:
- args.use_case = strdup(arg);
- if (!args.use_case) {
- fprintf(stderr, "no mem for use-case\n");
- argp_usage(state);
- }
- break;
- case ARG_PREALLOCATED:
- args.preallocated = true;
- break;
- default:
- return ARGP_ERR_UNKNOWN;
- }
- return 0;
- }
- const struct argp bench_htab_mem_argp = {
- .options = opts,
- .parser = htab_mem_parse_arg,
- };
- static void htab_mem_validate(void)
- {
- if (!strcmp(use_cases[2].name, args.use_case) && env.producer_cnt % 2) {
- fprintf(stderr, "%s needs an even number of producers\n", args.use_case);
- exit(1);
- }
- }
- static int htab_mem_bench_init_barriers(void)
- {
- pthread_barrier_t *barriers;
- unsigned int i, nr;
- if (!ctx.uc->need_sync)
- return 0;
- nr = (env.producer_cnt + 1) / 2;
- barriers = calloc(nr, sizeof(*barriers));
- if (!barriers)
- return -1;
- /* Used for synchronization between two threads */
- for (i = 0; i < nr; i++)
- pthread_barrier_init(&barriers[i], NULL, 2);
- ctx.notify = barriers;
- return 0;
- }
- static void htab_mem_bench_exit_barriers(void)
- {
- unsigned int i, nr;
- if (!ctx.notify)
- return;
- nr = (env.producer_cnt + 1) / 2;
- for (i = 0; i < nr; i++)
- pthread_barrier_destroy(&ctx.notify[i]);
- free(ctx.notify);
- }
- static const struct htab_mem_use_case *htab_mem_find_use_case_or_exit(const char *name)
- {
- unsigned int i;
- for (i = 0; i < ARRAY_SIZE(use_cases); i++) {
- if (!strcmp(name, use_cases[i].name))
- return &use_cases[i];
- }
- fprintf(stderr, "no such use-case: %s\n", name);
- fprintf(stderr, "available use case:");
- for (i = 0; i < ARRAY_SIZE(use_cases); i++)
- fprintf(stderr, " %s", use_cases[i].name);
- fprintf(stderr, "\n");
- exit(1);
- }
- static void htab_mem_setup(void)
- {
- struct bpf_map *map;
- const char **names;
- int err;
- setup_libbpf();
- ctx.uc = htab_mem_find_use_case_or_exit(args.use_case);
- err = htab_mem_bench_init_barriers();
- if (err) {
- fprintf(stderr, "failed to init barrier\n");
- exit(1);
- }
- ctx.fd = cgroup_setup_and_join("/htab_mem");
- if (ctx.fd < 0)
- goto cleanup;
- ctx.skel = htab_mem_bench__open();
- if (!ctx.skel) {
- fprintf(stderr, "failed to open skeleton\n");
- goto cleanup;
- }
- map = ctx.skel->maps.htab;
- bpf_map__set_value_size(map, args.value_size);
- /* Ensure that different CPUs can operate on different subset */
- bpf_map__set_max_entries(map, MAX(8192, 64 * env.nr_cpus));
- if (args.preallocated)
- bpf_map__set_map_flags(map, bpf_map__map_flags(map) & ~BPF_F_NO_PREALLOC);
- names = ctx.uc->progs;
- while (*names) {
- struct bpf_program *prog;
- prog = bpf_object__find_program_by_name(ctx.skel->obj, *names);
- if (!prog) {
- fprintf(stderr, "no such program %s\n", *names);
- goto cleanup;
- }
- bpf_program__set_autoload(prog, true);
- names++;
- }
- ctx.skel->bss->nr_thread = env.producer_cnt;
- err = htab_mem_bench__load(ctx.skel);
- if (err) {
- fprintf(stderr, "failed to load skeleton\n");
- goto cleanup;
- }
- err = htab_mem_bench__attach(ctx.skel);
- if (err) {
- fprintf(stderr, "failed to attach skeleton\n");
- goto cleanup;
- }
- return;
- cleanup:
- htab_mem_bench__destroy(ctx.skel);
- htab_mem_bench_exit_barriers();
- if (ctx.fd >= 0) {
- close(ctx.fd);
- cleanup_cgroup_environment();
- }
- exit(1);
- }
- static void htab_mem_add_fn(pthread_barrier_t *notify)
- {
- while (true) {
- /* Do addition */
- (void)syscall(__NR_getpgid, 0);
- /* Notify deletion thread to do deletion */
- pthread_barrier_wait(notify);
- /* Wait for deletion to complete */
- pthread_barrier_wait(notify);
- }
- }
- static void htab_mem_delete_fn(pthread_barrier_t *notify)
- {
- while (true) {
- /* Wait for addition to complete */
- pthread_barrier_wait(notify);
- /* Do deletion */
- (void)syscall(__NR_getppid);
- /* Notify addition thread to do addition */
- pthread_barrier_wait(notify);
- }
- }
- static void *htab_mem_producer(void *arg)
- {
- pthread_barrier_t *notify;
- int seq;
- if (!ctx.uc->need_sync) {
- while (true)
- (void)syscall(__NR_getpgid, 0);
- return NULL;
- }
- seq = (long)arg;
- notify = &ctx.notify[seq / 2];
- if (seq & 1)
- htab_mem_delete_fn(notify);
- else
- htab_mem_add_fn(notify);
- return NULL;
- }
- static void htab_mem_read_mem_cgrp_file(const char *name, unsigned long *value)
- {
- char buf[32];
- ssize_t got;
- int fd;
- fd = openat(ctx.fd, name, O_RDONLY);
- if (fd < 0) {
- /* cgroup v1 ? */
- fprintf(stderr, "no %s\n", name);
- *value = 0;
- return;
- }
- got = read(fd, buf, sizeof(buf) - 1);
- if (got <= 0) {
- *value = 0;
- return;
- }
- buf[got] = 0;
- *value = strtoull(buf, NULL, 0);
- close(fd);
- }
- static void htab_mem_measure(struct bench_res *res)
- {
- res->hits = atomic_swap(&ctx.skel->bss->op_cnt, 0) / env.producer_cnt;
- htab_mem_read_mem_cgrp_file("memory.current", &res->gp_ct);
- }
- static void htab_mem_report_progress(int iter, struct bench_res *res, long delta_ns)
- {
- double loop, mem;
- loop = res->hits / 1000.0 / (delta_ns / 1000000000.0);
- mem = res->gp_ct / 1048576.0;
- printf("Iter %3d (%7.3lfus): ", iter, (delta_ns - 1000000000) / 1000.0);
- printf("per-prod-op %7.2lfk/s, memory usage %7.2lfMiB\n", loop, mem);
- }
- static void htab_mem_report_final(struct bench_res res[], int res_cnt)
- {
- double mem_mean = 0.0, mem_stddev = 0.0;
- double loop_mean = 0.0, loop_stddev = 0.0;
- unsigned long peak_mem;
- int i;
- for (i = 0; i < res_cnt; i++) {
- loop_mean += res[i].hits / 1000.0 / (0.0 + res_cnt);
- mem_mean += res[i].gp_ct / 1048576.0 / (0.0 + res_cnt);
- }
- if (res_cnt > 1) {
- for (i = 0; i < res_cnt; i++) {
- loop_stddev += (loop_mean - res[i].hits / 1000.0) *
- (loop_mean - res[i].hits / 1000.0) /
- (res_cnt - 1.0);
- mem_stddev += (mem_mean - res[i].gp_ct / 1048576.0) *
- (mem_mean - res[i].gp_ct / 1048576.0) /
- (res_cnt - 1.0);
- }
- loop_stddev = sqrt(loop_stddev);
- mem_stddev = sqrt(mem_stddev);
- }
- htab_mem_read_mem_cgrp_file("memory.peak", &peak_mem);
- printf("Summary: per-prod-op %7.2lf \u00B1 %7.2lfk/s, memory usage %7.2lf \u00B1 %7.2lfMiB,"
- " peak memory usage %7.2lfMiB\n",
- loop_mean, loop_stddev, mem_mean, mem_stddev, peak_mem / 1048576.0);
- close(ctx.fd);
- cleanup_cgroup_environment();
- }
- const struct bench bench_htab_mem = {
- .name = "htab-mem",
- .argp = &bench_htab_mem_argp,
- .validate = htab_mem_validate,
- .setup = htab_mem_setup,
- .producer_thread = htab_mem_producer,
- .measure = htab_mem_measure,
- .report_progress = htab_mem_report_progress,
- .report_final = htab_mem_report_final,
- };
|