]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/test-bus-benchmark.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / libsystemd / sd-bus / test-bus-benchmark.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <sys/wait.h>
4
5 #include "sd-bus.h"
6
7 #include "alloc-util.h"
8 #include "bus-internal.h"
9 #include "bus-kernel.h"
10 #include "bus-util.h"
11 #include "def.h"
12 #include "fd-util.h"
13 #include "missing_resource.h"
14 #include "time-util.h"
15 #include "util.h"
16
17 #define MAX_SIZE (2*1024*1024)
18
19 static usec_t arg_loop_usec = 100 * USEC_PER_MSEC;
20
21 typedef enum Type {
22 TYPE_LEGACY,
23 TYPE_DIRECT,
24 } Type;
25
26 static void server(sd_bus *b, size_t *result) {
27 int r;
28
29 for (;;) {
30 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
31
32 r = sd_bus_process(b, &m);
33 assert_se(r >= 0);
34
35 if (r == 0)
36 assert_se(sd_bus_wait(b, USEC_INFINITY) >= 0);
37 if (!m)
38 continue;
39
40 if (sd_bus_message_is_method_call(m, "benchmark.server", "Ping"))
41 assert_se(sd_bus_reply_method_return(m, NULL) >= 0);
42 else if (sd_bus_message_is_method_call(m, "benchmark.server", "Work")) {
43 const void *p;
44 size_t sz;
45
46 /* Make sure the mmap is mapped */
47 assert_se(sd_bus_message_read_array(m, 'y', &p, &sz) > 0);
48
49 r = sd_bus_reply_method_return(m, NULL);
50 assert_se(r >= 0);
51 } else if (sd_bus_message_is_method_call(m, "benchmark.server", "Exit")) {
52 uint64_t res;
53 assert_se(sd_bus_message_read(m, "t", &res) > 0);
54
55 *result = res;
56 return;
57
58 } else if (!sd_bus_message_is_signal(m, NULL, NULL))
59 assert_not_reached("Unknown method");
60 }
61 }
62
63 static void transaction(sd_bus *b, size_t sz, const char *server_name) {
64 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
65 uint8_t *p;
66
67 assert_se(sd_bus_message_new_method_call(b, &m, server_name, "/", "benchmark.server", "Work") >= 0);
68 assert_se(sd_bus_message_append_array_space(m, 'y', sz, (void**) &p) >= 0);
69
70 memset(p, 0x80, sz);
71
72 assert_se(sd_bus_call(b, m, 0, NULL, &reply) >= 0);
73 }
74
75 static void client_bisect(const char *address, const char *server_name) {
76 _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL;
77 size_t lsize, rsize, csize;
78 sd_bus *b;
79 int r;
80
81 r = sd_bus_new(&b);
82 assert_se(r >= 0);
83
84 r = sd_bus_set_address(b, address);
85 assert_se(r >= 0);
86
87 r = sd_bus_start(b);
88 assert_se(r >= 0);
89
90 r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
91 assert_se(r >= 0);
92
93 lsize = 1;
94 rsize = MAX_SIZE;
95
96 printf("SIZE\tCOPY\tMEMFD\n");
97
98 for (;;) {
99 usec_t t;
100 unsigned n_copying, n_memfd;
101
102 csize = (lsize + rsize) / 2;
103
104 if (csize <= lsize)
105 break;
106
107 if (csize <= 0)
108 break;
109
110 printf("%zu\t", csize);
111
112 b->use_memfd = 0;
113
114 t = now(CLOCK_MONOTONIC);
115 for (n_copying = 0;; n_copying++) {
116 transaction(b, csize, server_name);
117 if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
118 break;
119 }
120 printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
121
122 b->use_memfd = -1;
123
124 t = now(CLOCK_MONOTONIC);
125 for (n_memfd = 0;; n_memfd++) {
126 transaction(b, csize, server_name);
127 if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
128 break;
129 }
130 printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
131
132 if (n_copying == n_memfd)
133 break;
134
135 if (n_copying > n_memfd)
136 lsize = csize;
137 else
138 rsize = csize;
139 }
140
141 b->use_memfd = 1;
142 assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
143 assert_se(sd_bus_message_append(x, "t", csize) >= 0);
144 assert_se(sd_bus_send(b, x, NULL) >= 0);
145
146 sd_bus_unref(b);
147 }
148
149 static void client_chart(Type type, const char *address, const char *server_name, int fd) {
150 _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL;
151 size_t csize;
152 sd_bus *b;
153 int r;
154
155 r = sd_bus_new(&b);
156 assert_se(r >= 0);
157
158 if (type == TYPE_DIRECT) {
159 r = sd_bus_set_fd(b, fd, fd);
160 assert_se(r >= 0);
161 } else {
162 r = sd_bus_set_address(b, address);
163 assert_se(r >= 0);
164
165 r = sd_bus_set_bus_client(b, true);
166 assert_se(r >= 0);
167 }
168
169 r = sd_bus_start(b);
170 assert_se(r >= 0);
171
172 r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
173 assert_se(r >= 0);
174
175 switch (type) {
176 case TYPE_LEGACY:
177 printf("SIZE\tLEGACY\n");
178 break;
179 case TYPE_DIRECT:
180 printf("SIZE\tDIRECT\n");
181 break;
182 }
183
184 for (csize = 1; csize <= MAX_SIZE; csize *= 2) {
185 usec_t t;
186 unsigned n_memfd;
187
188 printf("%zu\t", csize);
189
190 t = now(CLOCK_MONOTONIC);
191 for (n_memfd = 0;; n_memfd++) {
192 transaction(b, csize, server_name);
193 if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
194 break;
195 }
196
197 printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
198 }
199
200 b->use_memfd = 1;
201 assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
202 assert_se(sd_bus_message_append(x, "t", csize) >= 0);
203 assert_se(sd_bus_send(b, x, NULL) >= 0);
204
205 sd_bus_unref(b);
206 }
207
208 int main(int argc, char *argv[]) {
209 enum {
210 MODE_BISECT,
211 MODE_CHART,
212 } mode = MODE_BISECT;
213 Type type = TYPE_LEGACY;
214 int i, pair[2] = { -1, -1 };
215 _cleanup_free_ char *address = NULL, *server_name = NULL;
216 _cleanup_close_ int bus_ref = -1;
217 const char *unique;
218 cpu_set_t cpuset;
219 size_t result;
220 sd_bus *b;
221 pid_t pid;
222 int r;
223
224 for (i = 1; i < argc; i++) {
225 if (streq(argv[i], "chart")) {
226 mode = MODE_CHART;
227 continue;
228 } else if (streq(argv[i], "legacy")) {
229 type = TYPE_LEGACY;
230 continue;
231 } else if (streq(argv[i], "direct")) {
232 type = TYPE_DIRECT;
233 continue;
234 }
235
236 assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0);
237 }
238
239 assert_se(arg_loop_usec > 0);
240
241 if (type == TYPE_LEGACY) {
242 const char *e;
243
244 e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
245 assert_se(e);
246
247 address = strdup(e);
248 assert_se(address);
249 }
250
251 r = sd_bus_new(&b);
252 assert_se(r >= 0);
253
254 if (type == TYPE_DIRECT) {
255 assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0);
256
257 r = sd_bus_set_fd(b, pair[0], pair[0]);
258 assert_se(r >= 0);
259
260 r = sd_bus_set_server(b, true, SD_ID128_NULL);
261 assert_se(r >= 0);
262 } else {
263 r = sd_bus_set_address(b, address);
264 assert_se(r >= 0);
265
266 r = sd_bus_set_bus_client(b, true);
267 assert_se(r >= 0);
268 }
269
270 r = sd_bus_start(b);
271 assert_se(r >= 0);
272
273 if (type != TYPE_DIRECT) {
274 r = sd_bus_get_unique_name(b, &unique);
275 assert_se(r >= 0);
276
277 server_name = strdup(unique);
278 assert_se(server_name);
279 }
280
281 sync();
282 setpriority(PRIO_PROCESS, 0, -19);
283
284 pid = fork();
285 assert_se(pid >= 0);
286
287 if (pid == 0) {
288 CPU_ZERO(&cpuset);
289 CPU_SET(0, &cpuset);
290 pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
291
292 safe_close(bus_ref);
293 sd_bus_unref(b);
294
295 switch (mode) {
296 case MODE_BISECT:
297 client_bisect(address, server_name);
298 break;
299
300 case MODE_CHART:
301 client_chart(type, address, server_name, pair[1]);
302 break;
303 }
304
305 _exit(EXIT_SUCCESS);
306 }
307
308 CPU_ZERO(&cpuset);
309 CPU_SET(1, &cpuset);
310 pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
311
312 server(b, &result);
313
314 if (mode == MODE_BISECT)
315 printf("Copying/memfd are equally fast at %zu bytes\n", result);
316
317 assert_se(waitpid(pid, NULL, 0) == pid);
318
319 safe_close(pair[1]);
320 sd_bus_unref(b);
321
322 return 0;
323 }