]> git.ozlabs.org Git - ccan/blob - ccan/tdb2/tools/speed.c
tdb2: cleanups for tools/speed.c
[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         putc('\n', stderr);
102 }
103
104 int main(int argc, char *argv[])
105 {
106         unsigned int i, j, num = 1000, stage = 0, stopat = -1;
107         int flags = TDB_DEFAULT;
108         bool transaction = false, summary = false;
109         TDB_DATA key, data;
110         struct tdb_context *tdb;
111         struct timeval start, stop;
112         union tdb_attribute seed, stats, log;
113         enum TDB_ERROR ecode;
114
115         /* Try to keep benchmarks even. */
116         seed.base.attr = TDB_ATTRIBUTE_SEED;
117         seed.base.next = NULL;
118         seed.seed.seed = 0;
119
120         log.base.attr = TDB_ATTRIBUTE_LOG;
121         log.base.next = &seed;
122         log.log.fn = tdb_log;
123
124         memset(&stats, 0, sizeof(stats));
125         stats.base.attr = TDB_ATTRIBUTE_STATS;
126         stats.base.next = NULL;
127         stats.stats.size = sizeof(stats);
128
129         if (argv[1] && strcmp(argv[1], "--internal") == 0) {
130                 flags = TDB_INTERNAL;
131                 argc--;
132                 argv++;
133         }
134         if (argv[1] && strcmp(argv[1], "--transaction") == 0) {
135                 transaction = true;
136                 argc--;
137                 argv++;
138         }
139         if (argv[1] && strcmp(argv[1], "--no-sync") == 0) {
140                 flags |= TDB_NOSYNC;
141                 argc--;
142                 argv++;
143         }
144         if (argv[1] && strcmp(argv[1], "--summary") == 0) {
145                 summary = true;
146                 argc--;
147                 argv++;
148         }
149         if (argv[1] && strcmp(argv[1], "--stats") == 0) {
150                 seed.base.next = &stats;
151                 argc--;
152                 argv++;
153         }
154
155         tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR|O_CREAT|O_TRUNC,
156                        0600, &log);
157         if (!tdb)
158                 err(1, "Opening /tmp/speed.tdb");
159
160         key.dptr = (void *)&i;
161         key.dsize = sizeof(i);
162         data = key;
163
164         if (argv[1]) {
165                 num = atoi(argv[1]);
166                 argv++;
167                 argc--;
168         }
169
170         if (argv[1]) {
171                 stopat = atoi(argv[1]);
172                 argv++;
173                 argc--;
174         }
175
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());
190
191         if (tdb_check(tdb, NULL, NULL))
192                 errx(1, "tdb_check failed!");
193         if (summary) {
194                 char *sumstr = NULL;
195                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
196                 printf("%s\n", sumstr);
197                 free(sumstr);
198         }
199         if (seed.base.next)
200                 dump_and_clear_stats(&stats.stats);
201
202         if (++stage == stopat)
203                 exit(0);
204
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);
216                 }
217         }
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!");
225         if (summary) {
226                 char *sumstr = NULL;
227                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
228                 printf("%s\n", sumstr);
229                 free(sumstr);
230         }
231         if (seed.base.next)
232                 dump_and_clear_stats(&stats.stats);
233         if (++stage == stopat)
234                 exit(0);
235
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));
247         }
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!");
255         if (summary) {
256                 char *sumstr = NULL;
257                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
258                 printf("%s\n", sumstr);
259                 free(sumstr);
260         }
261         if (seed.base.next)
262                 dump_and_clear_stats(&stats.stats);
263         if (++stage == stopat)
264                 exit(0);
265
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));
270         i = 0;
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!");
283         if (summary) {
284                 char *sumstr = NULL;
285                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
286                 printf("%s\n", sumstr);
287                 free(sumstr);
288         }
289         if (seed.base.next)
290                 dump_and_clear_stats(&stats.stats);
291         if (++stage == stopat)
292                 exit(0);
293
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));
304         }
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!");
312         if (summary) {
313                 char *sumstr = NULL;
314                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
315                 printf("%s\n", sumstr);
316                 free(sumstr);
317         }
318         if (seed.base.next)
319                 dump_and_clear_stats(&stats.stats);
320         if (++stage == stopat)
321                 exit(0);
322
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));
333         }
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!");
341         if (summary) {
342                 char *sumstr = NULL;
343                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
344                 printf("%s\n", sumstr);
345                 free(sumstr);
346         }
347         if (seed.base.next)
348                 dump_and_clear_stats(&stats.stats);
349         if (++stage == stopat)
350                 exit(0);
351
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!");
368         if (summary) {
369                 char *sumstr = NULL;
370                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
371                 printf("%s\n", sumstr);
372                 free(sumstr);
373         }
374         if (++stage == stopat)
375                 exit(0);
376
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));
387                 i += num;
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));
391         }
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
398         if (tdb_check(tdb, NULL, NULL))
399                 errx(1, "tdb_check failed!");
400         if (summary) {
401                 char *sumstr = NULL;
402                 tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &sumstr);
403                 printf("%s\n", sumstr);
404                 free(sumstr);
405         }
406         if (seed.base.next)
407                 dump_and_clear_stats(&stats.stats);
408         if (++stage == stopat)
409                 exit(0);
410
411         return 0;
412 }