perf_plugin_datastore.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. /*
  2. This file is part of GNUnet.
  3. (C) 2004, 2005, 2006, 2007, 2009, 2011 Christian Grothoff (and other contributing authors)
  4. GNUnet is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published
  6. by the Free Software Foundation; either version 3, or (at your
  7. 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. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNUnet; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  15. Boston, MA 02111-1307, USA.
  16. */
  17. /*
  18. * @file perf_plugin_datastore.c
  19. * @brief Profile database plugin directly, focusing on iterators.
  20. * @author Christian Grothoff
  21. */
  22. #include "platform.h"
  23. #include "gnunet_util_lib.h"
  24. #include "gnunet_protocols.h"
  25. #include "gnunet_datastore_plugin.h"
  26. #include <gauger.h>
  27. #define VERBOSE GNUNET_NO
  28. /**
  29. * Target datastore size (in bytes). Realistic sizes are
  30. * more like 16 GB (not the default of 16 MB); however,
  31. * those take too long to run them in the usual "make check"
  32. * sequence. Hence the value used for shipping is tiny.
  33. */
  34. #define MAX_SIZE 1024LL * 1024 * 16 * 1
  35. #define ITERATIONS 2
  36. /**
  37. * Number of put operations equivalent to 1/10th of MAX_SIZE
  38. */
  39. #define PUT_10 (MAX_SIZE / 32 / 1024 / ITERATIONS)
  40. static char category[256];
  41. static unsigned int hits[PUT_10 / 8 + 1];
  42. static unsigned long long stored_bytes;
  43. static unsigned long long stored_entries;
  44. static unsigned long long stored_ops;
  45. static const char *plugin_name;
  46. static int ok;
  47. enum RunPhase
  48. {
  49. RP_ERROR = 0,
  50. RP_PUT,
  51. RP_REP_GET,
  52. RP_ZA_GET,
  53. RP_EXP_GET,
  54. RP_DONE
  55. };
  56. struct CpsRunContext
  57. {
  58. unsigned int i;
  59. struct GNUNET_TIME_Absolute start;
  60. struct GNUNET_TIME_Absolute end;
  61. const struct GNUNET_CONFIGURATION_Handle *cfg;
  62. struct GNUNET_DATASTORE_PluginFunctions *api;
  63. enum RunPhase phase;
  64. unsigned int cnt;
  65. unsigned int iter;
  66. uint64_t offset;
  67. };
  68. /**
  69. * Function called by plugins to notify us about a
  70. * change in their disk utilization.
  71. *
  72. * @param cls closure (NULL)
  73. * @param delta change in disk utilization,
  74. * 0 for "reset to empty"
  75. */
  76. static void
  77. disk_utilization_change_cb (void *cls, int delta)
  78. {
  79. }
  80. static void
  81. putValue (struct GNUNET_DATASTORE_PluginFunctions *api, int i, int k)
  82. {
  83. char value[65536];
  84. size_t size;
  85. static GNUNET_HashCode key;
  86. static int ic;
  87. char *msg;
  88. unsigned int prio;
  89. /* most content is 32k */
  90. size = 32 * 1024;
  91. if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16) == 0) /* but some of it is less! */
  92. size = 8 + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 32 * 1024);
  93. size = size - (size & 7); /* always multiple of 8 */
  94. /* generate random key */
  95. key.bits[0] = (unsigned int) GNUNET_TIME_absolute_get ().abs_value;
  96. GNUNET_CRYPTO_hash (&key, sizeof (GNUNET_HashCode), &key);
  97. memset (value, i, size);
  98. if (i > 255)
  99. memset (value, i - 255, size / 2);
  100. value[0] = k;
  101. memcpy (&value[4], &i, sizeof (i));
  102. msg = NULL;
  103. prio = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 100);
  104. if (GNUNET_OK != api->put (api->cls, &key, size, value, 1 + i % 4 /* type */ ,
  105. prio, i % 4 /* anonymity */ ,
  106. 0 /* replication */ ,
  107. GNUNET_TIME_relative_to_absolute
  108. (GNUNET_TIME_relative_multiply
  109. (GNUNET_TIME_UNIT_MILLISECONDS,
  110. 60 * 60 * 60 * 1000 +
  111. GNUNET_CRYPTO_random_u32
  112. (GNUNET_CRYPTO_QUALITY_WEAK, 1000))), &msg))
  113. {
  114. fprintf (stderr, "ERROR: `%s'\n", msg);
  115. GNUNET_free_non_null (msg);
  116. return;
  117. }
  118. ic++;
  119. stored_bytes += size;
  120. stored_ops++;
  121. stored_entries++;
  122. }
  123. static void
  124. test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
  125. static int
  126. iterate_zeros (void *cls, const GNUNET_HashCode * key, uint32_t size,
  127. const void *data, enum GNUNET_BLOCK_Type type, uint32_t priority,
  128. uint32_t anonymity, struct GNUNET_TIME_Absolute expiration,
  129. uint64_t uid)
  130. {
  131. struct CpsRunContext *crc = cls;
  132. int i;
  133. const char *cdata = data;
  134. GNUNET_assert (key != NULL);
  135. GNUNET_assert (size >= 8);
  136. memcpy (&i, &cdata[4], sizeof (i));
  137. hits[i / 8] |= (1 << (i % 8));
  138. #if VERBOSE
  139. fprintf (stderr, "Found result type=%u, priority=%u, size=%u, expire=%llu\n",
  140. type, priority, size, (unsigned long long) expiration.abs_value);
  141. #endif
  142. crc->cnt++;
  143. if (crc->cnt == PUT_10 / 4 - 1)
  144. {
  145. unsigned int bc;
  146. bc = 0;
  147. for (i = 0; i < PUT_10; i++)
  148. if (0 != (hits[i / 8] & (1 << (i % 8))))
  149. bc++;
  150. crc->end = GNUNET_TIME_absolute_get ();
  151. printf ("%s took %llu ms yielding %u/%u items\n",
  152. "Select random zero-anonymity item",
  153. (unsigned long long) (crc->end.abs_value - crc->start.abs_value),
  154. bc, crc->cnt);
  155. if (crc->cnt > 0)
  156. GAUGER (category, "Select random zero-anonymity item",
  157. (crc->end.abs_value - crc->start.abs_value) / crc->cnt,
  158. "ms/item");
  159. memset (hits, 0, sizeof (hits));
  160. crc->phase++;
  161. crc->cnt = 0;
  162. crc->start = GNUNET_TIME_absolute_get ();
  163. }
  164. GNUNET_SCHEDULER_add_now (&test, crc);
  165. return GNUNET_OK;
  166. }
  167. static int
  168. expiration_get (void *cls, const GNUNET_HashCode * key, uint32_t size,
  169. const void *data, enum GNUNET_BLOCK_Type type,
  170. uint32_t priority, uint32_t anonymity,
  171. struct GNUNET_TIME_Absolute expiration, uint64_t uid)
  172. {
  173. struct CpsRunContext *crc = cls;
  174. int i;
  175. const char *cdata = data;
  176. GNUNET_assert (size >= 8);
  177. memcpy (&i, &cdata[4], sizeof (i));
  178. hits[i / 8] |= (1 << (i % 8));
  179. crc->cnt++;
  180. if (PUT_10 <= crc->cnt)
  181. {
  182. unsigned int bc;
  183. bc = 0;
  184. for (i = 0; i < PUT_10; i++)
  185. if (0 != (hits[i / 8] & (1 << (i % 8))))
  186. bc++;
  187. crc->end = GNUNET_TIME_absolute_get ();
  188. printf ("%s took %llu ms yielding %u/%u items\n",
  189. "Selecting and deleting by expiration",
  190. (unsigned long long) (crc->end.abs_value - crc->start.abs_value),
  191. bc, (unsigned int) PUT_10);
  192. if (crc->cnt > 0)
  193. GAUGER (category, "Selecting and deleting by expiration",
  194. (crc->end.abs_value - crc->start.abs_value) / crc->cnt,
  195. "ms/item");
  196. memset (hits, 0, sizeof (hits));
  197. if (++crc->iter == ITERATIONS)
  198. crc->phase++;
  199. else
  200. crc->phase = RP_PUT;
  201. crc->cnt = 0;
  202. crc->start = GNUNET_TIME_absolute_get ();
  203. }
  204. GNUNET_SCHEDULER_add_now (&test, crc);
  205. return GNUNET_NO;
  206. }
  207. static int
  208. replication_get (void *cls, const GNUNET_HashCode * key, uint32_t size,
  209. const void *data, enum GNUNET_BLOCK_Type type,
  210. uint32_t priority, uint32_t anonymity,
  211. struct GNUNET_TIME_Absolute expiration, uint64_t uid)
  212. {
  213. struct CpsRunContext *crc = cls;
  214. int i;
  215. const char *cdata = data;
  216. GNUNET_assert (NULL != key);
  217. GNUNET_assert (size >= 8);
  218. memcpy (&i, &cdata[4], sizeof (i));
  219. hits[i / 8] |= (1 << (i % 8));
  220. crc->cnt++;
  221. if (PUT_10 <= crc->cnt)
  222. {
  223. unsigned int bc;
  224. bc = 0;
  225. for (i = 0; i < PUT_10; i++)
  226. if (0 != (hits[i / 8] & (1 << (i % 8))))
  227. bc++;
  228. crc->end = GNUNET_TIME_absolute_get ();
  229. printf ("%s took %llu ms yielding %u/%u items\n",
  230. "Selecting random item for replication",
  231. (unsigned long long) (crc->end.abs_value - crc->start.abs_value),
  232. bc, (unsigned int) PUT_10);
  233. if (crc->cnt > 0)
  234. GAUGER (category, "Selecting random item for replication",
  235. (crc->end.abs_value - crc->start.abs_value) / crc->cnt,
  236. "ms/item");
  237. memset (hits, 0, sizeof (hits));
  238. crc->phase++;
  239. crc->offset = 0;
  240. crc->cnt = 0;
  241. crc->start = GNUNET_TIME_absolute_get ();
  242. }
  243. GNUNET_SCHEDULER_add_now (&test, crc);
  244. return GNUNET_OK;
  245. }
  246. /**
  247. * Function called when the service shuts
  248. * down. Unloads our datastore plugin.
  249. *
  250. * @param api api to unload
  251. * @param cfg configuration to use
  252. */
  253. static void
  254. unload_plugin (struct GNUNET_DATASTORE_PluginFunctions *api,
  255. const struct GNUNET_CONFIGURATION_Handle *cfg)
  256. {
  257. char *name;
  258. char *libname;
  259. if (GNUNET_OK !=
  260. GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE",
  261. &name))
  262. {
  263. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  264. _("No `%s' specified for `%s' in configuration!\n"), "DATABASE",
  265. "DATASTORE");
  266. return;
  267. }
  268. GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
  269. GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
  270. GNUNET_free (libname);
  271. GNUNET_free (name);
  272. }
  273. /**
  274. * Last task run during shutdown. Disconnects us from
  275. * the transport and core.
  276. */
  277. static void
  278. cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  279. {
  280. struct CpsRunContext *crc = cls;
  281. unload_plugin (crc->api, crc->cfg);
  282. GNUNET_free (crc);
  283. }
  284. static void
  285. test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  286. {
  287. struct CpsRunContext *crc = cls;
  288. int j;
  289. if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
  290. {
  291. GNUNET_break (0);
  292. crc->phase = RP_ERROR;
  293. }
  294. #if VERBOSE
  295. fprintf (stderr, "In phase %d, iteration %u\n", crc->phase, crc->cnt);
  296. #endif
  297. switch (crc->phase)
  298. {
  299. case RP_ERROR:
  300. GNUNET_break (0);
  301. crc->api->drop (crc->api->cls);
  302. ok = 1;
  303. GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
  304. &cleaning_task, crc);
  305. break;
  306. case RP_PUT:
  307. crc->start = GNUNET_TIME_absolute_get ();
  308. for (j = 0; j < PUT_10; j++)
  309. putValue (crc->api, j, crc->i);
  310. crc->end = GNUNET_TIME_absolute_get ();
  311. {
  312. printf ("%s took %llu ms for %llu items\n", "Storing an item",
  313. (unsigned long long) (crc->end.abs_value - crc->start.abs_value),
  314. PUT_10);
  315. if (PUT_10 > 0)
  316. GAUGER (category, "Storing an item",
  317. (crc->end.abs_value - crc->start.abs_value) / PUT_10,
  318. "ms/item");
  319. }
  320. crc->i++;
  321. crc->start = GNUNET_TIME_absolute_get ();
  322. crc->phase++;
  323. GNUNET_SCHEDULER_add_now (&test, crc);
  324. break;
  325. case RP_REP_GET:
  326. crc->api->get_replication (crc->api->cls, &replication_get, crc);
  327. break;
  328. case RP_ZA_GET:
  329. crc->api->get_zero_anonymity (crc->api->cls, crc->offset++, 1,
  330. &iterate_zeros, crc);
  331. break;
  332. case RP_EXP_GET:
  333. crc->api->get_expiration (crc->api->cls, &expiration_get, crc);
  334. break;
  335. case RP_DONE:
  336. crc->api->drop (crc->api->cls);
  337. ok = 0;
  338. GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
  339. &cleaning_task, crc);
  340. break;
  341. }
  342. }
  343. /**
  344. * Load the datastore plugin.
  345. */
  346. static struct GNUNET_DATASTORE_PluginFunctions *
  347. load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
  348. {
  349. static struct GNUNET_DATASTORE_PluginEnvironment env;
  350. struct GNUNET_DATASTORE_PluginFunctions *ret;
  351. char *name;
  352. char *libname;
  353. if (GNUNET_OK !=
  354. GNUNET_CONFIGURATION_get_value_string (cfg, "DATASTORE", "DATABASE",
  355. &name))
  356. {
  357. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  358. _("No `%s' specified for `%s' in configuration!\n"), "DATABASE",
  359. "DATASTORE");
  360. return NULL;
  361. }
  362. env.cfg = cfg;
  363. env.duc = &disk_utilization_change_cb;
  364. env.cls = NULL;
  365. GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading `%s' datastore plugin\n"),
  366. name);
  367. GNUNET_asprintf (&libname, "libgnunet_plugin_datastore_%s", name);
  368. if (NULL == (ret = GNUNET_PLUGIN_load (libname, &env)))
  369. {
  370. fprintf (stderr, "Failed to load plugin `%s'!\n", name);
  371. return NULL;
  372. }
  373. GNUNET_free (libname);
  374. GNUNET_free (name);
  375. return ret;
  376. }
  377. static void
  378. run (void *cls, char *const *args, const char *cfgfile,
  379. const struct GNUNET_CONFIGURATION_Handle *c)
  380. {
  381. struct GNUNET_DATASTORE_PluginFunctions *api;
  382. struct CpsRunContext *crc;
  383. api = load_plugin (c);
  384. if (api == NULL)
  385. {
  386. fprintf (stderr,
  387. "Could not initialize plugin, assuming database not configured. Test not run!\n");
  388. return;
  389. }
  390. crc = GNUNET_malloc (sizeof (struct CpsRunContext));
  391. crc->api = api;
  392. crc->cfg = c;
  393. crc->phase = RP_PUT;
  394. ok = 2;
  395. GNUNET_SCHEDULER_add_now (&test, crc);
  396. }
  397. static int
  398. check ()
  399. {
  400. char cfg_name[128];
  401. char *const argv[] = {
  402. "perf-plugin-datastore",
  403. "-c",
  404. cfg_name,
  405. #if VERBOSE
  406. "-L", "DEBUG",
  407. #endif
  408. NULL
  409. };
  410. struct GNUNET_GETOPT_CommandLineOption options[] = {
  411. GNUNET_GETOPT_OPTION_END
  412. };
  413. GNUNET_snprintf (category, sizeof (category), "DATASTORE-%s", plugin_name);
  414. GNUNET_snprintf (cfg_name, sizeof (cfg_name),
  415. "perf_plugin_datastore_data_%s.conf", plugin_name);
  416. GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
  417. "perf-plugin-datastore", "nohelp", options, &run, NULL);
  418. if (ok != 0)
  419. fprintf (stderr, "Missed some testcases: %u\n", ok);
  420. return ok;
  421. }
  422. int
  423. main (int argc, char *argv[])
  424. {
  425. int ret;
  426. char *pos;
  427. char dir_name[128];
  428. sleep (1);
  429. /* determine name of plugin to use */
  430. plugin_name = argv[0];
  431. while (NULL != (pos = strstr (plugin_name, "_")))
  432. plugin_name = pos + 1;
  433. if (NULL != (pos = strstr (plugin_name, ".")))
  434. pos[0] = 0;
  435. else
  436. pos = (char *) plugin_name;
  437. GNUNET_snprintf (dir_name, sizeof (dir_name), "/tmp/perf-gnunet-datastore-%s",
  438. plugin_name);
  439. GNUNET_DISK_directory_remove (dir_name);
  440. GNUNET_log_setup ("perf-plugin-datastore",
  441. #if VERBOSE
  442. "DEBUG",
  443. #else
  444. "WARNING",
  445. #endif
  446. NULL);
  447. ret = check ();
  448. if (pos != plugin_name)
  449. pos[0] = '.';
  450. GNUNET_DISK_directory_remove (dir_name);
  451. return ret;
  452. }
  453. /* end of perf_plugin_datastore.c */