Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright (c) 2023 Isovalent */
  3
  4#include <sys/random.h>
  5#include <argp.h>
  6#include "bench.h"
  7#include "bpf_hashmap_lookup.skel.h"
  8#include "bpf_util.h"
  9
 10/* BPF triggering benchmarks */
 11static struct ctx {
 12	struct bpf_hashmap_lookup *skel;
 13} ctx;
 14
 15/* only available to kernel, so define it here */
 16#define BPF_MAX_LOOPS (1<<23)
 17
 18#define MAX_KEY_SIZE 1024 /* the size of the key map */
 19
 20static struct {
 21	__u32 key_size;
 22	__u32 map_flags;
 23	__u32 max_entries;
 24	__u32 nr_entries;
 25	__u32 nr_loops;
 26} args = {
 27	.key_size = 4,
 28	.map_flags = 0,
 29	.max_entries = 1000,
 30	.nr_entries = 500,
 31	.nr_loops = 1000000,
 32};
 33
 34enum {
 35	ARG_KEY_SIZE = 8001,
 36	ARG_MAP_FLAGS,
 37	ARG_MAX_ENTRIES,
 38	ARG_NR_ENTRIES,
 39	ARG_NR_LOOPS,
 40};
 41
 42static const struct argp_option opts[] = {
 43	{ "key_size", ARG_KEY_SIZE, "KEY_SIZE", 0,
 44	  "The hashmap key size (max 1024)"},
 45	{ "map_flags", ARG_MAP_FLAGS, "MAP_FLAGS", 0,
 46	  "The hashmap flags passed to BPF_MAP_CREATE"},
 47	{ "max_entries", ARG_MAX_ENTRIES, "MAX_ENTRIES", 0,
 48	  "The hashmap max entries"},
 49	{ "nr_entries", ARG_NR_ENTRIES, "NR_ENTRIES", 0,
 50	  "The number of entries to insert/lookup"},
 51	{ "nr_loops", ARG_NR_LOOPS, "NR_LOOPS", 0,
 52	  "The number of loops for the benchmark"},
 53	{},
 54};
 55
 56static error_t parse_arg(int key, char *arg, struct argp_state *state)
 57{
 58	long ret;
 59
 60	switch (key) {
 61	case ARG_KEY_SIZE:
 62		ret = strtol(arg, NULL, 10);
 63		if (ret < 1 || ret > MAX_KEY_SIZE) {
 64			fprintf(stderr, "invalid key_size");
 65			argp_usage(state);
 66		}
 67		args.key_size = ret;
 68		break;
 69	case ARG_MAP_FLAGS:
 70		ret = strtol(arg, NULL, 0);
 71		if (ret < 0 || ret > UINT_MAX) {
 72			fprintf(stderr, "invalid map_flags");
 73			argp_usage(state);
 74		}
 75		args.map_flags = ret;
 76		break;
 77	case ARG_MAX_ENTRIES:
 78		ret = strtol(arg, NULL, 10);
 79		if (ret < 1 || ret > UINT_MAX) {
 80			fprintf(stderr, "invalid max_entries");
 81			argp_usage(state);
 82		}
 83		args.max_entries = ret;
 84		break;
 85	case ARG_NR_ENTRIES:
 86		ret = strtol(arg, NULL, 10);
 87		if (ret < 1 || ret > UINT_MAX) {
 88			fprintf(stderr, "invalid nr_entries");
 89			argp_usage(state);
 90		}
 91		args.nr_entries = ret;
 92		break;
 93	case ARG_NR_LOOPS:
 94		ret = strtol(arg, NULL, 10);
 95		if (ret < 1 || ret > BPF_MAX_LOOPS) {
 96			fprintf(stderr, "invalid nr_loops: %ld (min=1 max=%u)\n",
 97				ret, BPF_MAX_LOOPS);
 98			argp_usage(state);
 99		}
100		args.nr_loops = ret;
101		break;
102	default:
103		return ARGP_ERR_UNKNOWN;
104	}
105
106	return 0;
107}
108
109const struct argp bench_hashmap_lookup_argp = {
110	.options = opts,
111	.parser = parse_arg,
112};
113
114static void validate(void)
115{
116	if (env.consumer_cnt != 0) {
117		fprintf(stderr, "benchmark doesn't support consumer!\n");
118		exit(1);
119	}
120
121	if (args.nr_entries > args.max_entries) {
122		fprintf(stderr, "args.nr_entries is too big! (max %u, got %u)\n",
123			args.max_entries, args.nr_entries);
124		exit(1);
125	}
126}
127
128static void *producer(void *input)
129{
130	while (true) {
131		/* trigger the bpf program */
132		syscall(__NR_getpgid);
133	}
134	return NULL;
135}
136
137static void measure(struct bench_res *res)
138{
139}
140
141static inline void patch_key(u32 i, u32 *key)
142{
143#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
144	*key = i + 1;
145#else
146	*key = __builtin_bswap32(i + 1);
147#endif
148	/* the rest of key is random */
149}
150
151static void setup(void)
152{
153	struct bpf_link *link;
154	int map_fd;
155	int ret;
156	int i;
157
158	setup_libbpf();
159
160	ctx.skel = bpf_hashmap_lookup__open();
161	if (!ctx.skel) {
162		fprintf(stderr, "failed to open skeleton\n");
163		exit(1);
164	}
165
166	bpf_map__set_max_entries(ctx.skel->maps.hash_map_bench, args.max_entries);
167	bpf_map__set_key_size(ctx.skel->maps.hash_map_bench, args.key_size);
168	bpf_map__set_value_size(ctx.skel->maps.hash_map_bench, 8);
169	bpf_map__set_map_flags(ctx.skel->maps.hash_map_bench, args.map_flags);
170
171	ctx.skel->bss->nr_entries = args.nr_entries;
172	ctx.skel->bss->nr_loops = args.nr_loops / args.nr_entries;
173
174	if (args.key_size > 4) {
175		for (i = 1; i < args.key_size/4; i++)
176			ctx.skel->bss->key[i] = 2654435761 * i;
177	}
178
179	ret = bpf_hashmap_lookup__load(ctx.skel);
180	if (ret) {
181		bpf_hashmap_lookup__destroy(ctx.skel);
182		fprintf(stderr, "failed to load map: %s", strerror(-ret));
183		exit(1);
184	}
185
186	/* fill in the hash_map */
187	map_fd = bpf_map__fd(ctx.skel->maps.hash_map_bench);
188	for (u64 i = 0; i < args.nr_entries; i++) {
189		patch_key(i, ctx.skel->bss->key);
190		bpf_map_update_elem(map_fd, ctx.skel->bss->key, &i, BPF_ANY);
191	}
192
193	link = bpf_program__attach(ctx.skel->progs.benchmark);
194	if (!link) {
195		fprintf(stderr, "failed to attach program!\n");
196		exit(1);
197	}
198}
199
200static inline double events_from_time(u64 time)
201{
202	if (time)
203		return args.nr_loops * 1000000000llu / time / 1000000.0L;
204
205	return 0;
206}
207
208static int compute_events(u64 *times, double *events_mean, double *events_stddev, u64 *mean_time)
209{
210	int i, n = 0;
211
212	*events_mean = 0;
213	*events_stddev = 0;
214	*mean_time = 0;
215
216	for (i = 0; i < 32; i++) {
217		if (!times[i])
218			break;
219		*mean_time += times[i];
220		*events_mean += events_from_time(times[i]);
221		n += 1;
222	}
223	if (!n)
224		return 0;
225
226	*mean_time /= n;
227	*events_mean /= n;
228
229	if (n > 1) {
230		for (i = 0; i < n; i++) {
231			double events_i = *events_mean - events_from_time(times[i]);
232			*events_stddev += events_i * events_i / (n - 1);
233		}
234		*events_stddev = sqrt(*events_stddev);
235	}
236
237	return n;
238}
239
240static void hashmap_report_final(struct bench_res res[], int res_cnt)
241{
242	unsigned int nr_cpus = bpf_num_possible_cpus();
243	double events_mean, events_stddev;
244	u64 mean_time;
245	int i, n;
246
247	for (i = 0; i < nr_cpus; i++) {
248		n = compute_events(ctx.skel->bss->percpu_times[i], &events_mean,
249				   &events_stddev, &mean_time);
250		if (n == 0)
251			continue;
252
253		if (env.quiet) {
254			/* we expect only one cpu to be present */
255			if (env.affinity)
256				printf("%.3lf\n", events_mean);
257			else
258				printf("cpu%02d %.3lf\n", i, events_mean);
259		} else {
260			printf("cpu%02d: lookup %.3lfM ± %.3lfM events/sec"
261			       " (approximated from %d samples of ~%lums)\n",
262			       i, events_mean, 2*events_stddev,
263			       n, mean_time / 1000000);
264		}
265	}
266}
267
268const struct bench bench_bpf_hashmap_lookup = {
269	.name = "bpf-hashmap-lookup",
270	.argp = &bench_hashmap_lookup_argp,
271	.validate = validate,
272	.setup = setup,
273	.producer_thread = producer,
274	.measure = measure,
275	.report_progress = NULL,
276	.report_final = hashmap_report_final,
277};
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright (c) 2023 Isovalent */
  3
  4#include <sys/random.h>
  5#include <argp.h>
  6#include "bench.h"
  7#include "bpf_hashmap_lookup.skel.h"
  8#include "bpf_util.h"
  9
 10/* BPF triggering benchmarks */
 11static struct ctx {
 12	struct bpf_hashmap_lookup *skel;
 13} ctx;
 14
 15/* only available to kernel, so define it here */
 16#define BPF_MAX_LOOPS (1<<23)
 17
 18#define MAX_KEY_SIZE 1024 /* the size of the key map */
 19
 20static struct {
 21	__u32 key_size;
 22	__u32 map_flags;
 23	__u32 max_entries;
 24	__u32 nr_entries;
 25	__u32 nr_loops;
 26} args = {
 27	.key_size = 4,
 28	.map_flags = 0,
 29	.max_entries = 1000,
 30	.nr_entries = 500,
 31	.nr_loops = 1000000,
 32};
 33
 34enum {
 35	ARG_KEY_SIZE = 8001,
 36	ARG_MAP_FLAGS,
 37	ARG_MAX_ENTRIES,
 38	ARG_NR_ENTRIES,
 39	ARG_NR_LOOPS,
 40};
 41
 42static const struct argp_option opts[] = {
 43	{ "key_size", ARG_KEY_SIZE, "KEY_SIZE", 0,
 44	  "The hashmap key size (max 1024)"},
 45	{ "map_flags", ARG_MAP_FLAGS, "MAP_FLAGS", 0,
 46	  "The hashmap flags passed to BPF_MAP_CREATE"},
 47	{ "max_entries", ARG_MAX_ENTRIES, "MAX_ENTRIES", 0,
 48	  "The hashmap max entries"},
 49	{ "nr_entries", ARG_NR_ENTRIES, "NR_ENTRIES", 0,
 50	  "The number of entries to insert/lookup"},
 51	{ "nr_loops", ARG_NR_LOOPS, "NR_LOOPS", 0,
 52	  "The number of loops for the benchmark"},
 53	{},
 54};
 55
 56static error_t parse_arg(int key, char *arg, struct argp_state *state)
 57{
 58	long ret;
 59
 60	switch (key) {
 61	case ARG_KEY_SIZE:
 62		ret = strtol(arg, NULL, 10);
 63		if (ret < 1 || ret > MAX_KEY_SIZE) {
 64			fprintf(stderr, "invalid key_size");
 65			argp_usage(state);
 66		}
 67		args.key_size = ret;
 68		break;
 69	case ARG_MAP_FLAGS:
 70		ret = strtol(arg, NULL, 0);
 71		if (ret < 0 || ret > UINT_MAX) {
 72			fprintf(stderr, "invalid map_flags");
 73			argp_usage(state);
 74		}
 75		args.map_flags = ret;
 76		break;
 77	case ARG_MAX_ENTRIES:
 78		ret = strtol(arg, NULL, 10);
 79		if (ret < 1 || ret > UINT_MAX) {
 80			fprintf(stderr, "invalid max_entries");
 81			argp_usage(state);
 82		}
 83		args.max_entries = ret;
 84		break;
 85	case ARG_NR_ENTRIES:
 86		ret = strtol(arg, NULL, 10);
 87		if (ret < 1 || ret > UINT_MAX) {
 88			fprintf(stderr, "invalid nr_entries");
 89			argp_usage(state);
 90		}
 91		args.nr_entries = ret;
 92		break;
 93	case ARG_NR_LOOPS:
 94		ret = strtol(arg, NULL, 10);
 95		if (ret < 1 || ret > BPF_MAX_LOOPS) {
 96			fprintf(stderr, "invalid nr_loops: %ld (min=1 max=%u)\n",
 97				ret, BPF_MAX_LOOPS);
 98			argp_usage(state);
 99		}
100		args.nr_loops = ret;
101		break;
102	default:
103		return ARGP_ERR_UNKNOWN;
104	}
105
106	return 0;
107}
108
109const struct argp bench_hashmap_lookup_argp = {
110	.options = opts,
111	.parser = parse_arg,
112};
113
114static void validate(void)
115{
116	if (env.consumer_cnt != 0) {
117		fprintf(stderr, "benchmark doesn't support consumer!\n");
118		exit(1);
119	}
120
121	if (args.nr_entries > args.max_entries) {
122		fprintf(stderr, "args.nr_entries is too big! (max %u, got %u)\n",
123			args.max_entries, args.nr_entries);
124		exit(1);
125	}
126}
127
128static void *producer(void *input)
129{
130	while (true) {
131		/* trigger the bpf program */
132		syscall(__NR_getpgid);
133	}
134	return NULL;
135}
136
137static void measure(struct bench_res *res)
138{
139}
140
141static inline void patch_key(u32 i, u32 *key)
142{
143#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
144	*key = i + 1;
145#else
146	*key = __builtin_bswap32(i + 1);
147#endif
148	/* the rest of key is random */
149}
150
151static void setup(void)
152{
153	struct bpf_link *link;
154	int map_fd;
155	int ret;
156	int i;
157
158	setup_libbpf();
159
160	ctx.skel = bpf_hashmap_lookup__open();
161	if (!ctx.skel) {
162		fprintf(stderr, "failed to open skeleton\n");
163		exit(1);
164	}
165
166	bpf_map__set_max_entries(ctx.skel->maps.hash_map_bench, args.max_entries);
167	bpf_map__set_key_size(ctx.skel->maps.hash_map_bench, args.key_size);
168	bpf_map__set_value_size(ctx.skel->maps.hash_map_bench, 8);
169	bpf_map__set_map_flags(ctx.skel->maps.hash_map_bench, args.map_flags);
170
171	ctx.skel->bss->nr_entries = args.nr_entries;
172	ctx.skel->bss->nr_loops = args.nr_loops / args.nr_entries;
173
174	if (args.key_size > 4) {
175		for (i = 1; i < args.key_size/4; i++)
176			ctx.skel->bss->key[i] = 2654435761 * i;
177	}
178
179	ret = bpf_hashmap_lookup__load(ctx.skel);
180	if (ret) {
181		bpf_hashmap_lookup__destroy(ctx.skel);
182		fprintf(stderr, "failed to load map: %s", strerror(-ret));
183		exit(1);
184	}
185
186	/* fill in the hash_map */
187	map_fd = bpf_map__fd(ctx.skel->maps.hash_map_bench);
188	for (u64 i = 0; i < args.nr_entries; i++) {
189		patch_key(i, ctx.skel->bss->key);
190		bpf_map_update_elem(map_fd, ctx.skel->bss->key, &i, BPF_ANY);
191	}
192
193	link = bpf_program__attach(ctx.skel->progs.benchmark);
194	if (!link) {
195		fprintf(stderr, "failed to attach program!\n");
196		exit(1);
197	}
198}
199
200static inline double events_from_time(u64 time)
201{
202	if (time)
203		return args.nr_loops * 1000000000llu / time / 1000000.0L;
204
205	return 0;
206}
207
208static int compute_events(u64 *times, double *events_mean, double *events_stddev, u64 *mean_time)
209{
210	int i, n = 0;
211
212	*events_mean = 0;
213	*events_stddev = 0;
214	*mean_time = 0;
215
216	for (i = 0; i < 32; i++) {
217		if (!times[i])
218			break;
219		*mean_time += times[i];
220		*events_mean += events_from_time(times[i]);
221		n += 1;
222	}
223	if (!n)
224		return 0;
225
226	*mean_time /= n;
227	*events_mean /= n;
228
229	if (n > 1) {
230		for (i = 0; i < n; i++) {
231			double events_i = *events_mean - events_from_time(times[i]);
232			*events_stddev += events_i * events_i / (n - 1);
233		}
234		*events_stddev = sqrt(*events_stddev);
235	}
236
237	return n;
238}
239
240static void hashmap_report_final(struct bench_res res[], int res_cnt)
241{
242	unsigned int nr_cpus = bpf_num_possible_cpus();
243	double events_mean, events_stddev;
244	u64 mean_time;
245	int i, n;
246
247	for (i = 0; i < nr_cpus; i++) {
248		n = compute_events(ctx.skel->bss->percpu_times[i], &events_mean,
249				   &events_stddev, &mean_time);
250		if (n == 0)
251			continue;
252
253		if (env.quiet) {
254			/* we expect only one cpu to be present */
255			if (env.affinity)
256				printf("%.3lf\n", events_mean);
257			else
258				printf("cpu%02d %.3lf\n", i, events_mean);
259		} else {
260			printf("cpu%02d: lookup %.3lfM ± %.3lfM events/sec"
261			       " (approximated from %d samples of ~%lums)\n",
262			       i, events_mean, 2*events_stddev,
263			       n, mean_time / 1000000);
264		}
265	}
266}
267
268const struct bench bench_bpf_hashmap_lookup = {
269	.name = "bpf-hashmap-lookup",
270	.argp = &bench_hashmap_lookup_argp,
271	.validate = validate,
272	.setup = setup,
273	.producer_thread = producer,
274	.measure = measure,
275	.report_progress = NULL,
276	.report_final = hashmap_report_final,
277};