diff options
author | Christian Grothoff <christian@grothoff.org> | 2018-05-12 10:31:38 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2018-05-12 10:31:38 +0200 |
commit | 620bdac536dff4b17317d9d20b8a6840836d6490 (patch) | |
tree | c26d468621ac29b1d6d3b4cd0682b413bffca3aa /src | |
parent | e7b6bcdfd095ba3dfc3f8721a2a1f8a7f69d792b (diff) | |
download | gnunet-620bdac536dff4b17317d9d20b8a6840836d6490.tar.gz gnunet-620bdac536dff4b17317d9d20b8a6840836d6490.zip |
add first sketch of gns benchmarking tool
Diffstat (limited to 'src')
-rw-r--r-- | src/gns/.gitignore | 1 | ||||
-rw-r--r-- | src/gns/Makefile.am | 11 | ||||
-rw-r--r-- | src/gns/gnunet-gns-benchmark.c | 495 |
3 files changed, 507 insertions, 0 deletions
diff --git a/src/gns/.gitignore b/src/gns/.gitignore index 5aecfd51d..2b9a18f21 100644 --- a/src/gns/.gitignore +++ b/src/gns/.gitignore | |||
@@ -3,3 +3,4 @@ gnunet-bcd | |||
3 | gnunet-dns2gns | 3 | gnunet-dns2gns |
4 | gnunet-gns | 4 | gnunet-gns |
5 | gnunet-gns-proxy | 5 | gnunet-gns-proxy |
6 | gnunet-gns-benchmark | ||
diff --git a/src/gns/Makefile.am b/src/gns/Makefile.am index b2b81d73f..ac11c834e 100644 --- a/src/gns/Makefile.am +++ b/src/gns/Makefile.am | |||
@@ -76,6 +76,9 @@ bin_PROGRAMS = \ | |||
76 | $(DO_NONPOSIX_GNSIMPORT) \ | 76 | $(DO_NONPOSIX_GNSIMPORT) \ |
77 | gnunet-gns | 77 | gnunet-gns |
78 | 78 | ||
79 | noinst_PROGRAMS = \ | ||
80 | gnunet-gns-benchmark | ||
81 | |||
79 | if HAVE_MHD | 82 | if HAVE_MHD |
80 | if LINUX | 83 | if LINUX |
81 | bin_PROGRAMS += gnunet-bcd | 84 | bin_PROGRAMS += gnunet-bcd |
@@ -114,6 +117,14 @@ gnunet_gns_LDADD = \ | |||
114 | $(top_builddir)/src/util/libgnunetutil.la \ | 117 | $(top_builddir)/src/util/libgnunetutil.la \ |
115 | $(GN_LIBINTL) | 118 | $(GN_LIBINTL) |
116 | 119 | ||
120 | gnunet_gns_benchmark_SOURCES = \ | ||
121 | gnunet-gns-benchmark.c | ||
122 | gnunet_gns_benchmark_LDADD = \ | ||
123 | libgnunetgns.la \ | ||
124 | $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ | ||
125 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
126 | $(GN_LIBINTL) | ||
127 | |||
117 | 128 | ||
118 | gnunet_bcd_SOURCES = \ | 129 | gnunet_bcd_SOURCES = \ |
119 | gnunet-bcd.c | 130 | gnunet-bcd.c |
diff --git a/src/gns/gnunet-gns-benchmark.c b/src/gns/gnunet-gns-benchmark.c new file mode 100644 index 000000000..f1efaac09 --- /dev/null +++ b/src/gns/gnunet-gns-benchmark.c | |||
@@ -0,0 +1,495 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2018 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
18 | Boston, MA 02110-1301, USA. | ||
19 | */ | ||
20 | /** | ||
21 | * @file src/gns/gnunet-gns-benchmark.c | ||
22 | * @brief issue many queries to GNS and compute performance statistics | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include <gnunet_util_lib.h> | ||
27 | #include <gnunet_gnsrecord_lib.h> | ||
28 | #include <gnunet_gns_service.h> | ||
29 | |||
30 | |||
31 | /** | ||
32 | * How long do we wait at least between requests by default? | ||
33 | */ | ||
34 | #define DEF_REQUEST_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 1) | ||
35 | |||
36 | /** | ||
37 | * How long do we wait until we consider a request failed by default? | ||
38 | */ | ||
39 | #define DEF_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 1) | ||
40 | |||
41 | |||
42 | /** | ||
43 | * We distinguish between different categories of | ||
44 | * requests, for which we track statistics separately. | ||
45 | * However, this process does not change how it acts | ||
46 | * based on the category. | ||
47 | */ | ||
48 | enum RequestCategory | ||
49 | { | ||
50 | RC_SHARED = 0, | ||
51 | RC_PRIVATE = 1, | ||
52 | /** | ||
53 | * Must be last and match number of categories. | ||
54 | */ | ||
55 | RC_MAX = 2 | ||
56 | }; | ||
57 | |||
58 | |||
59 | /** | ||
60 | * Request we should make. We keep this struct in memory per request, | ||
61 | * thus optimizing it is crucial for the overall memory consumption of | ||
62 | * the zone importer. | ||
63 | */ | ||
64 | struct Request | ||
65 | { | ||
66 | |||
67 | /** | ||
68 | * Active requests are kept in a DLL. | ||
69 | */ | ||
70 | struct Request *next; | ||
71 | |||
72 | /** | ||
73 | * Active requests are kept in a DLL. | ||
74 | */ | ||
75 | struct Request *prev; | ||
76 | |||
77 | /** | ||
78 | * Socket used to make the request, NULL if not active. | ||
79 | */ | ||
80 | struct GNUNET_GNS_LookupWithTldRequest *lr; | ||
81 | |||
82 | /** | ||
83 | * Hostname we are resolving, allocated at the end of | ||
84 | * this struct (optimizing memory consumption by reducing | ||
85 | * total number of allocations). | ||
86 | */ | ||
87 | char *hostname; | ||
88 | |||
89 | /** | ||
90 | * While we are fetching the record, the value is set to the | ||
91 | * starting time of the GNS operation. | ||
92 | */ | ||
93 | struct GNUNET_TIME_Absolute op_start_time; | ||
94 | |||
95 | /** | ||
96 | * Observed latency, set once we got a reply. | ||
97 | */ | ||
98 | struct GNUNET_TIME_Relative latency; | ||
99 | |||
100 | /** | ||
101 | * Category of the request. | ||
102 | */ | ||
103 | enum RequestCategory cat; | ||
104 | |||
105 | }; | ||
106 | |||
107 | |||
108 | /** | ||
109 | * GNS handle. | ||
110 | */ | ||
111 | static struct GNUNET_GNS_Handle *gns; | ||
112 | |||
113 | /** | ||
114 | * Number of lookups we performed overall per category. | ||
115 | */ | ||
116 | static unsigned int lookups[RC_MAX]; | ||
117 | |||
118 | /** | ||
119 | * Number of replies we got per category. | ||
120 | */ | ||
121 | static unsigned int replies[RC_MAX]; | ||
122 | |||
123 | /** | ||
124 | * Number of replies we got per category. | ||
125 | */ | ||
126 | static unsigned int failures[RC_MAX]; | ||
127 | |||
128 | /** | ||
129 | * Sum of the observed latencies of successful queries, | ||
130 | * per category. | ||
131 | */ | ||
132 | static struct GNUNET_TIME_Relative latency_sum[RC_MAX]; | ||
133 | |||
134 | /** | ||
135 | * Active requests are kept in a DLL. | ||
136 | */ | ||
137 | static struct Request *act_head; | ||
138 | |||
139 | /** | ||
140 | * Active requests are kept in a DLL. | ||
141 | */ | ||
142 | static struct Request *act_tail; | ||
143 | |||
144 | /** | ||
145 | * Completed successful requests are kept in a DLL. | ||
146 | */ | ||
147 | static struct Request *succ_head; | ||
148 | |||
149 | /** | ||
150 | * Completed successful requests are kept in a DLL. | ||
151 | */ | ||
152 | static struct Request *succ_tail; | ||
153 | |||
154 | /** | ||
155 | * Yet to be started requests are kept in a DLL. | ||
156 | */ | ||
157 | static struct Request *todo_head; | ||
158 | |||
159 | /** | ||
160 | * Yet to be started requests are kept in a DLL. | ||
161 | */ | ||
162 | static struct Request *todo_tail; | ||
163 | |||
164 | /** | ||
165 | * Main task. | ||
166 | */ | ||
167 | static struct GNUNET_SCHEDULER_Task *t; | ||
168 | |||
169 | /** | ||
170 | * Delay between requests. | ||
171 | */ | ||
172 | static struct GNUNET_TIME_Relative request_delay; | ||
173 | |||
174 | /** | ||
175 | * Timeout for requests. | ||
176 | */ | ||
177 | static struct GNUNET_TIME_Relative timeout; | ||
178 | |||
179 | |||
180 | /** | ||
181 | * Free @a req and data structures reachable from it. | ||
182 | * | ||
183 | * @param req request to free | ||
184 | */ | ||
185 | static void | ||
186 | free_request (struct Request *req) | ||
187 | { | ||
188 | if (NULL != req->lr) | ||
189 | GNUNET_GNS_lookup_with_tld_cancel (req->lr); | ||
190 | GNUNET_free (req); | ||
191 | } | ||
192 | |||
193 | |||
194 | /** | ||
195 | * Function called with the result of a GNS resolution. | ||
196 | * | ||
197 | * @param cls closure with the `struct Request` | ||
198 | * @param gns_tld #GNUNET_YES if GNS lookup was attempted | ||
199 | * @param rd_count number of records in @a rd | ||
200 | * @param rd the records in reply | ||
201 | */ | ||
202 | static void | ||
203 | process_result (void *cls, | ||
204 | int gns_tld, | ||
205 | uint32_t rd_count, | ||
206 | const struct GNUNET_GNSRECORD_Data *rd) | ||
207 | { | ||
208 | struct Request *req = cls; | ||
209 | |||
210 | (void) gns_tld; | ||
211 | (void) rd_count; | ||
212 | (void) rd; | ||
213 | req->lr = NULL; | ||
214 | req->latency = GNUNET_TIME_absolute_get_duration (req->op_start_time); | ||
215 | GNUNET_CONTAINER_DLL_remove (act_head, | ||
216 | act_tail, | ||
217 | req); | ||
218 | GNUNET_CONTAINER_DLL_insert (succ_head, | ||
219 | succ_tail, | ||
220 | req); | ||
221 | replies[req->cat]++; | ||
222 | latency_sum[req->cat] | ||
223 | = GNUNET_TIME_relative_add (latency_sum[req->cat], | ||
224 | req->latency); | ||
225 | } | ||
226 | |||
227 | |||
228 | /** | ||
229 | * Process request from the queue. | ||
230 | * | ||
231 | * @param cls NULL | ||
232 | */ | ||
233 | static void | ||
234 | process_queue (void *cls) | ||
235 | { | ||
236 | struct Request *req; | ||
237 | struct GNUNET_TIME_Relative duration; | ||
238 | |||
239 | (void) cls; | ||
240 | /* check for expired requests */ | ||
241 | while (NULL != (req = act_head)) | ||
242 | { | ||
243 | duration = GNUNET_TIME_absolute_get_duration (req->op_start_time); | ||
244 | if (duration.rel_value_us < timeout.rel_value_us) | ||
245 | break; | ||
246 | GNUNET_CONTAINER_DLL_remove (act_head, | ||
247 | act_tail, | ||
248 | req); | ||
249 | failures[req->cat]++; | ||
250 | free_request (req); | ||
251 | } | ||
252 | if (NULL == (req = todo_head)) | ||
253 | { | ||
254 | struct GNUNET_TIME_Absolute at; | ||
255 | |||
256 | if (NULL == (req = act_head)) | ||
257 | { | ||
258 | GNUNET_SCHEDULER_shutdown (); | ||
259 | return; | ||
260 | } | ||
261 | at = GNUNET_TIME_absolute_add (req->op_start_time, | ||
262 | timeout); | ||
263 | t = GNUNET_SCHEDULER_add_at (at, | ||
264 | &process_queue, | ||
265 | NULL); | ||
266 | return; | ||
267 | } | ||
268 | GNUNET_CONTAINER_DLL_remove (todo_head, | ||
269 | todo_tail, | ||
270 | req); | ||
271 | GNUNET_CONTAINER_DLL_insert_tail (act_head, | ||
272 | act_tail, | ||
273 | req); | ||
274 | lookups[req->cat]++; | ||
275 | req->op_start_time = GNUNET_TIME_absolute_get (); | ||
276 | req->lr = GNUNET_GNS_lookup_with_tld (gns, | ||
277 | req->hostname, | ||
278 | GNUNET_GNSRECORD_TYPE_ANY, | ||
279 | GNUNET_GNS_LO_DEFAULT, | ||
280 | &process_result, | ||
281 | req); | ||
282 | t = GNUNET_SCHEDULER_add_delayed (request_delay, | ||
283 | &process_queue, | ||
284 | NULL); | ||
285 | } | ||
286 | |||
287 | |||
288 | /** | ||
289 | * Clean up and terminate the process. | ||
290 | * | ||
291 | * @param cls NULL | ||
292 | */ | ||
293 | static void | ||
294 | do_shutdown (void *cls) | ||
295 | { | ||
296 | struct Request *req; | ||
297 | |||
298 | (void) cls; | ||
299 | /* FIXME: calculate statistics */ | ||
300 | if (NULL != gns) | ||
301 | { | ||
302 | GNUNET_GNS_disconnect (gns); | ||
303 | gns = NULL; | ||
304 | } | ||
305 | if (NULL != t) | ||
306 | { | ||
307 | GNUNET_SCHEDULER_cancel (t); | ||
308 | t = NULL; | ||
309 | } | ||
310 | while (NULL != (req = act_head)) | ||
311 | { | ||
312 | GNUNET_CONTAINER_DLL_remove (act_head, | ||
313 | act_tail, | ||
314 | req); | ||
315 | free_request (req); | ||
316 | } | ||
317 | while (NULL != (req = succ_head)) | ||
318 | { | ||
319 | GNUNET_CONTAINER_DLL_remove (succ_head, | ||
320 | succ_tail, | ||
321 | req); | ||
322 | free_request (req); | ||
323 | } | ||
324 | while (NULL != (req = todo_head)) | ||
325 | { | ||
326 | GNUNET_CONTAINER_DLL_remove (todo_head, | ||
327 | todo_tail, | ||
328 | req); | ||
329 | free_request (req); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | |||
334 | /** | ||
335 | * Add @a hostname to the list of requests to be made. | ||
336 | * | ||
337 | * @param hostname name to resolve | ||
338 | * @param cat category of the @a hostname | ||
339 | */ | ||
340 | static void | ||
341 | queue (const char *hostname, | ||
342 | enum RequestCategory cat) | ||
343 | { | ||
344 | struct Request *req; | ||
345 | const char *dot; | ||
346 | size_t hlen; | ||
347 | |||
348 | dot = strchr (hostname, | ||
349 | (unsigned char) '.'); | ||
350 | if (NULL == dot) | ||
351 | { | ||
352 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
353 | "Refusing invalid hostname `%s' (lacks '.')\n", | ||
354 | hostname); | ||
355 | return; | ||
356 | } | ||
357 | hlen = strlen (hostname) + 1; | ||
358 | req = GNUNET_malloc (sizeof (struct Request) + hlen); | ||
359 | req->cat = cat; | ||
360 | req->hostname = (char *) &req[1]; | ||
361 | memcpy (req->hostname, | ||
362 | hostname, | ||
363 | hlen); | ||
364 | GNUNET_CONTAINER_DLL_insert (todo_head, | ||
365 | todo_tail, | ||
366 | req); | ||
367 | } | ||
368 | |||
369 | |||
370 | /** | ||
371 | * Begin processing hostnames from stdin. | ||
372 | * | ||
373 | * @param cls NULL | ||
374 | */ | ||
375 | static void | ||
376 | process_stdin (void *cls) | ||
377 | { | ||
378 | static struct GNUNET_TIME_Absolute last; | ||
379 | static uint64_t idot; | ||
380 | char hn[270]; | ||
381 | |||
382 | (void) cls; | ||
383 | t = NULL; | ||
384 | while (NULL != | ||
385 | fgets (hn, | ||
386 | sizeof (hn), | ||
387 | stdin)) | ||
388 | { | ||
389 | if (strlen(hn) > 0) | ||
390 | hn[strlen(hn)-1] = '\0'; /* eat newline */ | ||
391 | if (0 == idot) | ||
392 | last = GNUNET_TIME_absolute_get (); | ||
393 | idot++; | ||
394 | if (0 == idot % 10000) | ||
395 | { | ||
396 | struct GNUNET_TIME_Relative delta; | ||
397 | |||
398 | delta = GNUNET_TIME_absolute_get_duration (last); | ||
399 | last = GNUNET_TIME_absolute_get (); | ||
400 | fprintf (stderr, | ||
401 | "Imported 10000 records in %s\n", | ||
402 | GNUNET_STRINGS_relative_time_to_string (delta, | ||
403 | GNUNET_YES)); | ||
404 | } | ||
405 | queue (hn, | ||
406 | RC_SHARED); // FIXME: parse input line! | ||
407 | } | ||
408 | fprintf (stderr, | ||
409 | "Done reading %llu domain names\n", | ||
410 | (unsigned long long) idot); | ||
411 | t = GNUNET_SCHEDULER_add_now (&process_queue, | ||
412 | NULL); | ||
413 | } | ||
414 | |||
415 | |||
416 | /** | ||
417 | * Process requests from the queue, then if the queue is | ||
418 | * not empty, try again. | ||
419 | * | ||
420 | * @param cls NULL | ||
421 | * @param args remaining command-line arguments | ||
422 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
423 | * @param cfg configuration | ||
424 | */ | ||
425 | static void | ||
426 | run (void *cls, | ||
427 | char *const *args, | ||
428 | const char *cfgfile, | ||
429 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
430 | { | ||
431 | (void) cls; | ||
432 | (void) args; | ||
433 | (void) cfgfile; | ||
434 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, | ||
435 | NULL); | ||
436 | gns = GNUNET_GNS_connect (cfg); | ||
437 | if (NULL == gns) | ||
438 | { | ||
439 | GNUNET_break (0); | ||
440 | GNUNET_SCHEDULER_shutdown (); | ||
441 | return; | ||
442 | } | ||
443 | t = GNUNET_SCHEDULER_add_now (&process_stdin, | ||
444 | NULL); | ||
445 | } | ||
446 | |||
447 | |||
448 | /** | ||
449 | * Call with list of names with numeric category to query. | ||
450 | * | ||
451 | * @param argc unused | ||
452 | * @param argv unused | ||
453 | * @return 0 on success | ||
454 | */ | ||
455 | int | ||
456 | main (int argc, | ||
457 | char *const*argv) | ||
458 | { | ||
459 | int ret = 0; | ||
460 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
461 | GNUNET_GETOPT_option_relative_time ('d', | ||
462 | "delay", | ||
463 | "RELATIVETIME", | ||
464 | gettext_noop ("how long to wait between queries"), | ||
465 | &request_delay), | ||
466 | GNUNET_GETOPT_option_relative_time ('t', | ||
467 | "timeout", | ||
468 | "RELATIVETIME", | ||
469 | gettext_noop ("how long to wait for an answer"), | ||
470 | &timeout), | ||
471 | GNUNET_GETOPT_OPTION_END | ||
472 | }; | ||
473 | |||
474 | if (GNUNET_OK != | ||
475 | GNUNET_STRINGS_get_utf8_args (argc, argv, | ||
476 | &argc, &argv)) | ||
477 | return 2; | ||
478 | timeout = DEF_TIMEOUT; | ||
479 | request_delay = DEF_REQUEST_DELAY; | ||
480 | if (GNUNET_OK != | ||
481 | GNUNET_PROGRAM_run (argc, | ||
482 | argv, | ||
483 | "gnunet-gns-benchmark", | ||
484 | "resolve GNS names and measure performance", | ||
485 | options, | ||
486 | &run, | ||
487 | NULL)) | ||
488 | ret = 1; | ||
489 | GNUNET_free ((void*) argv); | ||
490 | fprintf (stderr, | ||
491 | "Statistics here\n"); | ||
492 | return ret; | ||
493 | } | ||
494 | |||
495 | /* end of gnunet-gns-benchmark.c */ | ||