tdb2: rearrange log function to put data arg at the end.
[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_attribute_stats *stats)
47 {
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);
92
93         /* Now clear. */
94         memset(&stats->allocs, 0, (char *)(stats+1) - (char *)&stats->allocs);
95 }
96
97 static void tdb_log(struct tdb_context *tdb, enum tdb_log_level level,
98                     const char *message, void *data)
99 {
100         fputs(message, stderr);
101 }
102
103 int main(int argc, char *argv[])
104 {
105         unsigned int i, j, num = 1000, stage = 0, stopat = -1;
106         int flags = TDB_DEFAULT;
107         bool transaction = false, summary = false;
108         TDB_DATA key, data;
109         struct tdb_context *tdb;
110         struct timeval start, stop;
111         union tdb_attribute seed, stats, log;
112         enum TDB_ERROR ecode;
113
114         /* Try to keep benchmarks even. */
115         seed.base.attr = TDB_ATTRIBUTE_SEED;
116         seed.base.next = NULL;
117         seed.seed.seed = 0;
118
119         log.base.attr = TDB_ATTRIBUTE_LOG;
120         log.base.next = &seed;
121         log.log.fn = tdb_log;
122
123         memset(&stats, 0, sizeof(stats));
124         stats.base.attr = TDB_ATTRIBUTE_STATS;
125         stats.base.next = NULL;
126         stats.stats.size = sizeof(stats);
127
128         if (argv[1] && strcmp(argv[1], "--internal") == 0) {
129                 flags = TDB_INTERNAL;
130                 argc--;
131                 argv++;
132         }
133         if (argv[1] && strcmp(argv[1], "--transaction") == 0) {
134                 transaction = true;
135                 argc--;
136                 argv++;
137         }
138         if (argv[1] && strcmp(argv[1], "--no-sync") == 0) {
139                 flags |= TDB_NOSYNC;
140                 argc--;
141                 argv++;
142         }
143         if (argv[1] && strcmp(argv[1], "--summary") == 0) {
144                 summary = true;
145                 argc--;
146                 argv++;
147         }
148         if (argv[1] && strcmp(argv[1], "--stats") == 0) {
149                 seed.base.next = &stats;
150                 argc--;
151                 argv++;
152         }
153
154         tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR|O_CREAT|O_TRUNC,
155                        0600, &log);
156         if (!tdb)
157                 err(1, "Opening /tmp/speed.tdb");
158
159         key.dptr = (void *)&i;
160         key.dsize = sizeof(i);
161         data = key;
162
163         if (argv[1]) {
164                 num = atoi(argv[1]);
165                 argv++;
166                 argc--;
167         }
168
169         if (argv[1]) {
170                 stopat = atoi(argv[1]);
171                 argv++;
172                 argc--;
173         }
174
175         if (transaction && (ecode = tdb_transaction_start(tdb)))
176                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
177
178         /* Add 1000 records. */
179         printf("Adding %u records: ", num); fflush(stdout);
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());
190
191         if (summary) {
192                 char *sumstr = NULL;
193                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
194                 printf("%s\n", sumstr);
195                 free(sumstr);
196         }
197         if (seed.base.next)
198                 dump_and_clear_stats(&stats.stats);
199         if (++stage == stopat)
200                 exit(0);
201
202         if (transaction && (ecode = tdb_transaction_start(tdb)))
203                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
204
205         /* Finding 1000 records. */
206         printf("Finding %u records: ", num); fflush(stdout);
207         gettimeofday(&start, NULL);
208         for (i = 0; i < num; i++) {
209                 struct tdb_data dbuf;
210                 if ((ecode = tdb_fetch(tdb, key, &dbuf)) != TDB_SUCCESS
211                     || *(int *)dbuf.dptr != i) {
212                         errx(1, "Fetching key %u in tdb gave %u",
213                              i, ecode ? ecode : *(int *)dbuf.dptr);
214                 }
215         }
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         if (summary) {
222                 char *sumstr = NULL;
223                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
224                 printf("%s\n", sumstr);
225                 free(sumstr);
226         }
227         if (seed.base.next)
228                 dump_and_clear_stats(&stats.stats);
229         if (++stage == stopat)
230                 exit(0);
231
232         if (transaction && (ecode = tdb_transaction_start(tdb)))
233                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
234
235         /* Missing 1000 records. */
236         printf("Missing %u records: ", num); fflush(stdout);
237         gettimeofday(&start, NULL);
238         for (i = num; i < num*2; i++) {
239                 struct tdb_data dbuf;
240                 ecode = tdb_fetch(tdb, key, &dbuf);
241                 if (ecode != TDB_ERR_NOEXIST)
242                         errx(1, "Fetching key %u in tdb gave %s",
243                              i, tdb_errorstr(ecode));
244         }
245         gettimeofday(&stop, NULL);
246         if (transaction && (ecode = tdb_transaction_commit(tdb)))
247                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
248         printf(" %zu ns (%zu bytes)\n",
249                normalize(&start, &stop, num), file_size());
250         if (summary) {
251                 char *sumstr = NULL;
252                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
253                 printf("%s\n", sumstr);
254                 free(sumstr);
255         }
256         if (seed.base.next)
257                 dump_and_clear_stats(&stats.stats);
258         if (++stage == stopat)
259                 exit(0);
260
261         if (transaction && (ecode = tdb_transaction_start(tdb)))
262                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
263
264         /* Traverse 1000 records. */
265         printf("Traversing %u records: ", num); fflush(stdout);
266         i = 0;
267         gettimeofday(&start, NULL);
268         if (tdb_traverse(tdb, count_record, &i) != num)
269                 errx(1, "Traverse returned wrong number of records");
270         if (i != (num - 1) * (num / 2))
271                 errx(1, "Traverse tallied to %u", i);
272         gettimeofday(&stop, NULL);
273         if (transaction && (ecode = tdb_transaction_commit(tdb)))
274                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
275         printf(" %zu ns (%zu bytes)\n",
276                normalize(&start, &stop, num), file_size());
277         if (summary) {
278                 char *sumstr = NULL;
279                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
280                 printf("%s\n", sumstr);
281                 free(sumstr);
282         }
283         if (seed.base.next)
284                 dump_and_clear_stats(&stats.stats);
285         if (++stage == stopat)
286                 exit(0);
287
288         if (transaction && (ecode = tdb_transaction_start(tdb)))
289                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
290
291         /* Delete 1000 records (not in order). */
292         printf("Deleting %u records: ", num); fflush(stdout);
293         gettimeofday(&start, NULL);
294         for (j = 0; j < num; j++) {
295                 i = (j + 100003) % num;
296                 if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
297                         errx(1, "Deleting key %u in tdb: %s",
298                              i, tdb_errorstr(ecode));
299         }
300         gettimeofday(&stop, NULL);
301         if (transaction && (ecode = tdb_transaction_commit(tdb)))
302                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
303         printf(" %zu ns (%zu bytes)\n",
304                normalize(&start, &stop, num), file_size());
305         if (summary) {
306                 char *sumstr = NULL;
307                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
308                 printf("%s\n", sumstr);
309                 free(sumstr);
310         }
311         if (seed.base.next)
312                 dump_and_clear_stats(&stats.stats);
313         if (++stage == stopat)
314                 exit(0);
315
316         if (transaction && (ecode = tdb_transaction_start(tdb)))
317                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
318
319         /* Re-add 1000 records (not in order). */
320         printf("Re-adding %u records: ", num); fflush(stdout);
321         gettimeofday(&start, NULL);
322         for (j = 0; j < num; j++) {
323                 i = (j + 100003) % num;
324                 if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
325                         errx(1, "Inserting key %u in tdb: %s",
326                              i, tdb_errorstr(ecode));
327         }
328         gettimeofday(&stop, NULL);
329         if (transaction && (ecode = tdb_transaction_commit(tdb)))
330                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
331         printf(" %zu ns (%zu bytes)\n",
332                normalize(&start, &stop, num), file_size());
333         if (summary) {
334                 char *sumstr = NULL;
335                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
336                 printf("%s\n", sumstr);
337                 free(sumstr);
338         }
339         if (seed.base.next)
340                 dump_and_clear_stats(&stats.stats);
341         if (++stage == stopat)
342                 exit(0);
343
344         if (transaction && (ecode = tdb_transaction_start(tdb)))
345                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
346         /* Append 1000 records. */
347         printf("Appending %u records: ", num); fflush(stdout);
348         gettimeofday(&start, NULL);
349         for (i = 0; i < num; i++)
350                 if ((ecode = tdb_append(tdb, key, data)) != TDB_SUCCESS)
351                         errx(1, "Appending key %u in tdb: %s",
352                              i, tdb_errorstr(ecode));
353         gettimeofday(&stop, NULL);
354         if (transaction && (ecode = tdb_transaction_commit(tdb)))
355                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
356         printf(" %zu ns (%zu bytes)\n",
357                normalize(&start, &stop, num), file_size());
358         if (summary) {
359                 char *sumstr = NULL;
360                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
361                 printf("%s\n", sumstr);
362                 free(sumstr);
363         }
364         if (++stage == stopat)
365                 exit(0);
366
367         if (transaction && (ecode = tdb_transaction_start(tdb)))
368                 errx(1, "starting transaction: %s", tdb_errorstr(ecode));
369
370         /* Churn 1000 records: not in order! */
371         printf("Churning %u records: ", num); fflush(stdout);
372         gettimeofday(&start, NULL);
373         for (j = 0; j < num; j++) {
374                 i = (j + 1000019) % num;
375                 if ((ecode = tdb_delete(tdb, key)) != TDB_SUCCESS)
376                         errx(1, "Deleting key %u in tdb: %s",
377                              i, tdb_errorstr(ecode));
378                 i += num;
379                 if ((ecode = tdb_store(tdb, key, data, TDB_INSERT)) != 0)
380                         errx(1, "Inserting key %u in tdb: %s",
381                              i, tdb_errorstr(ecode));
382         }
383         gettimeofday(&stop, NULL);
384         if (transaction && (ecode = tdb_transaction_commit(tdb)))
385                 errx(1, "committing transaction: %s", tdb_errorstr(ecode));
386         printf(" %zu ns (%zu bytes)\n",
387                normalize(&start, &stop, num), file_size());
388
389         if (summary) {
390                 char *sumstr = NULL;
391                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
392                 printf("%s\n", sumstr);
393                 free(sumstr);
394         }
395         if (seed.base.next)
396                 dump_and_clear_stats(&stats.stats);
397         if (++stage == stopat)
398                 exit(0);
399
400         return 0;
401 }