tdb2: add write flag to tdb_direct
[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 int main(int argc, char *argv[])
98 {
99         unsigned int i, j, num = 1000, stage = 0, stopat = -1;
100         int flags = TDB_DEFAULT;
101         bool transaction = false;
102         TDB_DATA key, data;
103         struct tdb_context *tdb;
104         struct timeval start, stop;
105         union tdb_attribute seed, stats;
106
107         /* Try to keep benchmarks even. */
108         seed.base.attr = TDB_ATTRIBUTE_SEED;
109         seed.base.next = NULL;
110         seed.seed.seed = 0;
111
112         memset(&stats, 0, sizeof(stats));
113         stats.base.attr = TDB_ATTRIBUTE_STATS;
114         stats.base.next = NULL;
115         stats.stats.size = sizeof(stats);
116
117         if (argv[1] && strcmp(argv[1], "--internal") == 0) {
118                 flags = TDB_INTERNAL;
119                 argc--;
120                 argv++;
121         }
122         if (argv[1] && strcmp(argv[1], "--transaction") == 0) {
123                 transaction = true;
124                 argc--;
125                 argv++;
126         }
127         if (argv[1] && strcmp(argv[1], "--stats") == 0) {
128                 seed.base.next = &stats;
129                 argc--;
130                 argv++;
131         }
132
133         tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR|O_CREAT|O_TRUNC,
134                        0600, &seed);
135         if (!tdb)
136                 err(1, "Opening /tmp/speed.tdb");
137
138         key.dptr = (void *)&i;
139         key.dsize = sizeof(i);
140         data = key;
141
142         if (argv[1]) {
143                 num = atoi(argv[1]);
144                 argv++;
145                 argc--;
146         }
147
148         if (argv[1]) {
149                 stopat = atoi(argv[1]);
150                 argv++;
151                 argc--;
152         }
153
154         if (transaction && tdb_transaction_start(tdb))
155                 errx(1, "starting transaction: %s", tdb_errorstr(tdb));
156
157         /* Add 1000 records. */
158         printf("Adding %u records: ", num); fflush(stdout);
159         gettimeofday(&start, NULL);
160         for (i = 0; i < num; i++)
161                 if (tdb_store(tdb, key, data, TDB_INSERT) != 0)
162                         errx(1, "Inserting key %u in tdb: %s",
163                              i, tdb_errorstr(tdb));
164         gettimeofday(&stop, NULL);
165         if (transaction && tdb_transaction_commit(tdb))
166                 errx(1, "committing transaction: %s", tdb_errorstr(tdb));
167         printf(" %zu ns (%zu bytes)\n",
168                normalize(&start, &stop, num), file_size());
169
170         if (seed.base.next)
171                 dump_and_clear_stats(&stats.stats);
172         if (++stage == stopat)
173                 exit(0);
174
175         if (transaction && tdb_transaction_start(tdb))
176                 errx(1, "starting transaction: %s", tdb_errorstr(tdb));
177
178         /* Finding 1000 records. */
179         printf("Finding %u records: ", num); fflush(stdout);
180         gettimeofday(&start, NULL);
181         for (i = 0; i < num; i++) {
182                 int *dptr;
183                 dptr = (int *)tdb_fetch(tdb, key).dptr;
184                 if (!dptr || *dptr != i)
185                         errx(1, "Fetching key %u in tdb gave %u",
186                              i, dptr ? *dptr : -1);
187         }
188         gettimeofday(&stop, NULL);
189         if (transaction && tdb_transaction_commit(tdb))
190                 errx(1, "committing transaction: %s", tdb_errorstr(tdb));
191         printf(" %zu ns (%zu bytes)\n",
192                normalize(&start, &stop, num), file_size());
193         if (seed.base.next)
194                 dump_and_clear_stats(&stats.stats);
195         if (++stage == stopat)
196                 exit(0);
197
198         if (transaction && tdb_transaction_start(tdb))
199                 errx(1, "starting transaction: %s", tdb_errorstr(tdb));
200
201         /* Missing 1000 records. */
202         printf("Missing %u records: ", num); fflush(stdout);
203         gettimeofday(&start, NULL);
204         for (i = num; i < num*2; i++) {
205                 int *dptr;
206                 dptr = (int *)tdb_fetch(tdb, key).dptr;
207                 if (dptr)
208                         errx(1, "Fetching key %u in tdb gave %u", i, *dptr);
209         }
210         gettimeofday(&stop, NULL);
211         if (transaction && tdb_transaction_commit(tdb))
212                 errx(1, "committing transaction: %s", tdb_errorstr(tdb));
213         printf(" %zu ns (%zu bytes)\n",
214                normalize(&start, &stop, num), file_size());
215         if (seed.base.next)
216                 dump_and_clear_stats(&stats.stats);
217         if (++stage == stopat)
218                 exit(0);
219
220         if (transaction && tdb_transaction_start(tdb))
221                 errx(1, "starting transaction: %s", tdb_errorstr(tdb));
222
223         /* Traverse 1000 records. */
224         printf("Traversing %u records: ", num); fflush(stdout);
225         i = 0;
226         gettimeofday(&start, NULL);
227         if (tdb_traverse(tdb, count_record, &i) != num)
228                 errx(1, "Traverse returned wrong number of records");
229         if (i != (num - 1) * (num / 2))
230                 errx(1, "Traverse tallied to %u", i);
231         gettimeofday(&stop, NULL);
232         if (transaction && tdb_transaction_commit(tdb))
233                 errx(1, "committing transaction: %s", tdb_errorstr(tdb));
234         printf(" %zu ns (%zu bytes)\n",
235                normalize(&start, &stop, num), file_size());
236         if (seed.base.next)
237                 dump_and_clear_stats(&stats.stats);
238         if (++stage == stopat)
239                 exit(0);
240
241         if (transaction && tdb_transaction_start(tdb))
242                 errx(1, "starting transaction: %s", tdb_errorstr(tdb));
243
244         /* Delete 1000 records (not in order). */
245         printf("Deleting %u records: ", num); fflush(stdout);
246         gettimeofday(&start, NULL);
247         for (j = 0; j < num; j++) {
248                 i = (j + 100003) % num;
249                 if (tdb_delete(tdb, key) != 0)
250                         errx(1, "Deleting key %u in tdb: %s",
251                              i, tdb_errorstr(tdb));
252         }
253         gettimeofday(&stop, NULL);
254         if (transaction && tdb_transaction_commit(tdb))
255                 errx(1, "committing transaction: %s", tdb_errorstr(tdb));
256         printf(" %zu ns (%zu bytes)\n",
257                normalize(&start, &stop, num), file_size());
258         if (seed.base.next)
259                 dump_and_clear_stats(&stats.stats);
260         if (++stage == stopat)
261                 exit(0);
262
263         if (transaction && tdb_transaction_start(tdb))
264                 errx(1, "starting transaction: %s", tdb_errorstr(tdb));
265
266         /* Re-add 1000 records (not in order). */
267         printf("Re-adding %u records: ", num); fflush(stdout);
268         gettimeofday(&start, NULL);
269         for (j = 0; j < num; j++) {
270                 i = (j + 100003) % num;
271                 if (tdb_store(tdb, key, data, TDB_INSERT) != 0)
272                         errx(1, "Inserting key %u in tdb: %s",
273                              i, tdb_errorstr(tdb));
274         }
275         gettimeofday(&stop, NULL);
276         if (transaction && tdb_transaction_commit(tdb))
277                 errx(1, "committing transaction: %s", tdb_errorstr(tdb));
278         printf(" %zu ns (%zu bytes)\n",
279                normalize(&start, &stop, num), file_size());
280         if (seed.base.next)
281                 dump_and_clear_stats(&stats.stats);
282         if (++stage == stopat)
283                 exit(0);
284
285         if (transaction && tdb_transaction_start(tdb))
286                 errx(1, "starting transaction: %s", tdb_errorstr(tdb));
287
288         /* Append 1000 records. */
289         printf("Appending %u records: ", num); fflush(stdout);
290         gettimeofday(&start, NULL);
291         for (i = 0; i < num; i++)
292                 if (tdb_append(tdb, key, data) != 0)
293                         errx(1, "Appending key %u in tdb: %s",
294                              i, tdb_errorstr(tdb));
295         gettimeofday(&stop, NULL);
296         if (transaction && tdb_transaction_commit(tdb))
297                 errx(1, "committing transaction: %s", tdb_errorstr(tdb));
298         printf(" %zu ns (%zu bytes)\n",
299                normalize(&start, &stop, num), file_size());
300         if (++stage == stopat)
301                 exit(0);
302
303         if (transaction && tdb_transaction_start(tdb))
304                 errx(1, "starting transaction: %s", tdb_errorstr(tdb));
305
306         /* Churn 1000 records: not in order! */
307         printf("Churning %u records: ", num); fflush(stdout);
308         gettimeofday(&start, NULL);
309         for (j = 0; j < num; j++) {
310                 i = (j + 1000019) % num;
311                 if (tdb_delete(tdb, key) != 0)
312                         errx(1, "Deleting key %u in tdb: %s",
313                              i, tdb_errorstr(tdb));
314                 i += num;
315                 if (tdb_store(tdb, key, data, TDB_INSERT) != 0)
316                         errx(1, "Inserting key %u in tdb: %s",
317                              i, tdb_errorstr(tdb));
318         }
319         gettimeofday(&stop, NULL);
320         if (transaction && tdb_transaction_commit(tdb))
321                 errx(1, "committing transaction: %s", tdb_errorstr(tdb));
322         printf(" %zu ns (%zu bytes)\n",
323                normalize(&start, &stop, num), file_size());
324
325         if (seed.base.next)
326                 dump_and_clear_stats(&stats.stats);
327         if (++stage == stopat)
328                 exit(0);
329
330         return 0;
331 }