perf_ats.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2010-2013, 2016 GNUnet e.V.
  4. GNUnet is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Affero General Public License as published
  6. by the Free Software Foundation, either version 3 of the License,
  7. or (at your option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. SPDX-License-Identifier: AGPL3.0-or-later
  15. */
  16. /**
  17. * @file ats/perf_ats.c
  18. * @brief ats benchmark: start peers and modify preferences, monitor change over time
  19. * @author Christian Grothoff
  20. * @author Matthias Wachs
  21. */
  22. #include "platform.h"
  23. #include "gnunet_util_lib.h"
  24. #include "gnunet_testbed_service.h"
  25. #include "gnunet_ats_service.h"
  26. #include "gnunet_core_service.h"
  27. #include "ats-testing.h"
  28. #define TEST_ATS_PREFRENCE_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
  29. #define TEST_ATS_PREFRENCE_START 1.0
  30. #define TEST_ATS_PREFRENCE_DELTA 1.0
  31. #define TEST_MESSAGE_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
  32. #define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
  33. #define BENCHMARK_DURATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
  34. #define LOGGING_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500)
  35. #define TESTNAME_PREFIX "perf_ats_"
  36. #define DEFAULT_SLAVES_NUM 2
  37. #define DEFAULT_MASTERS_NUM 1
  38. /**
  39. * timeout task
  40. */
  41. static struct GNUNET_SCHEDULER_Task *timeout_task;
  42. /**
  43. * Progress task
  44. */
  45. static struct GNUNET_SCHEDULER_Task *progress_task;
  46. /**
  47. * Test result
  48. */
  49. static int result;
  50. /**
  51. * Test result logging
  52. */
  53. static int logging;
  54. /**
  55. * Test core (#GNUNET_YES) or transport (#GNUNET_NO)
  56. */
  57. static int test_core;
  58. /**
  59. * Solver string
  60. */
  61. static char *solver;
  62. /**
  63. * Preference string
  64. */
  65. static char *testname;
  66. /**
  67. * Preference string
  68. */
  69. static char *pref_str;
  70. /**
  71. * ATS preference value
  72. */
  73. static int pref_val;
  74. /**
  75. * Benchmark duration
  76. */
  77. static struct GNUNET_TIME_Relative perf_duration;
  78. /**
  79. * Logging frequency
  80. */
  81. static struct GNUNET_TIME_Relative log_frequency;
  82. /**
  83. * Number master peers
  84. */
  85. static unsigned int num_masters;
  86. /**
  87. * Array of master peers
  88. */
  89. static struct BenchmarkPeer *mps;
  90. /**
  91. * Number slave peers
  92. */
  93. static unsigned int num_slaves;
  94. /**
  95. * Array of master peers
  96. */
  97. static struct BenchmarkPeer *sps;
  98. static struct LoggingHandle *l;
  99. static void
  100. evaluate ()
  101. {
  102. int c_m;
  103. int c_s;
  104. unsigned int duration;
  105. struct BenchmarkPeer *mp;
  106. struct BenchmarkPartner *p;
  107. unsigned int kb_sent_sec;
  108. double kb_sent_percent;
  109. unsigned int kb_recv_sec;
  110. double kb_recv_percent;
  111. unsigned int rtt;
  112. duration = 1 + (perf_duration.rel_value_us / (1000 * 1000));
  113. for (c_m = 0; c_m < num_masters; c_m++)
  114. {
  115. mp = &mps[c_m];
  116. fprintf (stderr,
  117. "Master [%u]: sent: %u KiB in %u sec. = %u KiB/s, received: %u KiB in %u sec. = %u KiB/s\n",
  118. mp->no, mp->total_bytes_sent / 1024, duration,
  119. (mp->total_bytes_sent / 1024) / duration,
  120. mp->total_bytes_received / 1024, duration,
  121. (mp->total_bytes_received / 1024) / duration);
  122. for (c_s = 0; c_s < num_slaves; c_s++)
  123. {
  124. p = &mp->partners[c_s];
  125. kb_sent_sec = 0;
  126. kb_recv_sec = 0;
  127. kb_sent_percent = 0.0;
  128. kb_recv_percent = 0.0;
  129. rtt = 0;
  130. if (duration > 0)
  131. {
  132. kb_sent_sec = (p->bytes_sent / 1024) / duration;
  133. kb_recv_sec = (p->bytes_received / 1024) / duration;
  134. }
  135. if (mp->total_bytes_sent > 0)
  136. kb_sent_percent = ((double) p->bytes_sent * 100) / mp->total_bytes_sent;
  137. if (mp->total_bytes_received > 0)
  138. kb_recv_percent = ((double) p->bytes_received * 100) / mp->total_bytes_received;
  139. if (1000 * p->messages_sent > 0)
  140. rtt = p->total_app_rtt / (1000 * p->messages_sent);
  141. fprintf (stderr,
  142. "%c Master [%u] -> Slave [%u]: sent %u KiB/s (%.2f %%), received %u KiB/s (%.2f %%)\n",
  143. (mp->pref_partner == p->dest) ? '*' : ' ',
  144. mp->no, p->dest->no,
  145. kb_sent_sec, kb_sent_percent,
  146. kb_recv_sec, kb_recv_percent);
  147. fprintf (stderr,
  148. "%c Master [%u] -> Slave [%u]: Average application layer RTT: %u ms\n",
  149. (mp->pref_partner == p->dest) ? '*' : ' ',
  150. mp->no, p->dest->no, rtt);
  151. }
  152. }
  153. }
  154. /**
  155. * Shutdown nicely
  156. *
  157. * @param cls NULL
  158. */
  159. static void
  160. do_shutdown (void *cls)
  161. {
  162. if (GNUNET_YES == logging)
  163. GNUNET_ATS_TEST_logging_clean_up(l);
  164. if (NULL != timeout_task)
  165. {
  166. GNUNET_SCHEDULER_cancel (timeout_task);
  167. timeout_task = NULL;
  168. }
  169. if (NULL != progress_task)
  170. {
  171. fprintf (stderr, "0\n");
  172. GNUNET_SCHEDULER_cancel (progress_task);
  173. progress_task = NULL;
  174. }
  175. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  176. "Benchmarking done\n");
  177. GNUNET_ATS_TEST_shutdown_topology ();
  178. }
  179. /**
  180. * Shutdown nicely
  181. *
  182. * @param cls NULL
  183. */
  184. static void
  185. do_timeout (void *cls)
  186. {
  187. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  188. "Terminating with timeout\n");
  189. timeout_task = NULL;
  190. evaluate ();
  191. GNUNET_SCHEDULER_shutdown ();
  192. }
  193. static void
  194. print_progress (void *cls)
  195. {
  196. static int calls;
  197. progress_task = NULL;
  198. fprintf (stderr,
  199. "%llu..",
  200. (long long unsigned) perf_duration.rel_value_us / (1000 * 1000) - calls);
  201. calls++;
  202. progress_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
  203. &print_progress,
  204. NULL);
  205. }
  206. static void
  207. ats_pref_task (void *cls)
  208. {
  209. struct BenchmarkPeer *me = cls;
  210. me->ats_task = NULL;
  211. GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, " Master [%u] set preference for slave [%u] to %f\n",
  212. me->no, me->pref_partner->no, me->pref_value);
  213. GNUNET_ATS_performance_change_preference (me->ats_perf_handle,
  214. &me->pref_partner->id,
  215. pref_val, me->pref_value, GNUNET_ATS_PREFERENCE_END);
  216. me->pref_value += TEST_ATS_PREFRENCE_DELTA;
  217. me->ats_task = GNUNET_SCHEDULER_add_delayed (TEST_ATS_PREFRENCE_FREQUENCY,
  218. &ats_pref_task, cls);
  219. }
  220. static void
  221. start_benchmark (void *cls)
  222. {
  223. int c_m;
  224. int c_s;
  225. progress_task = GNUNET_SCHEDULER_add_now (&print_progress,
  226. NULL);
  227. GNUNET_log(GNUNET_ERROR_TYPE_INFO,
  228. "Topology connected, start benchmarking...\n");
  229. /* Start sending test messages */
  230. for (c_m = 0; c_m < num_masters; c_m++)
  231. {
  232. for (c_s = 0; c_s < num_slaves; c_s++)
  233. {
  234. GNUNET_ATS_TEST_generate_traffic_start (&mps[c_m],
  235. &mps[c_m].partners[c_s],
  236. GNUNET_ATS_TEST_TG_LINEAR,
  237. UINT32_MAX,
  238. UINT32_MAX,
  239. GNUNET_TIME_UNIT_MINUTES,
  240. GNUNET_TIME_UNIT_FOREVER_REL);
  241. }
  242. if (pref_val != GNUNET_ATS_PREFERENCE_END)
  243. mps[c_m].ats_task = GNUNET_SCHEDULER_add_now (&ats_pref_task,
  244. &mps[c_m]);
  245. }
  246. if (GNUNET_YES == logging)
  247. l = GNUNET_ATS_TEST_logging_start (log_frequency,
  248. testname, mps,
  249. num_masters, num_slaves,
  250. GNUNET_NO);
  251. }
  252. static void
  253. do_benchmark (void *cls,
  254. struct BenchmarkPeer *masters,
  255. struct BenchmarkPeer *slaves)
  256. {
  257. mps = masters;
  258. sps = slaves;
  259. GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
  260. NULL);
  261. timeout_task = GNUNET_SCHEDULER_add_delayed (perf_duration,
  262. &do_timeout,
  263. NULL);
  264. progress_task = GNUNET_SCHEDULER_add_now (&start_benchmark,
  265. NULL);
  266. }
  267. static struct BenchmarkPartner *
  268. find_partner (struct BenchmarkPeer *me,
  269. const struct GNUNET_PeerIdentity *peer)
  270. {
  271. int c_m;
  272. GNUNET_assert (NULL != me);
  273. GNUNET_assert (NULL != peer);
  274. for (c_m = 0; c_m < me->num_partners; c_m++)
  275. {
  276. /* Find a partner with other as destination */
  277. if (0 == GNUNET_memcmp (peer, &me->partners[c_m].dest->id))
  278. {
  279. return &me->partners[c_m];
  280. }
  281. }
  282. return NULL;
  283. }
  284. static void
  285. log_request_cb (void *cls,
  286. const struct GNUNET_HELLO_Address *address,
  287. int address_active,
  288. struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
  289. struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
  290. const struct GNUNET_ATS_Properties *ats)
  291. {
  292. struct BenchmarkPeer *me = cls;
  293. struct BenchmarkPartner *p;
  294. char *peer_id;
  295. p = find_partner (me, &address->peer);
  296. if (NULL == p)
  297. {
  298. /* This is not one of my partners
  299. * Will happen since the peers will connect to each other due to gossiping
  300. */
  301. return;
  302. }
  303. peer_id = GNUNET_strdup (GNUNET_i2s (&me->id));
  304. if ((p->bandwidth_in != ntohl (bandwidth_in.value__)) ||
  305. (p->bandwidth_out != ntohl (bandwidth_out.value__)))
  306. p->bandwidth_in = ntohl (bandwidth_in.value__);
  307. p->bandwidth_out = ntohl (bandwidth_out.value__);
  308. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
  309. "%s [%u] received ATS information for peers `%s'\n",
  310. (GNUNET_YES == p->me->master) ? "Master" : "Slave",
  311. p->me->no,
  312. GNUNET_i2s (&p->dest->id));
  313. GNUNET_free (peer_id);
  314. if (NULL != l)
  315. GNUNET_ATS_TEST_logging_now (l);
  316. }
  317. /*
  318. * Start the performance test case
  319. */
  320. int
  321. main (int argc, char *argv[])
  322. {
  323. char *tmp;
  324. char *tmp_sep;
  325. char *test_name;
  326. char *conf_name;
  327. char *comm_name;
  328. char *dotexe;
  329. char *prefs[] = GNUNET_ATS_PreferenceTypeString;
  330. int c;
  331. result = 0;
  332. /* Determine testname
  333. * perf_ats_<solver>_<transport>_<preference>[.exe]*/
  334. /* Find test prefix, store in temp */
  335. tmp = strstr (argv[0], TESTNAME_PREFIX);
  336. if (NULL == tmp)
  337. {
  338. fprintf (stderr, "Unable to parse test name `%s'\n", argv[0]);
  339. return GNUNET_SYSERR;
  340. }
  341. /* Set tmp to end of test name prefix */
  342. tmp += strlen (TESTNAME_PREFIX);
  343. /* Determine solver name */
  344. solver = GNUNET_strdup (tmp);
  345. /* Remove .exe prefix */
  346. if (NULL != (dotexe = strstr (solver, ".exe")) && dotexe[4] == '\0')
  347. dotexe[0] = '\0';
  348. /* Determine first '_' after solver */
  349. tmp_sep = strchr (solver, '_');
  350. if (NULL == tmp_sep)
  351. {
  352. fprintf (stderr, "Unable to parse test name `%s'\n", argv[0]);
  353. GNUNET_free(solver);
  354. return GNUNET_SYSERR;
  355. }
  356. tmp_sep[0] = '\0';
  357. comm_name = GNUNET_strdup (&tmp_sep[1]);
  358. tmp_sep = strchr (comm_name, '_');
  359. if (NULL == tmp_sep)
  360. {
  361. fprintf (stderr, "Unable to parse test name `%s'\n", argv[0]);
  362. GNUNET_free(solver);
  363. return GNUNET_SYSERR;
  364. }
  365. tmp_sep[0] = '\0';
  366. for (c = 0; c <= strlen (comm_name); c++)
  367. comm_name[c] = toupper (comm_name[c]);
  368. if (0 == strcmp (comm_name, "CORE"))
  369. test_core = GNUNET_YES;
  370. else if (0 == strcmp (comm_name, "TRANSPORT"))
  371. test_core = GNUNET_NO;
  372. else
  373. {
  374. GNUNET_free (comm_name);
  375. GNUNET_free (solver);
  376. return GNUNET_SYSERR;
  377. }
  378. pref_str = GNUNET_strdup(tmp_sep + 1);
  379. GNUNET_asprintf (&conf_name, "%s%s_%s.conf", TESTNAME_PREFIX, solver,
  380. pref_str);
  381. GNUNET_asprintf (&test_name, "%s%s_%s", TESTNAME_PREFIX, solver, pref_str);
  382. for (c = 0; c <= strlen (pref_str); c++)
  383. pref_str[c] = toupper (pref_str[c]);
  384. pref_val = -1;
  385. if (0 != strcmp (pref_str, "NONE"))
  386. {
  387. for (c = 0; c < GNUNET_ATS_PREFERENCE_END; c++)
  388. {
  389. if (0 == strcmp (pref_str, prefs[c]))
  390. {
  391. pref_val = c;
  392. break;
  393. }
  394. }
  395. }
  396. else
  397. {
  398. /* abuse terminator to indicate no pref */
  399. pref_val = GNUNET_ATS_PREFERENCE_END;
  400. }
  401. if (-1 == pref_val)
  402. {
  403. fprintf (stderr, "Unknown preference: `%s'\n", pref_str);
  404. GNUNET_free(solver);
  405. GNUNET_free(pref_str);
  406. GNUNET_free (comm_name);
  407. return -1;
  408. }
  409. for (c = 0; c < (argc - 1); c++)
  410. {
  411. if (0 == strcmp (argv[c], "-d"))
  412. break;
  413. }
  414. if (c < argc - 1)
  415. {
  416. if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_relative (argv[c + 1], &perf_duration))
  417. fprintf (stderr, "Failed to parse duration `%s'\n", argv[c + 1]);
  418. }
  419. else
  420. {
  421. perf_duration = BENCHMARK_DURATION;
  422. }
  423. fprintf (stderr, "Running benchmark for %llu secs\n", (unsigned long long) (perf_duration.rel_value_us) / (1000 * 1000));
  424. for (c = 0; c < (argc - 1); c++)
  425. {
  426. if (0 == strcmp (argv[c], "-s"))
  427. break;
  428. }
  429. if (c < argc - 1)
  430. {
  431. if ((0L != (num_slaves = strtol (argv[c + 1], NULL, 10)))
  432. && (num_slaves >= 1))
  433. fprintf (stderr, "Starting %u slave peers\n", num_slaves);
  434. else
  435. num_slaves = DEFAULT_SLAVES_NUM;
  436. }
  437. else
  438. num_slaves = DEFAULT_SLAVES_NUM;
  439. for (c = 0; c < (argc - 1); c++)
  440. {
  441. if (0 == strcmp (argv[c], "-m"))
  442. break;
  443. }
  444. if (c < argc - 1)
  445. {
  446. if ((0L != (num_masters = strtol (argv[c + 1], NULL, 10)))
  447. && (num_masters >= 2))
  448. fprintf (stderr, "Starting %u master peers\n", num_masters);
  449. else
  450. num_masters = DEFAULT_MASTERS_NUM;
  451. }
  452. else
  453. num_masters = DEFAULT_MASTERS_NUM;
  454. logging = GNUNET_NO;
  455. for (c = 0; c < argc; c++)
  456. {
  457. if (0 == strcmp (argv[c], "-l"))
  458. logging = GNUNET_YES;
  459. }
  460. if (GNUNET_YES == logging)
  461. {
  462. for (c = 0; c < (argc - 1); c++)
  463. {
  464. if (0 == strcmp (argv[c], "-f"))
  465. break;
  466. }
  467. if (c < argc - 1)
  468. {
  469. if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_relative (argv[c + 1], &log_frequency))
  470. fprintf (stderr, "Failed to parse duration `%s'\n", argv[c + 1]);
  471. }
  472. else
  473. {
  474. log_frequency = LOGGING_FREQUENCY;
  475. }
  476. fprintf (stderr, "Using log frequency %llu ms\n",
  477. (unsigned long long) (log_frequency.rel_value_us) / (1000));
  478. }
  479. GNUNET_asprintf (&testname, "%s_%s_%s",solver, comm_name, pref_str);
  480. if (num_slaves < num_masters)
  481. {
  482. fprintf (stderr,
  483. "Number of master peers is lower than slaves! exit...\n");
  484. GNUNET_free(test_name);
  485. GNUNET_free(solver);
  486. GNUNET_free(pref_str);
  487. GNUNET_free (comm_name);
  488. return GNUNET_SYSERR;
  489. }
  490. /**
  491. * Setup the topology
  492. */
  493. GNUNET_ATS_TEST_create_topology ("perf-ats",
  494. conf_name,
  495. num_slaves,
  496. num_masters,
  497. test_core,
  498. &do_benchmark,
  499. NULL,
  500. &log_request_cb);
  501. return result;
  502. }
  503. /* end of file perf_ats.c */