bench_htab_mem.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Copyright (C) 2023. Huawei Technologies Co., Ltd */
  3. #include <argp.h>
  4. #include <stdbool.h>
  5. #include <pthread.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <sys/param.h>
  9. #include <fcntl.h>
  10. #include "bench.h"
  11. #include "bpf_util.h"
  12. #include "cgroup_helpers.h"
  13. #include "htab_mem_bench.skel.h"
  14. struct htab_mem_use_case {
  15. const char *name;
  16. const char **progs;
  17. /* Do synchronization between addition thread and deletion thread */
  18. bool need_sync;
  19. };
  20. static struct htab_mem_ctx {
  21. const struct htab_mem_use_case *uc;
  22. struct htab_mem_bench *skel;
  23. pthread_barrier_t *notify;
  24. int fd;
  25. } ctx;
  26. const char *ow_progs[] = {"overwrite", NULL};
  27. const char *batch_progs[] = {"batch_add_batch_del", NULL};
  28. const char *add_del_progs[] = {"add_only", "del_only", NULL};
  29. const static struct htab_mem_use_case use_cases[] = {
  30. { .name = "overwrite", .progs = ow_progs },
  31. { .name = "batch_add_batch_del", .progs = batch_progs },
  32. { .name = "add_del_on_diff_cpu", .progs = add_del_progs, .need_sync = true },
  33. };
  34. static struct htab_mem_args {
  35. u32 value_size;
  36. const char *use_case;
  37. bool preallocated;
  38. } args = {
  39. .value_size = 8,
  40. .use_case = "overwrite",
  41. .preallocated = false,
  42. };
  43. enum {
  44. ARG_VALUE_SIZE = 10000,
  45. ARG_USE_CASE = 10001,
  46. ARG_PREALLOCATED = 10002,
  47. };
  48. static const struct argp_option opts[] = {
  49. { "value-size", ARG_VALUE_SIZE, "VALUE_SIZE", 0,
  50. "Set the value size of hash map (default 8)" },
  51. { "use-case", ARG_USE_CASE, "USE_CASE", 0,
  52. "Set the use case of hash map: overwrite|batch_add_batch_del|add_del_on_diff_cpu" },
  53. { "preallocated", ARG_PREALLOCATED, NULL, 0, "use preallocated hash map" },
  54. {},
  55. };
  56. static error_t htab_mem_parse_arg(int key, char *arg, struct argp_state *state)
  57. {
  58. switch (key) {
  59. case ARG_VALUE_SIZE:
  60. args.value_size = strtoul(arg, NULL, 10);
  61. if (args.value_size > 4096) {
  62. fprintf(stderr, "too big value size %u\n", args.value_size);
  63. argp_usage(state);
  64. }
  65. break;
  66. case ARG_USE_CASE:
  67. args.use_case = strdup(arg);
  68. if (!args.use_case) {
  69. fprintf(stderr, "no mem for use-case\n");
  70. argp_usage(state);
  71. }
  72. break;
  73. case ARG_PREALLOCATED:
  74. args.preallocated = true;
  75. break;
  76. default:
  77. return ARGP_ERR_UNKNOWN;
  78. }
  79. return 0;
  80. }
  81. const struct argp bench_htab_mem_argp = {
  82. .options = opts,
  83. .parser = htab_mem_parse_arg,
  84. };
  85. static void htab_mem_validate(void)
  86. {
  87. if (!strcmp(use_cases[2].name, args.use_case) && env.producer_cnt % 2) {
  88. fprintf(stderr, "%s needs an even number of producers\n", args.use_case);
  89. exit(1);
  90. }
  91. }
  92. static int htab_mem_bench_init_barriers(void)
  93. {
  94. pthread_barrier_t *barriers;
  95. unsigned int i, nr;
  96. if (!ctx.uc->need_sync)
  97. return 0;
  98. nr = (env.producer_cnt + 1) / 2;
  99. barriers = calloc(nr, sizeof(*barriers));
  100. if (!barriers)
  101. return -1;
  102. /* Used for synchronization between two threads */
  103. for (i = 0; i < nr; i++)
  104. pthread_barrier_init(&barriers[i], NULL, 2);
  105. ctx.notify = barriers;
  106. return 0;
  107. }
  108. static void htab_mem_bench_exit_barriers(void)
  109. {
  110. unsigned int i, nr;
  111. if (!ctx.notify)
  112. return;
  113. nr = (env.producer_cnt + 1) / 2;
  114. for (i = 0; i < nr; i++)
  115. pthread_barrier_destroy(&ctx.notify[i]);
  116. free(ctx.notify);
  117. }
  118. static const struct htab_mem_use_case *htab_mem_find_use_case_or_exit(const char *name)
  119. {
  120. unsigned int i;
  121. for (i = 0; i < ARRAY_SIZE(use_cases); i++) {
  122. if (!strcmp(name, use_cases[i].name))
  123. return &use_cases[i];
  124. }
  125. fprintf(stderr, "no such use-case: %s\n", name);
  126. fprintf(stderr, "available use case:");
  127. for (i = 0; i < ARRAY_SIZE(use_cases); i++)
  128. fprintf(stderr, " %s", use_cases[i].name);
  129. fprintf(stderr, "\n");
  130. exit(1);
  131. }
  132. static void htab_mem_setup(void)
  133. {
  134. struct bpf_map *map;
  135. const char **names;
  136. int err;
  137. setup_libbpf();
  138. ctx.uc = htab_mem_find_use_case_or_exit(args.use_case);
  139. err = htab_mem_bench_init_barriers();
  140. if (err) {
  141. fprintf(stderr, "failed to init barrier\n");
  142. exit(1);
  143. }
  144. ctx.fd = cgroup_setup_and_join("/htab_mem");
  145. if (ctx.fd < 0)
  146. goto cleanup;
  147. ctx.skel = htab_mem_bench__open();
  148. if (!ctx.skel) {
  149. fprintf(stderr, "failed to open skeleton\n");
  150. goto cleanup;
  151. }
  152. map = ctx.skel->maps.htab;
  153. bpf_map__set_value_size(map, args.value_size);
  154. /* Ensure that different CPUs can operate on different subset */
  155. bpf_map__set_max_entries(map, MAX(8192, 64 * env.nr_cpus));
  156. if (args.preallocated)
  157. bpf_map__set_map_flags(map, bpf_map__map_flags(map) & ~BPF_F_NO_PREALLOC);
  158. names = ctx.uc->progs;
  159. while (*names) {
  160. struct bpf_program *prog;
  161. prog = bpf_object__find_program_by_name(ctx.skel->obj, *names);
  162. if (!prog) {
  163. fprintf(stderr, "no such program %s\n", *names);
  164. goto cleanup;
  165. }
  166. bpf_program__set_autoload(prog, true);
  167. names++;
  168. }
  169. ctx.skel->bss->nr_thread = env.producer_cnt;
  170. err = htab_mem_bench__load(ctx.skel);
  171. if (err) {
  172. fprintf(stderr, "failed to load skeleton\n");
  173. goto cleanup;
  174. }
  175. err = htab_mem_bench__attach(ctx.skel);
  176. if (err) {
  177. fprintf(stderr, "failed to attach skeleton\n");
  178. goto cleanup;
  179. }
  180. return;
  181. cleanup:
  182. htab_mem_bench__destroy(ctx.skel);
  183. htab_mem_bench_exit_barriers();
  184. if (ctx.fd >= 0) {
  185. close(ctx.fd);
  186. cleanup_cgroup_environment();
  187. }
  188. exit(1);
  189. }
  190. static void htab_mem_add_fn(pthread_barrier_t *notify)
  191. {
  192. while (true) {
  193. /* Do addition */
  194. (void)syscall(__NR_getpgid, 0);
  195. /* Notify deletion thread to do deletion */
  196. pthread_barrier_wait(notify);
  197. /* Wait for deletion to complete */
  198. pthread_barrier_wait(notify);
  199. }
  200. }
  201. static void htab_mem_delete_fn(pthread_barrier_t *notify)
  202. {
  203. while (true) {
  204. /* Wait for addition to complete */
  205. pthread_barrier_wait(notify);
  206. /* Do deletion */
  207. (void)syscall(__NR_getppid);
  208. /* Notify addition thread to do addition */
  209. pthread_barrier_wait(notify);
  210. }
  211. }
  212. static void *htab_mem_producer(void *arg)
  213. {
  214. pthread_barrier_t *notify;
  215. int seq;
  216. if (!ctx.uc->need_sync) {
  217. while (true)
  218. (void)syscall(__NR_getpgid, 0);
  219. return NULL;
  220. }
  221. seq = (long)arg;
  222. notify = &ctx.notify[seq / 2];
  223. if (seq & 1)
  224. htab_mem_delete_fn(notify);
  225. else
  226. htab_mem_add_fn(notify);
  227. return NULL;
  228. }
  229. static void htab_mem_read_mem_cgrp_file(const char *name, unsigned long *value)
  230. {
  231. char buf[32];
  232. ssize_t got;
  233. int fd;
  234. fd = openat(ctx.fd, name, O_RDONLY);
  235. if (fd < 0) {
  236. /* cgroup v1 ? */
  237. fprintf(stderr, "no %s\n", name);
  238. *value = 0;
  239. return;
  240. }
  241. got = read(fd, buf, sizeof(buf) - 1);
  242. if (got <= 0) {
  243. *value = 0;
  244. return;
  245. }
  246. buf[got] = 0;
  247. *value = strtoull(buf, NULL, 0);
  248. close(fd);
  249. }
  250. static void htab_mem_measure(struct bench_res *res)
  251. {
  252. res->hits = atomic_swap(&ctx.skel->bss->op_cnt, 0) / env.producer_cnt;
  253. htab_mem_read_mem_cgrp_file("memory.current", &res->gp_ct);
  254. }
  255. static void htab_mem_report_progress(int iter, struct bench_res *res, long delta_ns)
  256. {
  257. double loop, mem;
  258. loop = res->hits / 1000.0 / (delta_ns / 1000000000.0);
  259. mem = res->gp_ct / 1048576.0;
  260. printf("Iter %3d (%7.3lfus): ", iter, (delta_ns - 1000000000) / 1000.0);
  261. printf("per-prod-op %7.2lfk/s, memory usage %7.2lfMiB\n", loop, mem);
  262. }
  263. static void htab_mem_report_final(struct bench_res res[], int res_cnt)
  264. {
  265. double mem_mean = 0.0, mem_stddev = 0.0;
  266. double loop_mean = 0.0, loop_stddev = 0.0;
  267. unsigned long peak_mem;
  268. int i;
  269. for (i = 0; i < res_cnt; i++) {
  270. loop_mean += res[i].hits / 1000.0 / (0.0 + res_cnt);
  271. mem_mean += res[i].gp_ct / 1048576.0 / (0.0 + res_cnt);
  272. }
  273. if (res_cnt > 1) {
  274. for (i = 0; i < res_cnt; i++) {
  275. loop_stddev += (loop_mean - res[i].hits / 1000.0) *
  276. (loop_mean - res[i].hits / 1000.0) /
  277. (res_cnt - 1.0);
  278. mem_stddev += (mem_mean - res[i].gp_ct / 1048576.0) *
  279. (mem_mean - res[i].gp_ct / 1048576.0) /
  280. (res_cnt - 1.0);
  281. }
  282. loop_stddev = sqrt(loop_stddev);
  283. mem_stddev = sqrt(mem_stddev);
  284. }
  285. htab_mem_read_mem_cgrp_file("memory.peak", &peak_mem);
  286. printf("Summary: per-prod-op %7.2lf \u00B1 %7.2lfk/s, memory usage %7.2lf \u00B1 %7.2lfMiB,"
  287. " peak memory usage %7.2lfMiB\n",
  288. loop_mean, loop_stddev, mem_mean, mem_stddev, peak_mem / 1048576.0);
  289. close(ctx.fd);
  290. cleanup_cgroup_environment();
  291. }
  292. const struct bench bench_htab_mem = {
  293. .name = "htab-mem",
  294. .argp = &bench_htab_mem_argp,
  295. .validate = htab_mem_validate,
  296. .setup = htab_mem_setup,
  297. .producer_thread = htab_mem_producer,
  298. .measure = htab_mem_measure,
  299. .report_progress = htab_mem_report_progress,
  300. .report_final = htab_mem_report_final,
  301. };