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