tdb2: allow multiple chain locks.
[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 <ccan/tdb2/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_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);
103
104         /* Now clear. */
105         tdb_close(*tdb);
106         *tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR, 0, attr);
107 }
108
109 static void tdb_log(struct tdb_context *tdb, enum tdb_log_level level,
110                     const char *message, void *data)
111 {
112         fputs(message, stderr);
113         putc('\n', stderr);
114 }
115
116 int main(int argc, char *argv[])
117 {
118         unsigned int i, j, num = 1000, stage = 0, stopat = -1;
119         int flags = TDB_DEFAULT;
120         bool transaction = false, summary = false;
121         TDB_DATA key, data;
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;
127
128         /* Try to keep benchmarks even. */
129         seed.base.attr = TDB_ATTRIBUTE_SEED;
130         seed.base.next = NULL;
131         seed.seed.seed = 0;
132
133         log.base.attr = TDB_ATTRIBUTE_LOG;
134         log.base.next = &seed;
135         log.log.fn = tdb_log;
136
137         if (argv[1] && strcmp(argv[1], "--internal") == 0) {
138                 flags = TDB_INTERNAL;
139                 argc--;
140                 argv++;
141         }
142         if (argv[1] && strcmp(argv[1], "--transaction") == 0) {
143                 transaction = true;
144                 argc--;
145                 argv++;
146         }
147         if (argv[1] && strcmp(argv[1], "--no-sync") == 0) {
148                 flags |= TDB_NOSYNC;
149                 argc--;
150                 argv++;
151         }
152         if (argv[1] && strcmp(argv[1], "--summary") == 0) {
153                 summary = true;
154                 argc--;
155                 argv++;
156         }
157         if (argv[1] && strcmp(argv[1], "--stats") == 0) {
158                 do_stats = true;
159                 argc--;
160                 argv++;
161         }
162
163         tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR|O_CREAT|O_TRUNC,
164                        0600, &log);
165         if (!tdb)
166                 err(1, "Opening /tmp/speed.tdb");
167
168         key.dptr = (void *)&i;
169         key.dsize = sizeof(i);
170         data = key;
171
172         if (argv[1]) {
173                 num = atoi(argv[1]);
174                 argv++;
175                 argc--;
176         }
177
178         if (argv[1]) {
179                 stopat = atoi(argv[1]);
180                 argv++;
181                 argc--;
182         }
183
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());
198
199         if (tdb_check(tdb, NULL, NULL))
200                 errx(1, "tdb_check failed!");
201         if (summary) {
202                 char *sumstr = NULL;
203                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
204                 printf("%s\n", sumstr);
205                 free(sumstr);
206         }
207         if (do_stats)
208                 dump_and_clear_stats(&tdb, flags, &log);
209
210         if (++stage == stopat)
211                 exit(0);
212
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);
224                 }
225         }
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!");
233         if (summary) {
234                 char *sumstr = NULL;
235                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
236                 printf("%s\n", sumstr);
237                 free(sumstr);
238         }
239         if (do_stats)
240                 dump_and_clear_stats(&tdb, flags, &log);
241         if (++stage == stopat)
242                 exit(0);
243
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));
255         }
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!");
263         if (summary) {
264                 char *sumstr = NULL;
265                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
266                 printf("%s\n", sumstr);
267                 free(sumstr);
268         }
269         if (do_stats)
270                 dump_and_clear_stats(&tdb, flags, &log);
271         if (++stage == stopat)
272                 exit(0);
273
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));
278         i = 0;
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!");
291         if (summary) {
292                 char *sumstr = NULL;
293                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
294                 printf("%s\n", sumstr);
295                 free(sumstr);
296         }
297         if (do_stats)
298                 dump_and_clear_stats(&tdb, flags, &log);
299         if (++stage == stopat)
300                 exit(0);
301
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));
312         }
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!");
320         if (summary) {
321                 char *sumstr = NULL;
322                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
323                 printf("%s\n", sumstr);
324                 free(sumstr);
325         }
326         if (do_stats)
327                 dump_and_clear_stats(&tdb, flags, &log);
328         if (++stage == stopat)
329                 exit(0);
330
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));
341         }
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!");
349         if (summary) {
350                 char *sumstr = NULL;
351                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
352                 printf("%s\n", sumstr);
353                 free(sumstr);
354         }
355         if (do_stats)
356                 dump_and_clear_stats(&tdb, flags, &log);
357         if (++stage == stopat)
358                 exit(0);
359
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!");
376         if (summary) {
377                 char *sumstr = NULL;
378                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
379                 printf("%s\n", sumstr);
380                 free(sumstr);
381         }
382         if (++stage == stopat)
383                 exit(0);
384
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));
395                 i += num;
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));
399         }
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());
405
406         if (tdb_check(tdb, NULL, NULL))
407                 errx(1, "tdb_check failed!");
408         if (summary) {
409                 char *sumstr = NULL;
410                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
411                 printf("%s\n", sumstr);
412                 free(sumstr);
413         }
414         if (do_stats)
415                 dump_and_clear_stats(&tdb, flags, &log);
416         if (++stage == stopat)
417                 exit(0);
418
419         return 0;
420 }