basic benchmark functionality working
[oweals/gnunet.git] / src / ats / perf_ats_solver.c
1 /*
2  This file is part of GNUnet.
3  (C) 2010,2011 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18  Boston, MA 02111-1307, USA.
19  */
20 /**
21  * @file ats/perf_ats_solver.c
22  * @brief generic performance test for ATS solvers
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_statistics_service.h"
29 #include "gnunet-service-ats_addresses.h"
30 #include "gnunet-service-ats_normalization.h"
31 #include "gnunet_ats_service.h"
32 #include "gnunet_ats_plugin.h"
33 #include "test_ats_api_common.h"
34
35 #define DEFAULT_PEERS_START     10
36 #define DEFAULT_PEERS_END       10
37 #define DEFAULT_ADDRESSES       10
38 #define DEFAULT_ATS_COUNT       2
39
40 /**
41  * Handle for ATS address component
42  */
43 struct PerfHandle
44 {
45   struct PerfPeer *peers;
46
47   /**
48    * #peers to start benchmarking with
49    */
50   int N_peers_start;
51
52   /**
53    * #peers to end benchmarking with
54    */
55   int N_peers_end;
56
57   /**
58    * #addresses to benchmarking with
59    */
60   int N_address;
61
62   int opt_numeric;
63   int opt_dump;
64   int opt_update_percent;
65   int opt_update_quantity;
66
67   int bulk_running;
68
69   char *ats_string;
70
71   /**
72    *
73    */
74   struct GNUNET_STATISTICS_Handle *stat;
75
76   /**
77    * A multihashmap to store all addresses
78    */
79   struct GNUNET_CONTAINER_MultiPeerMap *addresses;
80
81
82   /**
83    * Configured ATS solver
84    */
85   int ats_mode;
86
87   /**
88    *  Solver handle
89    */
90   void *solver;
91
92   /**
93    * Address suggestion requests DLL head
94    */
95   struct GAS_Addresses_Suggestion_Requests *r_head;
96
97   /**
98    * Address suggestion requests DLL tail
99    */
100   struct GAS_Addresses_Suggestion_Requests *r_tail;
101
102   /* Solver functions */
103   struct GNUNET_ATS_PluginEnvironment env;
104
105   char *plugin;
106 };
107
108 struct PerfPeer
109 {
110   struct GNUNET_PeerIdentity id;
111
112   struct ATS_Address *head;
113   struct ATS_Address *tail;
114 };
115
116 static struct PerfHandle ph;
117
118 /**
119  * Return value
120  */
121 static int ret;
122
123
124 /**
125  * ATS information
126  */
127 //static struct GNUNET_ATS_Information ats[2];
128
129
130 static void
131 end_now (int res)
132 {
133   if (NULL != ph.stat)
134   {
135     GNUNET_STATISTICS_destroy (ph.stat, GNUNET_NO);
136     ph.stat = NULL;
137   }
138   /*
139    if (NULL != addresses)
140    {
141    GNUNET_CONTAINER_multihashmap_iterate (addresses, &addr_it, NULL);
142    GNUNET_CONTAINER_multihashmap_destroy (addresses);
143    addresses = NULL ;
144    }*/
145   if (NULL != ph.peers)
146   {
147     GNUNET_free(ph.peers);
148   }
149
150   GAS_normalization_stop ();
151   ret = res;
152 }
153
154
155 static void
156 perf_create_peer (int cp)
157 {
158
159   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
160       &ph.peers[cp].id, sizeof (struct GNUNET_PeerIdentity));
161   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Creating peer #%u: %s \n", cp,
162       GNUNET_i2s (&ph.peers[cp].id));
163 }
164
165
166
167 static void
168 perf_update_address (struct ATS_Address *cur)
169 {
170   int r_type;
171   int r_val;
172
173   r_type = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 2);
174   switch (r_type)
175   {
176   case 0:
177     r_val = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100);
178     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
179         "Updating peer `%s' address %p type %s val %u\n",
180         GNUNET_i2s (&cur->peer), cur, "GNUNET_ATS_QUALITY_NET_DELAY", r_val);
181     ph.env.sf.s_address_update_property (ph.solver, cur,
182         GNUNET_ATS_QUALITY_NET_DELAY,
183         r_val, (double) (100 + r_val / 100));
184     break;
185   case 1:
186     r_val = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 10);
187
188     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
189         "Updating peer `%s' address %p type %s val %u\n",
190         GNUNET_i2s (&cur->peer), cur, "GNUNET_ATS_QUALITY_NET_DISTANCE", r_val);
191     ph.env.sf.s_address_update_property (ph.solver, cur,
192         GNUNET_ATS_QUALITY_NET_DISTANCE,
193         r_val, (double) (100 + r_val) / 100);
194     break;
195   default:
196     break;
197   }
198   ph.env.sf.s_address_update_inuse (ph.solver, cur, GNUNET_YES);
199 }
200
201
202
203 static void
204 bandwidth_changed_cb (void *cls, struct ATS_Address *address)
205 {
206   if (0 == ntohl(address->assigned_bw_out.value__) &&
207       0 == ntohl(address->assigned_bw_in.value__))
208     return;
209
210   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
211       "Bandwidth changed addresses %s %p to %llu Bps out / %llu Bps in\n",
212       GNUNET_i2s (&address->peer),
213       address,
214       ntohl(address->assigned_bw_out.value__),
215       ntohl(address->assigned_bw_in.value__));
216   if (GNUNET_YES == ph.bulk_running)
217     GNUNET_break (0);
218   return;
219 }
220
221 const double *
222 get_preferences_cb (void *cls, const struct GNUNET_PeerIdentity *id)
223 {
224   return GAS_normalization_get_preferences (id);
225 }
226
227
228 const double *
229 get_property_cb (void *cls, const struct ATS_Address *address)
230 {
231   return GAS_normalization_get_properties ((struct ATS_Address *) address);
232 }
233
234 static void
235 normalized_property_changed_cb (void *cls, struct ATS_Address *peer,
236     uint32_t type, double prop_rel)
237 {
238   /* TODO */
239 }
240
241 static void
242 perf_address_initial_update (void *solver,
243     struct GNUNET_CONTAINER_MultiPeerMap * addresses,
244     struct ATS_Address *address)
245 {
246   ph.env.sf.s_address_update_property (solver, address, GNUNET_ATS_QUALITY_NET_DELAY,
247       100,
248       (double) (100 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100))
249           / 100);
250
251   ph.env.sf.s_address_update_property (solver, address,
252       GNUNET_ATS_QUALITY_NET_DISTANCE, 10,
253       (double) (100 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100))
254           / 100);
255 }
256
257 static void
258 perf_update_all_addresses (unsigned int cp, unsigned int ca, unsigned int up_q)
259 {
260   struct ATS_Address *cur;
261   int c_peer;
262   int c_select;
263   int c_addr;
264   int r;
265
266   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
267       "Updating addresses %u addresses per peer \n", up_q);
268   unsigned int m[ca];
269
270   for (c_peer = 0; c_peer < cp; c_peer++)
271   {
272     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Updating peer `%s'\n",
273         GNUNET_i2s (&ph.peers[c_peer].id));
274     for (c_select = 0; c_select < ca; c_select++)
275       m[c_select] = 0;
276     c_select = 0;
277     while (c_select < ph.opt_update_quantity)
278     {
279       r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, ca);
280       if (0 == m[r])
281       {
282         m[r] = 1;
283         c_select++;
284       }
285     }
286
287     c_addr = 0;
288     for (cur = ph.peers[c_peer].head; NULL != cur; cur = cur->next)
289     {
290       if (1 == m[c_addr])
291         perf_update_address (cur);
292       c_addr++;
293     }
294   }
295 }
296
297
298 static struct ATS_Address *
299 perf_create_address (int cp, int ca)
300 {
301   struct ATS_Address *a;
302   a = create_address (&ph.peers[cp].id,
303       "Test 1", "test 1", strlen ("test 1") + 1, 0);
304   GNUNET_CONTAINER_DLL_insert (ph.peers[cp].head, ph.peers[cp].tail, a);
305   GNUNET_CONTAINER_multipeermap_put (ph.addresses, &ph.peers[cp].id, a,
306       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
307   return a;
308 }
309
310 static void
311 perf_run ()
312 {
313   struct ATS_Address *cur;
314   struct ATS_Address *next;
315   int cp;
316   int ca;
317   int count_p = ph.N_peers_end;
318   int count_a = ph.N_address;
319   struct ATS_Address * cur_addr;
320   struct GNUNET_TIME_Absolute start;
321   struct GNUNET_TIME_Absolute end;
322   struct GNUNET_TIME_Relative delta;
323
324   ph.peers = GNUNET_malloc ((count_p) * sizeof (struct PerfPeer));
325
326   for (cp = 0; cp < count_p; cp++)
327     perf_create_peer (cp);
328   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
329       "Added %u peers\n", cp);
330
331   /* Set initial bulk start to not solve */
332   ph.env.sf.s_bulk_start (ph.solver);
333   ph.bulk_running = GNUNET_YES;
334
335   for (cp = 0; cp < count_p; cp++)
336   {
337     for (ca = 0; ca < count_a; ca++)
338     {
339       cur_addr = perf_create_address (cp, ca);
340       /* Add address */
341       ph.env.sf.s_add (ph.solver, cur_addr, GNUNET_ATS_NET_LAN);
342       perf_address_initial_update (ph.solver, ph.addresses, cur_addr);
343       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
344           "Adding address for peer %u address %u\n", cp, ca);
345     }
346     /* Notify solver about request */
347     ph.env.sf.s_get (ph.solver, &ph.peers[cp].id);
348
349     if (cp + 1 >= ph.N_peers_start)
350     {
351       /* Disable bulk to solve the problem */
352       if (GNUNET_YES == ph.bulk_running)
353       {
354         start = GNUNET_TIME_absolute_get();
355         ph.bulk_running = GNUNET_NO;
356         ph.env.sf.s_bulk_stop (ph.solver);
357       }
358       else
359       {
360         GNUNET_break (0);
361       }
362
363       /* Problem is solved by the solver here due to unlocking */
364
365       /* Disable bulk to solve the problem */
366       if (GNUNET_NO == ph.bulk_running)
367       {
368
369         end = GNUNET_TIME_absolute_get();
370         delta = GNUNET_TIME_absolute_get_difference(start, end);
371         fprintf (stderr, "Solver took %llu us to solve problem with %u peers and %u addresses per peer\n",
372             (unsigned long long) delta.rel_value_us,
373             cp + 1, ca);
374         ph.env.sf.s_bulk_start (ph.solver);
375         ph.bulk_running = GNUNET_YES;
376       }
377 #if 0
378       if ((0 < ph.opt_update_quantity) || (0 < ph.opt_update_percent))
379       {
380         /* Update */
381         GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
382             "Updating problem with %u peers and %u addresses\n", cp + 1, ca);
383         //ph.env.sf.s_bulk_start (ph.solver);
384         //update_addresses (cp + 1, ca, ph.opt_update_quantity);
385         //ph.env.sf.s_bulk_stop (ph.solver);
386       }
387 #endif
388     }
389   }
390   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
391       "Done, cleaning up addresses\n");
392   if (GNUNET_NO == ph.bulk_running)
393   {
394     ph.env.sf.s_bulk_start (ph.solver);
395     ph.bulk_running = GNUNET_YES;
396   }
397
398   for (cp = 0; cp < count_p; cp++)
399   {
400     for (cur = ph.peers[cp].head; cur != NULL ; cur = next)
401     {
402       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
403           "Deleting addresses for peer %u\n", cp);
404       GNUNET_CONTAINER_multipeermap_remove (ph.addresses, &ph.peers[cp].id, cur);
405       ph.env.sf.s_del (ph.solver, cur, GNUNET_NO);
406       next = cur->next;
407       GNUNET_CONTAINER_DLL_remove(ph.peers[cp].head, ph.peers[cp].tail, cur);
408       GNUNET_free(cur);
409     }
410
411   }
412   GNUNET_free(ph.peers);
413 }
414
415
416 static void
417 run (void *cls, char * const *args, const char *cfgfile,
418     const struct GNUNET_CONFIGURATION_Handle *cfg)
419 {
420   GNUNET_log_setup ("perf-ats-solver", "WARNING", NULL);
421   char *sep;
422   char *src_filename = GNUNET_strdup (__FILE__);
423   char *test_filename = cls;
424   char *solver;
425   char *plugin;
426   unsigned long long quotas_in[GNUNET_ATS_NetworkTypeCount];
427   unsigned long long quotas_out[GNUNET_ATS_NetworkTypeCount];
428   int c;
429
430   /* Extract test name */
431   if (NULL == (sep  = (strstr (src_filename,".c"))))
432   {
433     GNUNET_break (0);
434     ret = 1;
435     return;
436   }
437   sep[0] = '\0';
438
439   if (NULL != (sep = strstr (test_filename, ".exe")))
440     sep[0] = '\0';
441
442   if (NULL == (solver = strstr (test_filename, src_filename)))
443   {
444     GNUNET_break (0);
445     ret = 1;
446     return ;
447   }
448   solver += strlen (src_filename) +1;
449
450   if (0 == strcmp(solver, "proportional"))
451   {
452     ph.ats_mode = MODE_PROPORTIONAL;
453     ph.ats_string = "proportional";
454   }
455   else if (0 == strcmp(solver, "mlp"))
456   {
457     ph.ats_mode = MODE_MLP;
458     ph.ats_string = "mlp";
459   }
460   else if ((0 == strcmp(solver, "ril")))
461   {
462     ph.ats_mode = MODE_RIL;
463     ph.ats_string = "ril";
464   }
465   else
466   {
467     GNUNET_free (src_filename);
468     GNUNET_break (0);
469     ret = 1;
470     return ;
471   }
472   GNUNET_free (src_filename);
473
474   /* Calculcate peers */
475   if ((0 == ph.N_peers_start) && (0 == ph.N_peers_end))
476   {
477     ph.N_peers_start = DEFAULT_PEERS_START;
478     ph.N_peers_end = DEFAULT_PEERS_END;
479   }
480   if (0 == ph.N_address)
481     ph.N_address = DEFAULT_ADDRESSES;
482
483   if (ph.opt_update_quantity > ph.N_address)
484   {
485     fprintf (stderr,
486         _("Trying to update more addresses than we have per peer! (%u vs %u)"),
487         ph.opt_update_quantity, ph.N_address);
488     exit (1);
489   }
490
491   if (ph.N_peers_start != ph.N_peers_end)
492     fprintf (stderr, "Benchmarking solver `%s' with %u to %u peers and %u addresses\n",
493         ph.ats_string, ph.N_peers_start, ph.N_peers_end, ph.N_address);
494   else
495     fprintf (stderr, "Benchmarking solver `%s' with %u peers and %u addresses\n",
496         ph.ats_string, ph.N_peers_end, ph.N_address);
497
498   /* Load quotas */
499   if (GNUNET_ATS_NetworkTypeCount != load_quotas (cfg,
500       quotas_out, quotas_in, GNUNET_ATS_NetworkTypeCount))
501   {
502     GNUNET_break(0);
503     end_now (1);
504     return;
505   }
506
507   /* Load solver */
508   ph.env.cfg = cfg;
509   ph.stat = GNUNET_STATISTICS_create ("ats", cfg);
510   ph.env.stats = ph.stat;
511   ph.addresses = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
512   ph.env.addresses = ph.addresses;
513   ph.env.bandwidth_changed_cb = bandwidth_changed_cb;
514   ph.env.get_preferences = &get_preferences_cb;
515   ph.env.get_property = &get_property_cb;
516   ph.env.network_count = GNUNET_ATS_NetworkTypeCount;
517   int networks[GNUNET_ATS_NetworkTypeCount] = GNUNET_ATS_NetworkType;
518   for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
519   {
520     ph.env.networks[c] = networks[c];
521     ph.env.out_quota[c] = quotas_out[c];
522     ph.env.in_quota[c] = quotas_in[c];
523     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Loading network quotas: `%s' %llu %llu \n",
524         GNUNET_ATS_print_network_type(ph.env.networks[c]),
525         ph.env.out_quota[c],
526         ph.env.in_quota[c]);
527   }
528   GAS_normalization_start (NULL, NULL, &normalized_property_changed_cb, NULL );
529
530   GNUNET_asprintf (&plugin, "libgnunet_plugin_ats_%s", ph.ats_string);
531   GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Initializing solver `%s'\n"), ph.ats_string);
532   if  (NULL == (ph.solver = GNUNET_PLUGIN_load (plugin, &ph.env)))
533   {
534     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Failed to initialize solver `%s'!\n"), plugin);
535     ret = 1;
536     return;
537   }
538
539   /* Do work */
540   perf_run ();
541
542   /* Unload solver*/
543   GNUNET_log(GNUNET_ERROR_TYPE_INFO, _("Unloading solver `%s'\n"), ph.ats_string);
544   GNUNET_PLUGIN_unload (plugin, ph.solver);
545   GNUNET_free (plugin);
546   ph.solver = NULL;
547 }
548
549 int
550 main (int argc, char *argv[])
551 {
552   /* extract command line arguments */
553   ph.opt_dump = GNUNET_NO;
554   ph.opt_update_quantity = 0;
555   ph.opt_update_percent = 0;
556   ph.N_peers_start = 0;
557   ph.N_peers_end = 0;
558   ph.N_address = 0;
559   ph.ats_string = NULL;
560
561   static struct GNUNET_GETOPT_CommandLineOption options[] = {
562       { 'a', "addresses", NULL,
563           gettext_noop ("addresses to use"),
564           1, &GNUNET_GETOPT_set_uint, &ph.N_address },
565       { 's', "start", NULL,
566           gettext_noop ("start with peer"),
567           1, &GNUNET_GETOPT_set_uint, &ph.N_peers_start },
568       { 'e', "end", NULL,
569           gettext_noop ("end with peer"),
570           1, &GNUNET_GETOPT_set_uint, &ph.N_peers_end },
571       { 'p', "percentage", NULL,
572           gettext_noop ("update a fix percentage of addresses"),
573           1, &GNUNET_GETOPT_set_uint, &ph.opt_update_percent },
574       { 'q', "quantity", NULL,
575           gettext_noop ("update a fix quantity of addresses"),
576           1, &GNUNET_GETOPT_set_uint, &ph.opt_update_quantity },
577       GNUNET_GETOPT_OPTION_END
578   };
579
580   GNUNET_PROGRAM_run (argc, argv, argv[0], NULL, options, &run, argv[0]);
581
582   return ret;
583 }
584
585 /* end of file perf_ats_solver.c */