Loading...
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};
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};