check_type: fix incorrect documentation.
[ccan] / ccan / tdb2 / tools / speed.c
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 "tdb2.h"
14
15 /* Nanoseconds per operation */
16 static size_t normalize(const struct timeval *start,
17                         const struct timeval *stop,
18                         unsigned int num)
19 {
20         struct timeval diff;
21
22         timersub(stop, start, &diff);
23
24         /* Floating point is more accurate here. */
25         return (double)(diff.tv_sec * 1000000 + diff.tv_usec)
26                 / num * 1000;
27 }
28
29 static size_t file_size(void)
30 {
31         struct stat st;
32
33         if (stat("/tmp/speed.tdb", &st) != 0)
34                 return -1;
35         return st.st_size;
36 }
37
38 static int count_record(struct tdb_context *tdb,
39                         TDB_DATA key, TDB_DATA data, void *p)
40 {
41         int *total = p;
42         *total += *(int *)data.dptr;
43         return 0;
44 }
45
46 static void dump_and_clear_stats(struct tdb_context **tdb,
47                                  int flags,
48                                  union tdb_attribute *attr)
49 {
50         union tdb_attribute stats;
51         enum TDB_ERROR ecode;
52
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));
58
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_iterate_clash = %llu\n",
74                (unsigned long long)stats.stats.alloc_coalesce_iterate_clash);
75         printf("    alloc_coalesce_lockfail = %llu\n",
76                (unsigned long long)stats.stats.alloc_coalesce_lockfail);
77         printf("    alloc_coalesce_race = %llu\n",
78                (unsigned long long)stats.stats.alloc_coalesce_race);
79         printf("    alloc_coalesce_succeeded = %llu\n",
80                (unsigned long long)stats.stats.alloc_coalesce_succeeded);
81         printf("      alloc_coalesce_num_merged = %llu\n",
82                (unsigned long long)stats.stats.alloc_coalesce_num_merged);
83         printf("compares = %llu\n",
84                (unsigned long long)stats.stats.compares);
85         printf("  compare_wrong_bucket = %llu\n",
86                (unsigned long long)stats.stats.compare_wrong_bucket);
87         printf("  compare_wrong_offsetbits = %llu\n",
88                (unsigned long long)stats.stats.compare_wrong_offsetbits);
89         printf("  compare_wrong_keylen = %llu\n",
90                (unsigned long long)stats.stats.compare_wrong_keylen);
91         printf("  compare_wrong_rechash = %llu\n",
92                (unsigned long long)stats.stats.compare_wrong_rechash);
93         printf("  compare_wrong_keycmp = %llu\n",
94                (unsigned long long)stats.stats.compare_wrong_keycmp);
95         printf("transactions = %llu\n",
96                (unsigned long long)stats.stats.transactions);
97         printf("  transaction_cancel = %llu\n",
98                (unsigned long long)stats.stats.transaction_cancel);
99         printf("  transaction_nest = %llu\n",
100                (unsigned long long)stats.stats.transaction_nest);
101         printf("  transaction_expand_file = %llu\n",
102                (unsigned long long)stats.stats.transaction_expand_file);
103         printf("  transaction_read_direct = %llu\n",
104                (unsigned long long)stats.stats.transaction_read_direct);
105         printf("    transaction_read_direct_fail = %llu\n",
106                (unsigned long long)stats.stats.transaction_read_direct_fail);
107         printf("  transaction_write_direct = %llu\n",
108                (unsigned long long)stats.stats.transaction_write_direct);
109         printf("    transaction_write_direct_fail = %llu\n",
110                (unsigned long long)stats.stats.transaction_write_direct_fail);
111         printf("expands = %llu\n",
112                (unsigned long long)stats.stats.expands);
113         printf("frees = %llu\n",
114                (unsigned long long)stats.stats.frees);
115         printf("locks = %llu\n",
116                (unsigned long long)stats.stats.locks);
117         printf("  lock_lowlevel = %llu\n",
118                (unsigned long long)stats.stats.lock_lowlevel);
119         printf("  lock_nonblock = %llu\n",
120                (unsigned long long)stats.stats.lock_nonblock);
121         printf("    lock_nonblock_fail = %llu\n",
122                (unsigned long long)stats.stats.lock_nonblock_fail);
123
124         /* Now clear. */
125         tdb_close(*tdb);
126         *tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR, 0, attr);
127 }
128
129 static void tdb_log(struct tdb_context *tdb,
130                     enum tdb_log_level level,
131                     enum TDB_ERROR ecode,
132                     const char *message,
133                     void *data)
134 {
135         fprintf(stderr, "tdb:%s:%s:%s\n",
136                 tdb_name(tdb), tdb_errorstr(ecode), message);
137 }
138
139 int main(int argc, char *argv[])
140 {
141         unsigned int i, j, num = 1000, stage = 0, stopat = -1;
142         int flags = TDB_DEFAULT;
143         bool transaction = false, summary = false;
144         TDB_DATA key, data;
145         struct tdb_context *tdb;
146         struct timeval start, stop;
147         union tdb_attribute seed, log;
148         bool do_stats = false;
149         enum TDB_ERROR ecode;
150
151         /* Try to keep benchmarks even. */
152         seed.base.attr = TDB_ATTRIBUTE_SEED;
153         seed.base.next = NULL;
154         seed.seed.seed = 0;
155
156         log.base.attr = TDB_ATTRIBUTE_LOG;
157         log.base.next = &seed;
158         log.log.fn = tdb_log;
159
160         if (argv[1] && strcmp(argv[1], "--internal") == 0) {
161                 flags = TDB_INTERNAL;
162                 argc--;
163                 argv++;
164         }
165         if (argv[1] && strcmp(argv[1], "--transaction") == 0) {
166                 transaction = true;
167                 argc--;
168                 argv++;
169         }
170         if (argv[1] && strcmp(argv[1], "--no-sync") == 0) {
171                 flags |= TDB_NOSYNC;
172                 argc--;
173                 argv++;
174         }
175         if (argv[1] && strcmp(argv[1], "--summary") == 0) {
176                 summary = true;
177                 argc--;
178                 argv++;
179         }
180         if (argv[1] && strcmp(argv[1], "--stats") == 0) {
181                 do_stats = true;
182                 argc--;
183                 argv++;
184         }
185
186         tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR|O_CREAT|O_TRUNC,
187                        0600, &log);
188         if (!tdb)
189                 err(1, "Opening /tmp/speed.tdb");
190
191         key.dptr = (void *)&i;
192         key.dsize = sizeof(i);
193         data = key;
194
195         if (argv[1]) {
196                 num = atoi(argv[1]);
197                 argv++;
198                 argc--;
199         }
200
201         if (argv[1]) {
202                 stopat = atoi(argv[1]);
203                 argv++;
204                 argc--;
205         }
206
207         /* Add 1000 records. */
208         printf("Adding %u records: ", num); fflush(stdout);
209         if (transaction && (ecode = tdb_transaction_start(tdb)))
210                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
211         gettimeofday(&start, NULL);
212         for (i = 0; i < num; i++)
213                 if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
214                         errx(1, "Inserting key %u in tdb: %s",
215                              i, tdb_errorstr(ecode));
216         gettimeofday(&stop, NULL);
217         if (transaction && (ecode = tdb_transaction_commit(tdb)))
218                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
219         printf(" %zu ns (%zu bytes)\n",
220                normalize(&start, &stop, num), file_size());
221
222         if (tdb_check(tdb, NULL, NULL))
223                 errx(1, "tdb_check failed!");
224         if (summary) {
225                 char *sumstr = NULL;
226                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
227                 printf("%s\n", sumstr);
228                 free(sumstr);
229         }
230         if (do_stats)
231                 dump_and_clear_stats(&tdb, flags, &log);
232
233         if (++stage == stopat)
234                 exit(0);
235
236         /* Finding 1000 records. */
237         printf("Finding %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 = 0; i < num; i++) {
242                 struct tdb_data dbuf;
243                 if ((ecode = tdb_fetch(tdb, key, &dbuf)) != TDB_SUCCESS
244                     || *(int *)dbuf.dptr != i) {
245                         errx(1, "Fetching key %u in tdb gave %u",
246                              i, ecode ? ecode : *(int *)dbuf.dptr);
247                 }
248         }
249         gettimeofday(&stop, NULL);
250         if (transaction && (ecode = tdb_transaction_commit(tdb)))
251                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
252         printf(" %zu ns (%zu bytes)\n",
253                normalize(&start, &stop, num), file_size());
254         if (tdb_check(tdb, NULL, NULL))
255                 errx(1, "tdb_check failed!");
256         if (summary) {
257                 char *sumstr = NULL;
258                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
259                 printf("%s\n", sumstr);
260                 free(sumstr);
261         }
262         if (do_stats)
263                 dump_and_clear_stats(&tdb, flags, &log);
264         if (++stage == stopat)
265                 exit(0);
266
267         /* Missing 1000 records. */
268         printf("Missing %u records: ", num); fflush(stdout);
269         if (transaction && (ecode = tdb_transaction_start(tdb)))
270                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
271         gettimeofday(&start, NULL);
272         for (i = num; i < num*2; i++) {
273                 struct tdb_data dbuf;
274                 ecode = tdb_fetch(tdb, key, &dbuf);
275                 if (ecode != TDB_ERR_NOEXIST)
276                         errx(1, "Fetching key %u in tdb gave %s",
277                              i, tdb_errorstr(ecode));
278         }
279         gettimeofday(&stop, NULL);
280         if (transaction && (ecode = tdb_transaction_commit(tdb)))
281                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
282         printf(" %zu ns (%zu bytes)\n",
283                normalize(&start, &stop, num), file_size());
284         if (tdb_check(tdb, NULL, NULL))
285                 errx(1, "tdb_check failed!");
286         if (summary) {
287                 char *sumstr = NULL;
288                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
289                 printf("%s\n", sumstr);
290                 free(sumstr);
291         }
292         if (do_stats)
293                 dump_and_clear_stats(&tdb, flags, &log);
294         if (++stage == stopat)
295                 exit(0);
296
297         /* Traverse 1000 records. */
298         printf("Traversing %u records: ", num); fflush(stdout);
299         if (transaction && (ecode = tdb_transaction_start(tdb)))
300                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
301         i = 0;
302         gettimeofday(&start, NULL);
303         if (tdb_traverse(tdb, count_record, &i) != num)
304                 errx(1, "Traverse returned wrong number of records");
305         if (i != (num - 1) * (num / 2))
306                 errx(1, "Traverse tallied to %u", i);
307         gettimeofday(&stop, NULL);
308         if (transaction && (ecode = tdb_transaction_commit(tdb)))
309                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
310         printf(" %zu ns (%zu bytes)\n",
311                normalize(&start, &stop, num), file_size());
312         if (tdb_check(tdb, NULL, NULL))
313                 errx(1, "tdb_check failed!");
314         if (summary) {
315                 char *sumstr = NULL;
316                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
317                 printf("%s\n", sumstr);
318                 free(sumstr);
319         }
320         if (do_stats)
321                 dump_and_clear_stats(&tdb, flags, &log);
322         if (++stage == stopat)
323                 exit(0);
324
325         /* Delete 1000 records (not in order). */
326         printf("Deleting %u records: ", num); fflush(stdout);
327         if (transaction && (ecode = tdb_transaction_start(tdb)))
328                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
329         gettimeofday(&start, NULL);
330         for (j = 0; j < num; j++) {
331                 i = (j + 100003) % num;
332                 if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
333                         errx(1, "Deleting key %u in tdb: %s",
334                              i, tdb_errorstr(ecode));
335         }
336         gettimeofday(&stop, NULL);
337         if (transaction && (ecode = tdb_transaction_commit(tdb)))
338                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
339         printf(" %zu ns (%zu bytes)\n",
340                normalize(&start, &stop, num), file_size());
341         if (tdb_check(tdb, NULL, NULL))
342                 errx(1, "tdb_check failed!");
343         if (summary) {
344                 char *sumstr = NULL;
345                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
346                 printf("%s\n", sumstr);
347                 free(sumstr);
348         }
349         if (do_stats)
350                 dump_and_clear_stats(&tdb, flags, &log);
351         if (++stage == stopat)
352                 exit(0);
353
354         /* Re-add 1000 records (not in order). */
355         printf("Re-adding %u records: ", num); fflush(stdout);
356         if (transaction && (ecode = tdb_transaction_start(tdb)))
357                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
358         gettimeofday(&start, NULL);
359         for (j = 0; j < num; j++) {
360                 i = (j + 100003) % num;
361                 if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
362                         errx(1, "Inserting key %u in tdb: %s",
363                              i, tdb_errorstr(ecode));
364         }
365         gettimeofday(&stop, NULL);
366         if (transaction && (ecode = tdb_transaction_commit(tdb)))
367                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
368         printf(" %zu ns (%zu bytes)\n",
369                normalize(&start, &stop, num), file_size());
370         if (tdb_check(tdb, NULL, NULL))
371                 errx(1, "tdb_check failed!");
372         if (summary) {
373                 char *sumstr = NULL;
374                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
375                 printf("%s\n", sumstr);
376                 free(sumstr);
377         }
378         if (do_stats)
379                 dump_and_clear_stats(&tdb, flags, &log);
380         if (++stage == stopat)
381                 exit(0);
382
383         /* Append 1000 records. */
384         if (transaction && (ecode = tdb_transaction_start(tdb)))
385                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
386         printf("Appending %u records: ", num); fflush(stdout);
387         gettimeofday(&start, NULL);
388         for (i = 0; i < num; i++)
389                 if ((ecode = tdb_append(tdb, key, data)) != TDB_SUCCESS)
390                         errx(1, "Appending key %u in tdb: %s",
391                              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());
397         if (tdb_check(tdb, NULL, NULL))
398                 errx(1, "tdb_check failed!");
399         if (summary) {
400                 char *sumstr = NULL;
401                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
402                 printf("%s\n", sumstr);
403                 free(sumstr);
404         }
405         if (++stage == stopat)
406                 exit(0);
407
408         /* Churn 1000 records: not in order! */
409         if (transaction && (ecode = tdb_transaction_start(tdb)))
410                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
411         printf("Churning %u records: ", num); fflush(stdout);
412         gettimeofday(&start, NULL);
413         for (j = 0; j < num; j++) {
414                 i = (j + 1000019) % num;
415                 if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
416                         errx(1, "Deleting key %u in tdb: %s",
417                              i, tdb_errorstr(ecode));
418                 i += num;
419                 if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
420                         errx(1, "Inserting key %u in tdb: %s",
421                              i, tdb_errorstr(ecode));
422         }
423         gettimeofday(&stop, NULL);
424         if (transaction && (ecode = tdb_transaction_commit(tdb)))
425                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
426         printf(" %zu ns (%zu bytes)\n",
427                normalize(&start, &stop, num), file_size());
428
429         if (tdb_check(tdb, NULL, NULL))
430                 errx(1, "tdb_check failed!");
431         if (summary) {
432                 char *sumstr = NULL;
433                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
434                 printf("%s\n", sumstr);
435                 free(sumstr);
436         }
437         if (do_stats)
438                 dump_and_clear_stats(&tdb, flags, &log);
439         if (++stage == stopat)
440                 exit(0);
441
442         return 0;
443 }