]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/test-bus-benchmark.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / libsystemd / sd-bus / test-bus-benchmark.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/wait.h>
23
24 #include "sd-bus.h"
25
26 #include "bus-internal.h"
27 #include "bus-kernel.h"
28 #include "bus-util.h"
29 #include "def.h"
30 #include "time-util.h"
31 #include "util.h"
32
33 #define MAX_SIZE (2*1024*1024)
34
35 static usec_t arg_loop_usec = 100 * USEC_PER_MSEC;
36
37 typedef enum Type {
38 TYPE_KDBUS,
39 TYPE_LEGACY,
40 TYPE_DIRECT,
41 } Type;
42
43 static void server(sd_bus *b, size_t *result) {
44 int r;
45
46 for (;;) {
47 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
48
49 r = sd_bus_process(b, &m);
50 assert_se(r >= 0);
51
52 if (r == 0)
53 assert_se(sd_bus_wait(b, USEC_INFINITY) >= 0);
54 if (!m)
55 continue;
56
57 if (sd_bus_message_is_method_call(m, "benchmark.server", "Ping"))
58 assert_se(sd_bus_reply_method_return(m, NULL) >= 0);
59 else if (sd_bus_message_is_method_call(m, "benchmark.server", "Work")) {
60 const void *p;
61 size_t sz;
62
63 /* Make sure the mmap is mapped */
64 assert_se(sd_bus_message_read_array(m, 'y', &p, &sz) > 0);
65
66 r = sd_bus_reply_method_return(m, NULL);
67 assert_se(r >= 0);
68 } else if (sd_bus_message_is_method_call(m, "benchmark.server", "Exit")) {
69 uint64_t res;
70 assert_se(sd_bus_message_read(m, "t", &res) > 0);
71
72 *result = res;
73 return;
74
75 } else if (!sd_bus_message_is_signal(m, NULL, NULL))
76 assert_not_reached("Unknown method");
77 }
78 }
79
80 static void transaction(sd_bus *b, size_t sz, const char *server_name) {
81 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
82 uint8_t *p;
83
84 assert_se(sd_bus_message_new_method_call(b, &m, server_name, "/", "benchmark.server", "Work") >= 0);
85 assert_se(sd_bus_message_append_array_space(m, 'y', sz, (void**) &p) >= 0);
86
87 memset(p, 0x80, sz);
88
89 assert_se(sd_bus_call(b, m, 0, NULL, &reply) >= 0);
90 }
91
92 static void client_bisect(const char *address, const char *server_name) {
93 _cleanup_bus_message_unref_ sd_bus_message *x = NULL;
94 size_t lsize, rsize, csize;
95 sd_bus *b;
96 int r;
97
98 r = sd_bus_new(&b);
99 assert_se(r >= 0);
100
101 r = sd_bus_set_address(b, address);
102 assert_se(r >= 0);
103
104 r = sd_bus_start(b);
105 assert_se(r >= 0);
106
107 r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
108 assert_se(r >= 0);
109
110 lsize = 1;
111 rsize = MAX_SIZE;
112
113 printf("SIZE\tCOPY\tMEMFD\n");
114
115 for (;;) {
116 usec_t t;
117 unsigned n_copying, n_memfd;
118
119 csize = (lsize + rsize) / 2;
120
121 if (csize <= lsize)
122 break;
123
124 if (csize <= 0)
125 break;
126
127 printf("%zu\t", csize);
128
129 b->use_memfd = 0;
130
131 t = now(CLOCK_MONOTONIC);
132 for (n_copying = 0;; n_copying++) {
133 transaction(b, csize, server_name);
134 if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
135 break;
136 }
137 printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
138
139 b->use_memfd = -1;
140
141 t = now(CLOCK_MONOTONIC);
142 for (n_memfd = 0;; n_memfd++) {
143 transaction(b, csize, server_name);
144 if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
145 break;
146 }
147 printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
148
149 if (n_copying == n_memfd)
150 break;
151
152 if (n_copying > n_memfd)
153 lsize = csize;
154 else
155 rsize = csize;
156 }
157
158 b->use_memfd = 1;
159 assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
160 assert_se(sd_bus_message_append(x, "t", csize) >= 0);
161 assert_se(sd_bus_send(b, x, NULL) >= 0);
162
163 sd_bus_unref(b);
164 }
165
166 static void client_chart(Type type, const char *address, const char *server_name, int fd) {
167 _cleanup_bus_message_unref_ sd_bus_message *x = NULL;
168 size_t csize;
169 sd_bus *b;
170 int r;
171
172 r = sd_bus_new(&b);
173 assert_se(r >= 0);
174
175 if (type == TYPE_DIRECT) {
176 r = sd_bus_set_fd(b, fd, fd);
177 assert_se(r >= 0);
178 } else {
179 r = sd_bus_set_address(b, address);
180 assert_se(r >= 0);
181
182 r = sd_bus_set_bus_client(b, true);
183 assert_se(r >= 0);
184 }
185
186 r = sd_bus_start(b);
187 assert_se(r >= 0);
188
189 r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
190 assert_se(r >= 0);
191
192 switch (type) {
193 case TYPE_KDBUS:
194 printf("SIZE\tCOPY\tMEMFD\n");
195 break;
196 case TYPE_LEGACY:
197 printf("SIZE\tLEGACY\n");
198 break;
199 case TYPE_DIRECT:
200 printf("SIZE\tDIRECT\n");
201 break;
202 }
203
204 for (csize = 1; csize <= MAX_SIZE; csize *= 2) {
205 usec_t t;
206 unsigned n_copying, n_memfd;
207
208 printf("%zu\t", csize);
209
210 if (type == TYPE_KDBUS) {
211 b->use_memfd = 0;
212
213 t = now(CLOCK_MONOTONIC);
214 for (n_copying = 0;; n_copying++) {
215 transaction(b, csize, server_name);
216 if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
217 break;
218 }
219
220 printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
221
222 b->use_memfd = -1;
223 }
224
225 t = now(CLOCK_MONOTONIC);
226 for (n_memfd = 0;; n_memfd++) {
227 transaction(b, csize, server_name);
228 if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
229 break;
230 }
231
232 printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
233 }
234
235 b->use_memfd = 1;
236 assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
237 assert_se(sd_bus_message_append(x, "t", csize) >= 0);
238 assert_se(sd_bus_send(b, x, NULL) >= 0);
239
240 sd_bus_unref(b);
241 }
242
243 int main(int argc, char *argv[]) {
244 enum {
245 MODE_BISECT,
246 MODE_CHART,
247 } mode = MODE_BISECT;
248 Type type = TYPE_KDBUS;
249 int i, pair[2] = { -1, -1 };
250 _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *server_name = NULL;
251 _cleanup_close_ int bus_ref = -1;
252 const char *unique;
253 cpu_set_t cpuset;
254 size_t result;
255 sd_bus *b;
256 pid_t pid;
257 int r;
258
259 for (i = 1; i < argc; i++) {
260 if (streq(argv[i], "chart")) {
261 mode = MODE_CHART;
262 continue;
263 } else if (streq(argv[i], "legacy")) {
264 type = TYPE_LEGACY;
265 continue;
266 } else if (streq(argv[i], "direct")) {
267 type = TYPE_DIRECT;
268 continue;
269 }
270
271 assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0);
272 }
273
274 assert_se(!MODE_BISECT || TYPE_KDBUS);
275
276 assert_se(arg_loop_usec > 0);
277
278 if (type == TYPE_KDBUS) {
279 assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
280
281 bus_ref = bus_kernel_create_bus(name, false, &bus_name);
282 if (bus_ref == -ENOENT)
283 exit(EXIT_TEST_SKIP);
284
285 assert_se(bus_ref >= 0);
286
287 address = strappend("kernel:path=", bus_name);
288 assert_se(address);
289 } else if (type == TYPE_LEGACY) {
290 const char *e;
291
292 e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
293 assert_se(e);
294
295 address = strdup(e);
296 assert_se(address);
297 }
298
299 r = sd_bus_new(&b);
300 assert_se(r >= 0);
301
302 if (type == TYPE_DIRECT) {
303 assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0);
304
305 r = sd_bus_set_fd(b, pair[0], pair[0]);
306 assert_se(r >= 0);
307
308 r = sd_bus_set_server(b, true, SD_ID128_NULL);
309 assert_se(r >= 0);
310 } else {
311 r = sd_bus_set_address(b, address);
312 assert_se(r >= 0);
313
314 r = sd_bus_set_bus_client(b, true);
315 assert_se(r >= 0);
316 }
317
318 r = sd_bus_start(b);
319 assert_se(r >= 0);
320
321 if (type != TYPE_DIRECT) {
322 r = sd_bus_get_unique_name(b, &unique);
323 assert_se(r >= 0);
324
325 server_name = strdup(unique);
326 assert_se(server_name);
327 }
328
329 sync();
330 setpriority(PRIO_PROCESS, 0, -19);
331
332 pid = fork();
333 assert_se(pid >= 0);
334
335 if (pid == 0) {
336 CPU_ZERO(&cpuset);
337 CPU_SET(0, &cpuset);
338 pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
339
340 safe_close(bus_ref);
341 sd_bus_unref(b);
342
343 switch (mode) {
344 case MODE_BISECT:
345 client_bisect(address, server_name);
346 break;
347
348 case MODE_CHART:
349 client_chart(type, address, server_name, pair[1]);
350 break;
351 }
352
353 _exit(0);
354 }
355
356 CPU_ZERO(&cpuset);
357 CPU_SET(1, &cpuset);
358 pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
359
360 server(b, &result);
361
362 if (mode == MODE_BISECT)
363 printf("Copying/memfd are equally fast at %zu bytes\n", result);
364
365 assert_se(waitpid(pid, NULL, 0) == pid);
366
367 safe_close(pair[1]);
368 sd_bus_unref(b);
369
370 return 0;
371 }