1 /* Simple speed test for TDB */
13 #include <ccan/tdb2/tdb2.h>
15 /* Nanoseconds per operation */
16 static size_t normalize(const struct timeval *start,
17 const struct timeval *stop,
22 timersub(stop, start, &diff);
24 /* Floating point is more accurate here. */
25 return (double)(diff.tv_sec * 1000000 + diff.tv_usec)
29 static size_t file_size(void)
33 if (stat("/tmp/speed.tdb", &st) != 0)
38 static int count_record(struct tdb_context *tdb,
39 TDB_DATA key, TDB_DATA data, void *p)
42 *total += *(int *)data.dptr;
46 static void dump_and_clear_stats(struct tdb_context **tdb,
48 union tdb_attribute *attr)
50 union tdb_attribute stats;
53 stats.base.attr = TDB_ATTRIBUTE_STATS;
54 stats.stats.size = sizeof(stats.stats);
55 ecode = tdb_get_attribute(*tdb, &stats);
56 if (ecode != TDB_SUCCESS)
57 errx(1, "Getting stats: %s", tdb_errorstr(ecode));
59 printf("allocs = %llu\n",
60 (unsigned long long)stats.stats.allocs);
61 printf(" alloc_subhash = %llu\n",
62 (unsigned long long)stats.stats.alloc_subhash);
63 printf(" alloc_chain = %llu\n",
64 (unsigned long long)stats.stats.alloc_chain);
65 printf(" alloc_bucket_exact = %llu\n",
66 (unsigned long long)stats.stats.alloc_bucket_exact);
67 printf(" alloc_bucket_max = %llu\n",
68 (unsigned long long)stats.stats.alloc_bucket_max);
69 printf(" alloc_leftover = %llu\n",
70 (unsigned long long)stats.stats.alloc_leftover);
71 printf(" alloc_coalesce_tried = %llu\n",
72 (unsigned long long)stats.stats.alloc_coalesce_tried);
73 printf(" alloc_coalesce_lockfail = %llu\n",
74 (unsigned long long)stats.stats.alloc_coalesce_lockfail);
75 printf(" alloc_coalesce_race = %llu\n",
76 (unsigned long long)stats.stats.alloc_coalesce_race);
77 printf(" alloc_coalesce_succeeded = %llu\n",
78 (unsigned long long)stats.stats.alloc_coalesce_succeeded);
79 printf(" alloc_coalesce_num_merged = %llu\n",
80 (unsigned long long)stats.stats.alloc_coalesce_num_merged);
81 printf("compares = %llu\n",
82 (unsigned long long)stats.stats.compares);
83 printf(" compare_wrong_bucket = %llu\n",
84 (unsigned long long)stats.stats.compare_wrong_bucket);
85 printf(" compare_wrong_offsetbits = %llu\n",
86 (unsigned long long)stats.stats.compare_wrong_offsetbits);
87 printf(" compare_wrong_keylen = %llu\n",
88 (unsigned long long)stats.stats.compare_wrong_keylen);
89 printf(" compare_wrong_rechash = %llu\n",
90 (unsigned long long)stats.stats.compare_wrong_rechash);
91 printf(" compare_wrong_keycmp = %llu\n",
92 (unsigned long long)stats.stats.compare_wrong_keycmp);
93 printf("expands = %llu\n",
94 (unsigned long long)stats.stats.expands);
95 printf("frees = %llu\n",
96 (unsigned long long)stats.stats.frees);
97 printf("locks = %llu\n",
98 (unsigned long long)stats.stats.locks);
99 printf(" lock_lowlevel = %llu\n",
100 (unsigned long long)stats.stats.lock_lowlevel);
101 printf(" lock_nonblock = %llu\n",
102 (unsigned long long)stats.stats.lock_nonblock);
106 *tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR, 0, attr);
109 static void tdb_log(struct tdb_context *tdb, enum tdb_log_level level,
110 const char *message, void *data)
112 fputs(message, stderr);
116 int main(int argc, char *argv[])
118 unsigned int i, j, num = 1000, stage = 0, stopat = -1;
119 int flags = TDB_DEFAULT;
120 bool transaction = false, summary = false;
122 struct tdb_context *tdb;
123 struct timeval start, stop;
124 union tdb_attribute seed, log;
125 bool do_stats = false;
126 enum TDB_ERROR ecode;
128 /* Try to keep benchmarks even. */
129 seed.base.attr = TDB_ATTRIBUTE_SEED;
130 seed.base.next = NULL;
133 log.base.attr = TDB_ATTRIBUTE_LOG;
134 log.base.next = &seed;
135 log.log.fn = tdb_log;
137 if (argv[1] && strcmp(argv[1], "--internal") == 0) {
138 flags = TDB_INTERNAL;
142 if (argv[1] && strcmp(argv[1], "--transaction") == 0) {
147 if (argv[1] && strcmp(argv[1], "--no-sync") == 0) {
152 if (argv[1] && strcmp(argv[1], "--summary") == 0) {
157 if (argv[1] && strcmp(argv[1], "--stats") == 0) {
163 tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR|O_CREAT|O_TRUNC,
166 err(1, "Opening /tmp/speed.tdb");
168 key.dptr = (void *)&i;
169 key.dsize = sizeof(i);
179 stopat = atoi(argv[1]);
184 /* Add 1000 records. */
185 printf("Adding %u records: ", num); fflush(stdout);
186 if (transaction && (ecode = tdb_transaction_start(tdb)))
187 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
188 gettimeofday(&start, NULL);
189 for (i = 0; i < num; i++)
190 if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
191 errx(1, "Inserting key %u in tdb: %s",
192 i, tdb_errorstr(ecode));
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());
199 if (tdb_check(tdb, NULL, NULL))
200 errx(1, "tdb_check failed!");
203 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
204 printf("%s\n", sumstr);
208 dump_and_clear_stats(&tdb, flags, &log);
210 if (++stage == stopat)
213 /* Finding 1000 records. */
214 printf("Finding %u records: ", num); fflush(stdout);
215 if (transaction && (ecode = tdb_transaction_start(tdb)))
216 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
217 gettimeofday(&start, NULL);
218 for (i = 0; i < num; i++) {
219 struct tdb_data dbuf;
220 if ((ecode = tdb_fetch(tdb, key, &dbuf)) != TDB_SUCCESS
221 || *(int *)dbuf.dptr != i) {
222 errx(1, "Fetching key %u in tdb gave %u",
223 i, ecode ? ecode : *(int *)dbuf.dptr);
226 gettimeofday(&stop, NULL);
227 if (transaction && (ecode = tdb_transaction_commit(tdb)))
228 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
229 printf(" %zu ns (%zu bytes)\n",
230 normalize(&start, &stop, num), file_size());
231 if (tdb_check(tdb, NULL, NULL))
232 errx(1, "tdb_check failed!");
235 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
236 printf("%s\n", sumstr);
240 dump_and_clear_stats(&tdb, flags, &log);
241 if (++stage == stopat)
244 /* Missing 1000 records. */
245 printf("Missing %u records: ", num); fflush(stdout);
246 if (transaction && (ecode = tdb_transaction_start(tdb)))
247 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
248 gettimeofday(&start, NULL);
249 for (i = num; i < num*2; i++) {
250 struct tdb_data dbuf;
251 ecode = tdb_fetch(tdb, key, &dbuf);
252 if (ecode != TDB_ERR_NOEXIST)
253 errx(1, "Fetching key %u in tdb gave %s",
254 i, tdb_errorstr(ecode));
256 gettimeofday(&stop, NULL);
257 if (transaction && (ecode = tdb_transaction_commit(tdb)))
258 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
259 printf(" %zu ns (%zu bytes)\n",
260 normalize(&start, &stop, num), file_size());
261 if (tdb_check(tdb, NULL, NULL))
262 errx(1, "tdb_check failed!");
265 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
266 printf("%s\n", sumstr);
270 dump_and_clear_stats(&tdb, flags, &log);
271 if (++stage == stopat)
274 /* Traverse 1000 records. */
275 printf("Traversing %u records: ", num); fflush(stdout);
276 if (transaction && (ecode = tdb_transaction_start(tdb)))
277 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
279 gettimeofday(&start, NULL);
280 if (tdb_traverse(tdb, count_record, &i) != num)
281 errx(1, "Traverse returned wrong number of records");
282 if (i != (num - 1) * (num / 2))
283 errx(1, "Traverse tallied to %u", i);
284 gettimeofday(&stop, NULL);
285 if (transaction && (ecode = tdb_transaction_commit(tdb)))
286 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
287 printf(" %zu ns (%zu bytes)\n",
288 normalize(&start, &stop, num), file_size());
289 if (tdb_check(tdb, NULL, NULL))
290 errx(1, "tdb_check failed!");
293 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
294 printf("%s\n", sumstr);
298 dump_and_clear_stats(&tdb, flags, &log);
299 if (++stage == stopat)
302 /* Delete 1000 records (not in order). */
303 printf("Deleting %u records: ", num); fflush(stdout);
304 if (transaction && (ecode = tdb_transaction_start(tdb)))
305 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
306 gettimeofday(&start, NULL);
307 for (j = 0; j < num; j++) {
308 i = (j + 100003) % num;
309 if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
310 errx(1, "Deleting key %u in tdb: %s",
311 i, tdb_errorstr(ecode));
313 gettimeofday(&stop, NULL);
314 if (transaction && (ecode = tdb_transaction_commit(tdb)))
315 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
316 printf(" %zu ns (%zu bytes)\n",
317 normalize(&start, &stop, num), file_size());
318 if (tdb_check(tdb, NULL, NULL))
319 errx(1, "tdb_check failed!");
322 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
323 printf("%s\n", sumstr);
327 dump_and_clear_stats(&tdb, flags, &log);
328 if (++stage == stopat)
331 /* Re-add 1000 records (not in order). */
332 printf("Re-adding %u records: ", num); fflush(stdout);
333 if (transaction && (ecode = tdb_transaction_start(tdb)))
334 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
335 gettimeofday(&start, NULL);
336 for (j = 0; j < num; j++) {
337 i = (j + 100003) % num;
338 if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
339 errx(1, "Inserting key %u in tdb: %s",
340 i, tdb_errorstr(ecode));
342 gettimeofday(&stop, NULL);
343 if (transaction && (ecode = tdb_transaction_commit(tdb)))
344 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
345 printf(" %zu ns (%zu bytes)\n",
346 normalize(&start, &stop, num), file_size());
347 if (tdb_check(tdb, NULL, NULL))
348 errx(1, "tdb_check failed!");
351 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
352 printf("%s\n", sumstr);
356 dump_and_clear_stats(&tdb, flags, &log);
357 if (++stage == stopat)
360 /* Append 1000 records. */
361 if (transaction && (ecode = tdb_transaction_start(tdb)))
362 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
363 printf("Appending %u records: ", num); fflush(stdout);
364 gettimeofday(&start, NULL);
365 for (i = 0; i < num; i++)
366 if ((ecode = tdb_append(tdb, key, data)) != TDB_SUCCESS)
367 errx(1, "Appending key %u in tdb: %s",
368 i, tdb_errorstr(ecode));
369 gettimeofday(&stop, NULL);
370 if (transaction && (ecode = tdb_transaction_commit(tdb)))
371 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
372 printf(" %zu ns (%zu bytes)\n",
373 normalize(&start, &stop, num), file_size());
374 if (tdb_check(tdb, NULL, NULL))
375 errx(1, "tdb_check failed!");
378 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
379 printf("%s\n", sumstr);
382 if (++stage == stopat)
385 /* Churn 1000 records: not in order! */
386 if (transaction && (ecode = tdb_transaction_start(tdb)))
387 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
388 printf("Churning %u records: ", num); fflush(stdout);
389 gettimeofday(&start, NULL);
390 for (j = 0; j < num; j++) {
391 i = (j + 1000019) % num;
392 if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
393 errx(1, "Deleting key %u in tdb: %s",
394 i, tdb_errorstr(ecode));
396 if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
397 errx(1, "Inserting key %u in tdb: %s",
398 i, tdb_errorstr(ecode));
400 gettimeofday(&stop, NULL);
401 if (transaction && (ecode = tdb_transaction_commit(tdb)))
402 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
403 printf(" %zu ns (%zu bytes)\n",
404 normalize(&start, &stop, num), file_size());
406 if (tdb_check(tdb, NULL, NULL))
407 errx(1, "tdb_check failed!");
410 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
411 printf("%s\n", sumstr);
415 dump_and_clear_stats(&tdb, flags, &log);
416 if (++stage == stopat)