speed.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /* Simple speed tests for hashtables. */
  2. #include <ccan/htable/htable_type.h>
  3. #include <ccan/htable/htable.c>
  4. #include <ccan/hash/hash.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <time.h>
  9. #include <unistd.h>
  10. #include <sys/time.h>
  11. static size_t hashcount;
  12. struct object {
  13. /* The key. */
  14. unsigned int key;
  15. /* Some contents. Doubles as consistency check. */
  16. struct object *self;
  17. };
  18. static const unsigned int *objkey(const struct object *obj)
  19. {
  20. return &obj->key;
  21. }
  22. static size_t hash_obj(const unsigned int *key)
  23. {
  24. hashcount++;
  25. return hashl(key, 1, 0);
  26. }
  27. static bool cmp(const struct object *object, const unsigned int *key)
  28. {
  29. return object->key == *key;
  30. }
  31. HTABLE_DEFINE_TYPE(struct object, objkey, hash_obj, cmp, obj);
  32. static unsigned int popcount(unsigned long val)
  33. {
  34. #if HAVE_BUILTIN_POPCOUNTL
  35. return __builtin_popcountl(val);
  36. #else
  37. if (sizeof(long) == sizeof(u64)) {
  38. u64 v = val;
  39. v = (v & 0x5555555555555555ULL)
  40. + ((v >> 1) & 0x5555555555555555ULL);
  41. v = (v & 0x3333333333333333ULL)
  42. + ((v >> 1) & 0x3333333333333333ULL);
  43. v = (v & 0x0F0F0F0F0F0F0F0FULL)
  44. + ((v >> 1) & 0x0F0F0F0F0F0F0F0FULL);
  45. v = (v & 0x00FF00FF00FF00FFULL)
  46. + ((v >> 1) & 0x00FF00FF00FF00FFULL);
  47. v = (v & 0x0000FFFF0000FFFFULL)
  48. + ((v >> 1) & 0x0000FFFF0000FFFFULL);
  49. v = (v & 0x00000000FFFFFFFFULL)
  50. + ((v >> 1) & 0x00000000FFFFFFFFULL);
  51. return v;
  52. }
  53. val = (val & 0x55555555ULL) + ((val >> 1) & 0x55555555ULL);
  54. val = (val & 0x33333333ULL) + ((val >> 1) & 0x33333333ULL);
  55. val = (val & 0x0F0F0F0FULL) + ((val >> 1) & 0x0F0F0F0FULL);
  56. val = (val & 0x00FF00FFULL) + ((val >> 1) & 0x00FF00FFULL);
  57. val = (val & 0x0000FFFFULL) + ((val >> 1) & 0x0000FFFFULL);
  58. return val;
  59. #endif
  60. }
  61. static size_t perfect(const struct htable *ht)
  62. {
  63. size_t i, placed_perfect = 0;
  64. for (i = 0; i < ((size_t)1 << ht->bits); i++) {
  65. if (!entry_is_valid(ht->table[i]))
  66. continue;
  67. if (hash_bucket(ht, ht->rehash(get_raw_ptr(ht, ht->table[i]),
  68. ht->priv)) == i) {
  69. assert((ht->table[i] & ht->perfect_bit)
  70. == ht->perfect_bit);
  71. placed_perfect++;
  72. }
  73. }
  74. return placed_perfect;
  75. }
  76. static size_t count_deleted(const struct htable *ht)
  77. {
  78. size_t i, delete_markers = 0;
  79. for (i = 0; i < ((size_t)1 << ht->bits); i++) {
  80. if (ht->table[i] == HTABLE_DELETED)
  81. delete_markers++;
  82. }
  83. return delete_markers;
  84. }
  85. /* Nanoseconds per operation */
  86. static size_t normalize(const struct timeval *start,
  87. const struct timeval *stop,
  88. unsigned int num)
  89. {
  90. struct timeval diff;
  91. timersub(stop, start, &diff);
  92. /* Floating point is more accurate here. */
  93. return (double)(diff.tv_sec * 1000000 + diff.tv_usec)
  94. / num * 1000;
  95. }
  96. static size_t worst_run(struct htable *ht, size_t *deleted)
  97. {
  98. size_t longest = 0, len = 0, this_del = 0, i;
  99. *deleted = 0;
  100. /* This doesn't take into account end-wrap, but gives an idea. */
  101. for (i = 0; i < ((size_t)1 << ht->bits); i++) {
  102. if (ht->table[i]) {
  103. len++;
  104. if (ht->table[i] == HTABLE_DELETED)
  105. this_del++;
  106. } else {
  107. if (len > longest) {
  108. longest = len;
  109. *deleted = this_del;
  110. }
  111. len = 0;
  112. this_del = 0;
  113. }
  114. }
  115. return longest;
  116. }
  117. int main(int argc, char *argv[])
  118. {
  119. struct object *objs;
  120. size_t i, j, num, deleted;
  121. struct timeval start, stop;
  122. struct htable_obj *ht;
  123. struct htable *htr;
  124. bool make_dumb = false;
  125. if (argv[1] && strcmp(argv[1], "--dumb") == 0) {
  126. argv++;
  127. make_dumb = true;
  128. }
  129. num = argv[1] ? atoi(argv[1]) : 1000000;
  130. objs = calloc(num, sizeof(objs[0]));
  131. for (i = 0; i < num; i++) {
  132. objs[i].key = i;
  133. objs[i].self = &objs[i];
  134. }
  135. ht = htable_obj_new();
  136. htr = (void *)ht;
  137. printf("Initial insert: ");
  138. fflush(stdout);
  139. gettimeofday(&start, NULL);
  140. for (i = 0; i < num; i++)
  141. htable_obj_add(ht, objs[i].self);
  142. gettimeofday(&stop, NULL);
  143. printf(" %zu ns\n", normalize(&start, &stop, num));
  144. printf("Details: hash size %u, mask bits %u, perfect %.0f%%\n",
  145. 1U << htr->bits, popcount(htr->common_mask),
  146. perfect(htr) * 100.0 / htr->elems);
  147. if (make_dumb) {
  148. /* Screw with mask, to hobble us. */
  149. update_common(htr, (void *)~htr->common_bits);
  150. printf("Details: DUMB MODE: mask bits %u\n",
  151. popcount(htr->common_mask));
  152. }
  153. printf("Initial lookup (match): ");
  154. fflush(stdout);
  155. gettimeofday(&start, NULL);
  156. for (i = 0; i < num; i++)
  157. if (htable_obj_get(ht, &i)->self != objs[i].self)
  158. abort();
  159. gettimeofday(&stop, NULL);
  160. printf(" %zu ns\n", normalize(&start, &stop, num));
  161. printf("Initial lookup (miss): ");
  162. fflush(stdout);
  163. gettimeofday(&start, NULL);
  164. for (i = 0; i < num; i++) {
  165. unsigned int n = i + num;
  166. if (htable_obj_get(ht, &n))
  167. abort();
  168. }
  169. gettimeofday(&stop, NULL);
  170. printf(" %zu ns\n", normalize(&start, &stop, num));
  171. /* Lookups in order are very cache-friendly for judy; try random */
  172. printf("Initial lookup (random): ");
  173. fflush(stdout);
  174. gettimeofday(&start, NULL);
  175. for (i = 0, j = 0; i < num; i++, j = (j + 10007) % num)
  176. if (htable_obj_get(ht, &j)->self != &objs[j])
  177. abort();
  178. gettimeofday(&stop, NULL);
  179. printf(" %zu ns\n", normalize(&start, &stop, num));
  180. hashcount = 0;
  181. printf("Initial delete all: ");
  182. fflush(stdout);
  183. gettimeofday(&start, NULL);
  184. for (i = 0; i < num; i++)
  185. if (!htable_obj_del(ht, objs[i].self))
  186. abort();
  187. gettimeofday(&stop, NULL);
  188. printf(" %zu ns\n", normalize(&start, &stop, num));
  189. printf("Details: rehashes %zu\n", hashcount);
  190. printf("Initial re-inserting: ");
  191. fflush(stdout);
  192. gettimeofday(&start, NULL);
  193. for (i = 0; i < num; i++)
  194. htable_obj_add(ht, objs[i].self);
  195. gettimeofday(&stop, NULL);
  196. printf(" %zu ns\n", normalize(&start, &stop, num));
  197. hashcount = 0;
  198. printf("Deleting first half: ");
  199. fflush(stdout);
  200. gettimeofday(&start, NULL);
  201. for (i = 0; i < num; i+=2)
  202. if (!htable_obj_del(ht, objs[i].self))
  203. abort();
  204. gettimeofday(&stop, NULL);
  205. printf(" %zu ns\n", normalize(&start, &stop, num));
  206. printf("Details: rehashes %zu, delete markers %zu\n",
  207. hashcount, count_deleted(htr));
  208. printf("Adding (a different) half: ");
  209. fflush(stdout);
  210. for (i = 0; i < num; i+=2)
  211. objs[i].key = num+i;
  212. gettimeofday(&start, NULL);
  213. for (i = 0; i < num; i+=2)
  214. htable_obj_add(ht, objs[i].self);
  215. gettimeofday(&stop, NULL);
  216. printf(" %zu ns\n", normalize(&start, &stop, num));
  217. printf("Details: delete markers %zu, perfect %.0f%%\n",
  218. count_deleted(htr), perfect(htr) * 100.0 / htr->elems);
  219. printf("Lookup after half-change (match): ");
  220. fflush(stdout);
  221. gettimeofday(&start, NULL);
  222. for (i = 1; i < num; i+=2)
  223. if (htable_obj_get(ht, &i)->self != objs[i].self)
  224. abort();
  225. for (i = 0; i < num; i+=2) {
  226. unsigned int n = i + num;
  227. if (htable_obj_get(ht, &n)->self != objs[i].self)
  228. abort();
  229. }
  230. gettimeofday(&stop, NULL);
  231. printf(" %zu ns\n", normalize(&start, &stop, num));
  232. printf("Lookup after half-change (miss): ");
  233. fflush(stdout);
  234. gettimeofday(&start, NULL);
  235. for (i = 0; i < num; i++) {
  236. unsigned int n = i + num * 2;
  237. if (htable_obj_get(ht, &n))
  238. abort();
  239. }
  240. gettimeofday(&stop, NULL);
  241. printf(" %zu ns\n", normalize(&start, &stop, num));
  242. /* Hashtables with delete markers can fill with markers over time.
  243. * so do some changes to see how it operates in long-term. */
  244. for (i = 0; i < 5; i++) {
  245. if (i == 0) {
  246. /* We don't measure this: jmap is different. */
  247. printf("Details: initial churn\n");
  248. } else {
  249. printf("Churning %s time: ",
  250. i == 1 ? "second"
  251. : i == 2 ? "third"
  252. : i == 3 ? "fourth"
  253. : "fifth");
  254. fflush(stdout);
  255. }
  256. gettimeofday(&start, NULL);
  257. for (j = 0; j < num; j++) {
  258. if (!htable_obj_del(ht, &objs[j]))
  259. abort();
  260. objs[j].key = num*i+j;
  261. if (!htable_obj_add(ht, &objs[j]))
  262. abort();
  263. }
  264. gettimeofday(&stop, NULL);
  265. if (i != 0)
  266. printf(" %zu ns\n", normalize(&start, &stop, num));
  267. }
  268. /* Spread out the keys more to try to make it harder. */
  269. printf("Details: reinserting with spread\n");
  270. for (i = 0; i < num; i++) {
  271. if (!htable_obj_del(ht, objs[i].self))
  272. abort();
  273. objs[i].key = num * 5 + i * 9;
  274. if (!htable_obj_add(ht, objs[i].self))
  275. abort();
  276. }
  277. printf("Details: delete markers %zu, perfect %.0f%%\n",
  278. count_deleted(htr), perfect(htr) * 100.0 / htr->elems);
  279. i = worst_run(htr, &deleted);
  280. printf("Details: worst run %zu (%zu deleted)\n", i, deleted);
  281. printf("Lookup after churn & spread (match): ");
  282. fflush(stdout);
  283. gettimeofday(&start, NULL);
  284. for (i = 0; i < num; i++) {
  285. unsigned int n = num * 5 + i * 9;
  286. if (htable_obj_get(ht, &n)->self != objs[i].self)
  287. abort();
  288. }
  289. gettimeofday(&stop, NULL);
  290. printf(" %zu ns\n", normalize(&start, &stop, num));
  291. printf("Lookup after churn & spread (miss): ");
  292. fflush(stdout);
  293. gettimeofday(&start, NULL);
  294. for (i = 0; i < num; i++) {
  295. unsigned int n = num * (5 + 9) + i * 9;
  296. if (htable_obj_get(ht, &n))
  297. abort();
  298. }
  299. gettimeofday(&stop, NULL);
  300. printf(" %zu ns\n", normalize(&start, &stop, num));
  301. printf("Lookup after churn & spread (random): ");
  302. fflush(stdout);
  303. gettimeofday(&start, NULL);
  304. for (i = 0, j = 0; i < num; i++, j = (j + 10007) % num) {
  305. unsigned int n = num * 5 + j * 9;
  306. if (htable_obj_get(ht, &n)->self != &objs[j])
  307. abort();
  308. }
  309. gettimeofday(&stop, NULL);
  310. printf(" %zu ns\n", normalize(&start, &stop, num));
  311. hashcount = 0;
  312. printf("Deleting half after churn & spread: ");
  313. fflush(stdout);
  314. gettimeofday(&start, NULL);
  315. for (i = 0; i < num; i+=2)
  316. if (!htable_obj_del(ht, objs[i].self))
  317. abort();
  318. gettimeofday(&stop, NULL);
  319. printf(" %zu ns\n", normalize(&start, &stop, num));
  320. printf("Adding (a different) half after churn & spread: ");
  321. fflush(stdout);
  322. for (i = 0; i < num; i+=2)
  323. objs[i].key = num*6+i*9;
  324. gettimeofday(&start, NULL);
  325. for (i = 0; i < num; i+=2)
  326. htable_obj_add(ht, objs[i].self);
  327. gettimeofday(&stop, NULL);
  328. printf(" %zu ns\n", normalize(&start, &stop, num));
  329. printf("Details: delete markers %zu, perfect %.0f%%\n",
  330. count_deleted(htr), perfect(htr) * 100.0 / htr->elems);
  331. return 0;
  332. }