]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/test-bus-benchmark.c
tree-wide: remove Emacs lines from all files
[thirdparty/systemd.git] / src / libsystemd / sd-bus / test-bus-benchmark.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2013 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <sys/wait.h>
21
22 #include "sd-bus.h"
23
24 #include "alloc-util.h"
25 #include "bus-internal.h"
26 #include "bus-kernel.h"
27 #include "bus-util.h"
28 #include "def.h"
29 #include "fd-util.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_(sd_bus_message_unrefp) 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_(sd_bus_message_unrefp) 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_(sd_bus_message_unrefp) 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_(sd_bus_message_unrefp) 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 }