speed.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /* Simple speed test for TDB */
  2. #include <err.h>
  3. #include <time.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <unistd.h>
  7. #include <sys/time.h>
  8. #include <fcntl.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <stdbool.h>
  13. #include <ccan/tdb2/tdb2.h>
  14. /* Nanoseconds per operation */
  15. static size_t normalize(const struct timeval *start,
  16. const struct timeval *stop,
  17. unsigned int num)
  18. {
  19. struct timeval diff;
  20. timersub(stop, start, &diff);
  21. /* Floating point is more accurate here. */
  22. return (double)(diff.tv_sec * 1000000 + diff.tv_usec)
  23. / num * 1000;
  24. }
  25. static size_t file_size(void)
  26. {
  27. struct stat st;
  28. if (stat("/tmp/speed.tdb", &st) != 0)
  29. return -1;
  30. return st.st_size;
  31. }
  32. static int count_record(struct tdb_context *tdb,
  33. TDB_DATA key, TDB_DATA data, void *p)
  34. {
  35. int *total = p;
  36. *total += *(int *)data.dptr;
  37. return 0;
  38. }
  39. static void dump_and_clear_stats(struct tdb_attribute_stats *stats)
  40. {
  41. printf("allocs = %llu\n",
  42. (unsigned long long)stats->allocs);
  43. printf(" alloc_subhash = %llu\n",
  44. (unsigned long long)stats->alloc_subhash);
  45. printf(" alloc_chain = %llu\n",
  46. (unsigned long long)stats->alloc_chain);
  47. printf(" alloc_bucket_exact = %llu\n",
  48. (unsigned long long)stats->alloc_bucket_exact);
  49. printf(" alloc_bucket_max = %llu\n",
  50. (unsigned long long)stats->alloc_bucket_max);
  51. printf(" alloc_leftover = %llu\n",
  52. (unsigned long long)stats->alloc_leftover);
  53. printf(" alloc_coalesce_tried = %llu\n",
  54. (unsigned long long)stats->alloc_coalesce_tried);
  55. printf(" alloc_coalesce_lockfail = %llu\n",
  56. (unsigned long long)stats->alloc_coalesce_lockfail);
  57. printf(" alloc_coalesce_race = %llu\n",
  58. (unsigned long long)stats->alloc_coalesce_race);
  59. printf(" alloc_coalesce_succeeded = %llu\n",
  60. (unsigned long long)stats->alloc_coalesce_succeeded);
  61. printf(" alloc_coalesce_num_merged = %llu\n",
  62. (unsigned long long)stats->alloc_coalesce_num_merged);
  63. printf("compares = %llu\n",
  64. (unsigned long long)stats->compares);
  65. printf(" compare_wrong_bucket = %llu\n",
  66. (unsigned long long)stats->compare_wrong_bucket);
  67. printf(" compare_wrong_offsetbits = %llu\n",
  68. (unsigned long long)stats->compare_wrong_offsetbits);
  69. printf(" compare_wrong_keylen = %llu\n",
  70. (unsigned long long)stats->compare_wrong_keylen);
  71. printf(" compare_wrong_rechash = %llu\n",
  72. (unsigned long long)stats->compare_wrong_rechash);
  73. printf(" compare_wrong_keycmp = %llu\n",
  74. (unsigned long long)stats->compare_wrong_keycmp);
  75. printf("expands = %llu\n",
  76. (unsigned long long)stats->expands);
  77. printf("frees = %llu\n",
  78. (unsigned long long)stats->frees);
  79. printf("locks = %llu\n",
  80. (unsigned long long)stats->locks);
  81. printf(" lock_lowlevel = %llu\n",
  82. (unsigned long long)stats->lock_lowlevel);
  83. printf(" lock_nonblock = %llu\n",
  84. (unsigned long long)stats->lock_nonblock);
  85. /* Now clear. */
  86. memset(&stats->allocs, 0, (char *)(stats+1) - (char *)&stats->allocs);
  87. }
  88. static void tdb_log(struct tdb_context *tdb, enum tdb_log_level level,
  89. void *private, const char *message)
  90. {
  91. fputs(message, stderr);
  92. }
  93. int main(int argc, char *argv[])
  94. {
  95. unsigned int i, j, num = 1000, stage = 0, stopat = -1;
  96. int flags = TDB_DEFAULT;
  97. bool transaction = false, summary = false;
  98. TDB_DATA key, data;
  99. struct tdb_context *tdb;
  100. struct timeval start, stop;
  101. union tdb_attribute seed, stats, log;
  102. enum TDB_ERROR ecode;
  103. /* Try to keep benchmarks even. */
  104. seed.base.attr = TDB_ATTRIBUTE_SEED;
  105. seed.base.next = NULL;
  106. seed.seed.seed = 0;
  107. log.base.attr = TDB_ATTRIBUTE_LOG;
  108. log.base.next = &seed;
  109. log.log.log_fn = tdb_log;
  110. memset(&stats, 0, sizeof(stats));
  111. stats.base.attr = TDB_ATTRIBUTE_STATS;
  112. stats.base.next = NULL;
  113. stats.stats.size = sizeof(stats);
  114. if (argv[1] && strcmp(argv[1], "--internal") == 0) {
  115. flags = TDB_INTERNAL;
  116. argc--;
  117. argv++;
  118. }
  119. if (argv[1] && strcmp(argv[1], "--transaction") == 0) {
  120. transaction = true;
  121. argc--;
  122. argv++;
  123. }
  124. if (argv[1] && strcmp(argv[1], "--no-sync") == 0) {
  125. flags |= TDB_NOSYNC;
  126. argc--;
  127. argv++;
  128. }
  129. if (argv[1] && strcmp(argv[1], "--summary") == 0) {
  130. summary = true;
  131. argc--;
  132. argv++;
  133. }
  134. if (argv[1] && strcmp(argv[1], "--stats") == 0) {
  135. seed.base.next = &stats;
  136. argc--;
  137. argv++;
  138. }
  139. tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR|O_CREAT|O_TRUNC,
  140. 0600, &log);
  141. if (!tdb)
  142. err(1, "Opening /tmp/speed.tdb");
  143. key.dptr = (void *)&i;
  144. key.dsize = sizeof(i);
  145. data = key;
  146. if (argv[1]) {
  147. num = atoi(argv[1]);
  148. argv++;
  149. argc--;
  150. }
  151. if (argv[1]) {
  152. stopat = atoi(argv[1]);
  153. argv++;
  154. argc--;
  155. }
  156. if (transaction && (ecode = tdb_transaction_start(tdb)))
  157. errx(1, "starting transaction: %s", tdb_errorstr(ecode));
  158. /* Add 1000 records. */
  159. printf("Adding %u records: ", num); fflush(stdout);
  160. gettimeofday(&start, NULL);
  161. for (i = 0; i < num; i++)
  162. if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
  163. errx(1, "Inserting key %u in tdb: %s",
  164. i, tdb_errorstr(ecode));
  165. gettimeofday(&stop, NULL);
  166. if (transaction && (ecode = tdb_transaction_commit(tdb)))
  167. errx(1, "committing transaction: %s", tdb_errorstr(ecode));
  168. printf(" %zu ns (%zu bytes)\n",
  169. normalize(&start, &stop, num), file_size());
  170. if (summary) {
  171. char *sumstr = NULL;
  172. tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
  173. printf("%s\n", sumstr);
  174. free(sumstr);
  175. }
  176. if (seed.base.next)
  177. dump_and_clear_stats(&stats.stats);
  178. if (++stage == stopat)
  179. exit(0);
  180. if (transaction && (ecode = tdb_transaction_start(tdb)))
  181. errx(1, "starting transaction: %s", tdb_errorstr(ecode));
  182. /* Finding 1000 records. */
  183. printf("Finding %u records: ", num); fflush(stdout);
  184. gettimeofday(&start, NULL);
  185. for (i = 0; i < num; i++) {
  186. struct tdb_data dbuf;
  187. if ((ecode = tdb_fetch(tdb, key, &dbuf)) != TDB_SUCCESS
  188. || *(int *)dbuf.dptr != i) {
  189. errx(1, "Fetching key %u in tdb gave %u",
  190. i, ecode ? ecode : *(int *)dbuf.dptr);
  191. }
  192. }
  193. gettimeofday(&stop, NULL);
  194. if (transaction && (ecode = tdb_transaction_commit(tdb)))
  195. errx(1, "committing transaction: %s", tdb_errorstr(ecode));
  196. printf(" %zu ns (%zu bytes)\n",
  197. normalize(&start, &stop, num), file_size());
  198. if (summary) {
  199. char *sumstr = NULL;
  200. tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
  201. printf("%s\n", sumstr);
  202. free(sumstr);
  203. }
  204. if (seed.base.next)
  205. dump_and_clear_stats(&stats.stats);
  206. if (++stage == stopat)
  207. exit(0);
  208. if (transaction && (ecode = tdb_transaction_start(tdb)))
  209. errx(1, "starting transaction: %s", tdb_errorstr(ecode));
  210. /* Missing 1000 records. */
  211. printf("Missing %u records: ", num); fflush(stdout);
  212. gettimeofday(&start, NULL);
  213. for (i = num; i < num*2; i++) {
  214. struct tdb_data dbuf;
  215. ecode = tdb_fetch(tdb, key, &dbuf);
  216. if (ecode != TDB_ERR_NOEXIST)
  217. errx(1, "Fetching key %u in tdb gave %s",
  218. i, tdb_errorstr(ecode));
  219. }
  220. gettimeofday(&stop, NULL);
  221. if (transaction && (ecode = tdb_transaction_commit(tdb)))
  222. errx(1, "committing transaction: %s", tdb_errorstr(ecode));
  223. printf(" %zu ns (%zu bytes)\n",
  224. normalize(&start, &stop, num), file_size());
  225. if (summary) {
  226. char *sumstr = NULL;
  227. tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
  228. printf("%s\n", sumstr);
  229. free(sumstr);
  230. }
  231. if (seed.base.next)
  232. dump_and_clear_stats(&stats.stats);
  233. if (++stage == stopat)
  234. exit(0);
  235. if (transaction && (ecode = tdb_transaction_start(tdb)))
  236. errx(1, "starting transaction: %s", tdb_errorstr(ecode));
  237. /* Traverse 1000 records. */
  238. printf("Traversing %u records: ", num); fflush(stdout);
  239. i = 0;
  240. gettimeofday(&start, NULL);
  241. if (tdb_traverse(tdb, count_record, &i) != num)
  242. errx(1, "Traverse returned wrong number of records");
  243. if (i != (num - 1) * (num / 2))
  244. errx(1, "Traverse tallied to %u", i);
  245. gettimeofday(&stop, NULL);
  246. if (transaction && (ecode = tdb_transaction_commit(tdb)))
  247. errx(1, "committing transaction: %s", tdb_errorstr(ecode));
  248. printf(" %zu ns (%zu bytes)\n",
  249. normalize(&start, &stop, num), file_size());
  250. if (summary) {
  251. char *sumstr = NULL;
  252. tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
  253. printf("%s\n", sumstr);
  254. free(sumstr);
  255. }
  256. if (seed.base.next)
  257. dump_and_clear_stats(&stats.stats);
  258. if (++stage == stopat)
  259. exit(0);
  260. if (transaction && (ecode = tdb_transaction_start(tdb)))
  261. errx(1, "starting transaction: %s", tdb_errorstr(ecode));
  262. /* Delete 1000 records (not in order). */
  263. printf("Deleting %u records: ", num); fflush(stdout);
  264. gettimeofday(&start, NULL);
  265. for (j = 0; j < num; j++) {
  266. i = (j + 100003) % num;
  267. if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
  268. errx(1, "Deleting key %u in tdb: %s",
  269. i, tdb_errorstr(ecode));
  270. }
  271. gettimeofday(&stop, NULL);
  272. if (transaction && (ecode = tdb_transaction_commit(tdb)))
  273. errx(1, "committing transaction: %s", tdb_errorstr(ecode));
  274. printf(" %zu ns (%zu bytes)\n",
  275. normalize(&start, &stop, num), file_size());
  276. if (summary) {
  277. char *sumstr = NULL;
  278. tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
  279. printf("%s\n", sumstr);
  280. free(sumstr);
  281. }
  282. if (seed.base.next)
  283. dump_and_clear_stats(&stats.stats);
  284. if (++stage == stopat)
  285. exit(0);
  286. if (transaction && (ecode = tdb_transaction_start(tdb)))
  287. errx(1, "starting transaction: %s", tdb_errorstr(ecode));
  288. /* Re-add 1000 records (not in order). */
  289. printf("Re-adding %u records: ", num); fflush(stdout);
  290. gettimeofday(&start, NULL);
  291. for (j = 0; j < num; j++) {
  292. i = (j + 100003) % num;
  293. if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
  294. errx(1, "Inserting key %u in tdb: %s",
  295. i, tdb_errorstr(ecode));
  296. }
  297. gettimeofday(&stop, NULL);
  298. if (transaction && (ecode = tdb_transaction_commit(tdb)))
  299. errx(1, "committing transaction: %s", tdb_errorstr(ecode));
  300. printf(" %zu ns (%zu bytes)\n",
  301. normalize(&start, &stop, num), file_size());
  302. if (summary) {
  303. char *sumstr = NULL;
  304. tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
  305. printf("%s\n", sumstr);
  306. free(sumstr);
  307. }
  308. if (seed.base.next)
  309. dump_and_clear_stats(&stats.stats);
  310. if (++stage == stopat)
  311. exit(0);
  312. if (transaction && (ecode = tdb_transaction_start(tdb)))
  313. errx(1, "starting transaction: %s", tdb_errorstr(ecode));
  314. /* Append 1000 records. */
  315. printf("Appending %u records: ", num); fflush(stdout);
  316. gettimeofday(&start, NULL);
  317. for (i = 0; i < num; i++)
  318. if ((ecode = tdb_append(tdb, key, data)) != TDB_SUCCESS)
  319. errx(1, "Appending key %u in tdb: %s",
  320. i, tdb_errorstr(ecode));
  321. gettimeofday(&stop, NULL);
  322. if (transaction && (ecode = tdb_transaction_commit(tdb)))
  323. errx(1, "committing transaction: %s", tdb_errorstr(ecode));
  324. printf(" %zu ns (%zu bytes)\n",
  325. normalize(&start, &stop, num), file_size());
  326. if (summary) {
  327. char *sumstr = NULL;
  328. tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
  329. printf("%s\n", sumstr);
  330. free(sumstr);
  331. }
  332. if (++stage == stopat)
  333. exit(0);
  334. if (transaction && (ecode = tdb_transaction_start(tdb)))
  335. errx(1, "starting transaction: %s", tdb_errorstr(ecode));
  336. /* Churn 1000 records: not in order! */
  337. printf("Churning %u records: ", num); fflush(stdout);
  338. gettimeofday(&start, NULL);
  339. for (j = 0; j < num; j++) {
  340. i = (j + 1000019) % num;
  341. if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
  342. errx(1, "Deleting key %u in tdb: %s",
  343. i, tdb_errorstr(ecode));
  344. i += num;
  345. if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
  346. errx(1, "Inserting key %u in tdb: %s",
  347. i, tdb_errorstr(ecode));
  348. }
  349. gettimeofday(&stop, NULL);
  350. if (transaction && (ecode = tdb_transaction_commit(tdb)))
  351. errx(1, "committing transaction: %s", tdb_errorstr(ecode));
  352. printf(" %zu ns (%zu bytes)\n",
  353. normalize(&start, &stop, num), file_size());
  354. if (summary) {
  355. char *sumstr = NULL;
  356. tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
  357. printf("%s\n", sumstr);
  358. free(sumstr);
  359. }
  360. if (seed.base.next)
  361. dump_and_clear_stats(&stats.stats);
  362. if (++stage == stopat)
  363. exit(0);
  364. return 0;
  365. }