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