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_attribute_stats *stats)
48 printf("allocs = %llu\n",
49 (unsigned long long)stats->allocs);
50 printf(" alloc_subhash = %llu\n",
51 (unsigned long long)stats->alloc_subhash);
52 printf(" alloc_chain = %llu\n",
53 (unsigned long long)stats->alloc_chain);
54 printf(" alloc_bucket_exact = %llu\n",
55 (unsigned long long)stats->alloc_bucket_exact);
56 printf(" alloc_bucket_max = %llu\n",
57 (unsigned long long)stats->alloc_bucket_max);
58 printf(" alloc_leftover = %llu\n",
59 (unsigned long long)stats->alloc_leftover);
60 printf(" alloc_coalesce_tried = %llu\n",
61 (unsigned long long)stats->alloc_coalesce_tried);
62 printf(" alloc_coalesce_lockfail = %llu\n",
63 (unsigned long long)stats->alloc_coalesce_lockfail);
64 printf(" alloc_coalesce_race = %llu\n",
65 (unsigned long long)stats->alloc_coalesce_race);
66 printf(" alloc_coalesce_succeeded = %llu\n",
67 (unsigned long long)stats->alloc_coalesce_succeeded);
68 printf(" alloc_coalesce_num_merged = %llu\n",
69 (unsigned long long)stats->alloc_coalesce_num_merged);
70 printf("compares = %llu\n",
71 (unsigned long long)stats->compares);
72 printf(" compare_wrong_bucket = %llu\n",
73 (unsigned long long)stats->compare_wrong_bucket);
74 printf(" compare_wrong_offsetbits = %llu\n",
75 (unsigned long long)stats->compare_wrong_offsetbits);
76 printf(" compare_wrong_keylen = %llu\n",
77 (unsigned long long)stats->compare_wrong_keylen);
78 printf(" compare_wrong_rechash = %llu\n",
79 (unsigned long long)stats->compare_wrong_rechash);
80 printf(" compare_wrong_keycmp = %llu\n",
81 (unsigned long long)stats->compare_wrong_keycmp);
82 printf("expands = %llu\n",
83 (unsigned long long)stats->expands);
84 printf("frees = %llu\n",
85 (unsigned long long)stats->frees);
86 printf("locks = %llu\n",
87 (unsigned long long)stats->locks);
88 printf(" lock_lowlevel = %llu\n",
89 (unsigned long long)stats->lock_lowlevel);
90 printf(" lock_nonblock = %llu\n",
91 (unsigned long long)stats->lock_nonblock);
94 memset(&stats->allocs, 0, (char *)(stats+1) - (char *)&stats->allocs);
97 static void tdb_log(struct tdb_context *tdb, enum tdb_log_level level,
98 const char *message, void *data)
100 fputs(message, stderr);
104 int main(int argc, char *argv[])
106 unsigned int i, j, num = 1000, stage = 0, stopat = -1;
107 int flags = TDB_DEFAULT;
108 bool transaction = false, summary = false;
110 struct tdb_context *tdb;
111 struct timeval start, stop;
112 union tdb_attribute seed, stats, log;
113 enum TDB_ERROR ecode;
115 /* Try to keep benchmarks even. */
116 seed.base.attr = TDB_ATTRIBUTE_SEED;
117 seed.base.next = NULL;
120 log.base.attr = TDB_ATTRIBUTE_LOG;
121 log.base.next = &seed;
122 log.log.fn = tdb_log;
124 memset(&stats, 0, sizeof(stats));
125 stats.base.attr = TDB_ATTRIBUTE_STATS;
126 stats.base.next = NULL;
127 stats.stats.size = sizeof(stats);
129 if (argv[1] && strcmp(argv[1], "--internal") == 0) {
130 flags = TDB_INTERNAL;
134 if (argv[1] && strcmp(argv[1], "--transaction") == 0) {
139 if (argv[1] && strcmp(argv[1], "--no-sync") == 0) {
144 if (argv[1] && strcmp(argv[1], "--summary") == 0) {
149 if (argv[1] && strcmp(argv[1], "--stats") == 0) {
150 seed.base.next = &stats;
155 tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR|O_CREAT|O_TRUNC,
158 err(1, "Opening /tmp/speed.tdb");
160 key.dptr = (void *)&i;
161 key.dsize = sizeof(i);
171 stopat = atoi(argv[1]);
176 /* Add 1000 records. */
177 printf("Adding %u records: ", num); fflush(stdout);
178 if (transaction && (ecode = tdb_transaction_start(tdb)))
179 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
180 gettimeofday(&start, NULL);
181 for (i = 0; i < num; i++)
182 if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
183 errx(1, "Inserting key %u in tdb: %s",
184 i, tdb_errorstr(ecode));
185 gettimeofday(&stop, NULL);
186 if (transaction && (ecode = tdb_transaction_commit(tdb)))
187 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
188 printf(" %zu ns (%zu bytes)\n",
189 normalize(&start, &stop, num), file_size());
191 if (tdb_check(tdb, NULL, NULL))
192 errx(1, "tdb_check failed!");
195 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
196 printf("%s\n", sumstr);
200 dump_and_clear_stats(&stats.stats);
202 if (++stage == stopat)
205 /* Finding 1000 records. */
206 printf("Finding %u records: ", num); fflush(stdout);
207 if (transaction && (ecode = tdb_transaction_start(tdb)))
208 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
209 gettimeofday(&start, NULL);
210 for (i = 0; i < num; i++) {
211 struct tdb_data dbuf;
212 if ((ecode = tdb_fetch(tdb, key, &dbuf)) != TDB_SUCCESS
213 || *(int *)dbuf.dptr != i) {
214 errx(1, "Fetching key %u in tdb gave %u",
215 i, ecode ? ecode : *(int *)dbuf.dptr);
218 gettimeofday(&stop, NULL);
219 if (transaction && (ecode = tdb_transaction_commit(tdb)))
220 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
221 printf(" %zu ns (%zu bytes)\n",
222 normalize(&start, &stop, num), file_size());
223 if (tdb_check(tdb, NULL, NULL))
224 errx(1, "tdb_check failed!");
227 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
228 printf("%s\n", sumstr);
232 dump_and_clear_stats(&stats.stats);
233 if (++stage == stopat)
236 /* Missing 1000 records. */
237 printf("Missing %u records: ", num); fflush(stdout);
238 if (transaction && (ecode = tdb_transaction_start(tdb)))
239 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
240 gettimeofday(&start, NULL);
241 for (i = num; i < num*2; i++) {
242 struct tdb_data dbuf;
243 ecode = tdb_fetch(tdb, key, &dbuf);
244 if (ecode != TDB_ERR_NOEXIST)
245 errx(1, "Fetching key %u in tdb gave %s",
246 i, tdb_errorstr(ecode));
248 gettimeofday(&stop, NULL);
249 if (transaction && (ecode = tdb_transaction_commit(tdb)))
250 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
251 printf(" %zu ns (%zu bytes)\n",
252 normalize(&start, &stop, num), file_size());
253 if (tdb_check(tdb, NULL, NULL))
254 errx(1, "tdb_check failed!");
257 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
258 printf("%s\n", sumstr);
262 dump_and_clear_stats(&stats.stats);
263 if (++stage == stopat)
266 /* Traverse 1000 records. */
267 printf("Traversing %u records: ", num); fflush(stdout);
268 if (transaction && (ecode = tdb_transaction_start(tdb)))
269 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
271 gettimeofday(&start, NULL);
272 if (tdb_traverse(tdb, count_record, &i) != num)
273 errx(1, "Traverse returned wrong number of records");
274 if (i != (num - 1) * (num / 2))
275 errx(1, "Traverse tallied to %u", i);
276 gettimeofday(&stop, NULL);
277 if (transaction && (ecode = tdb_transaction_commit(tdb)))
278 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
279 printf(" %zu ns (%zu bytes)\n",
280 normalize(&start, &stop, num), file_size());
281 if (tdb_check(tdb, NULL, NULL))
282 errx(1, "tdb_check failed!");
285 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
286 printf("%s\n", sumstr);
290 dump_and_clear_stats(&stats.stats);
291 if (++stage == stopat)
294 /* Delete 1000 records (not in order). */
295 printf("Deleting %u records: ", num); fflush(stdout);
296 if (transaction && (ecode = tdb_transaction_start(tdb)))
297 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
298 gettimeofday(&start, NULL);
299 for (j = 0; j < num; j++) {
300 i = (j + 100003) % num;
301 if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
302 errx(1, "Deleting key %u in tdb: %s",
303 i, tdb_errorstr(ecode));
305 gettimeofday(&stop, NULL);
306 if (transaction && (ecode = tdb_transaction_commit(tdb)))
307 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
308 printf(" %zu ns (%zu bytes)\n",
309 normalize(&start, &stop, num), file_size());
310 if (tdb_check(tdb, NULL, NULL))
311 errx(1, "tdb_check failed!");
314 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
315 printf("%s\n", sumstr);
319 dump_and_clear_stats(&stats.stats);
320 if (++stage == stopat)
323 /* Re-add 1000 records (not in order). */
324 printf("Re-adding %u records: ", num); fflush(stdout);
325 if (transaction && (ecode = tdb_transaction_start(tdb)))
326 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
327 gettimeofday(&start, NULL);
328 for (j = 0; j < num; j++) {
329 i = (j + 100003) % num;
330 if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
331 errx(1, "Inserting key %u in tdb: %s",
332 i, tdb_errorstr(ecode));
334 gettimeofday(&stop, NULL);
335 if (transaction && (ecode = tdb_transaction_commit(tdb)))
336 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
337 printf(" %zu ns (%zu bytes)\n",
338 normalize(&start, &stop, num), file_size());
339 if (tdb_check(tdb, NULL, NULL))
340 errx(1, "tdb_check failed!");
343 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
344 printf("%s\n", sumstr);
348 dump_and_clear_stats(&stats.stats);
349 if (++stage == stopat)
352 /* Append 1000 records. */
353 if (transaction && (ecode = tdb_transaction_start(tdb)))
354 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
355 printf("Appending %u records: ", num); fflush(stdout);
356 gettimeofday(&start, NULL);
357 for (i = 0; i < num; i++)
358 if ((ecode = tdb_append(tdb, key, data)) != TDB_SUCCESS)
359 errx(1, "Appending key %u in tdb: %s",
360 i, tdb_errorstr(ecode));
361 gettimeofday(&stop, NULL);
362 if (transaction && (ecode = tdb_transaction_commit(tdb)))
363 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
364 printf(" %zu ns (%zu bytes)\n",
365 normalize(&start, &stop, num), file_size());
366 if (tdb_check(tdb, NULL, NULL))
367 errx(1, "tdb_check failed!");
370 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
371 printf("%s\n", sumstr);
374 if (++stage == stopat)
377 /* Churn 1000 records: not in order! */
378 if (transaction && (ecode = tdb_transaction_start(tdb)))
379 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
380 printf("Churning %u records: ", num); fflush(stdout);
381 gettimeofday(&start, NULL);
382 for (j = 0; j < num; j++) {
383 i = (j + 1000019) % num;
384 if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
385 errx(1, "Deleting key %u in tdb: %s",
386 i, tdb_errorstr(ecode));
388 if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
389 errx(1, "Inserting key %u in tdb: %s",
390 i, tdb_errorstr(ecode));
392 gettimeofday(&stop, NULL);
393 if (transaction && (ecode = tdb_transaction_commit(tdb)))
394 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
395 printf(" %zu ns (%zu bytes)\n",
396 normalize(&start, &stop, num), file_size());
398 if (tdb_check(tdb, NULL, NULL))
399 errx(1, "tdb_check failed!");
402 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
403 printf("%s\n", sumstr);
407 dump_and_clear_stats(&stats.stats);
408 if (++stage == stopat)