1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3 #ident "$Id$"
4 /*======
5 This file is part of PerconaFT.
6 
7 
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9 
10     PerconaFT is free software: you can redistribute it and/or modify
11     it under the terms of the GNU General Public License, version 2,
12     as published by the Free Software Foundation.
13 
14     PerconaFT is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
21 
22 ----------------------------------------
23 
24     PerconaFT is free software: you can redistribute it and/or modify
25     it under the terms of the GNU Affero General Public License, version 3,
26     as published by the Free Software Foundation.
27 
28     PerconaFT is distributed in the hope that it will be useful,
29     but WITHOUT ANY WARRANTY; without even the implied warranty of
30     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31     GNU Affero General Public License for more details.
32 
33     You should have received a copy of the GNU Affero General Public License
34     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
35 ======= */
36 
37 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38 
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <sys/time.h>
42 #include "test.h"
43 
44 #ifndef MIN
45 #define MIN(x, y) (((x) < (y)) ? (x) : (y))
46 #endif
47 const double USECS_PER_SEC = 1000000.0;
48 
le_add_to_bn(bn_data * bn,uint32_t idx,char * key,int keylen,char * val,int vallen)49 static void le_add_to_bn(bn_data *bn,
50                          uint32_t idx,
51                          char *key,
52                          int keylen,
53                          char *val,
54                          int vallen) {
55     LEAFENTRY r = NULL;
56     uint32_t size_needed = LE_CLEAN_MEMSIZE(vallen);
57     void *maybe_free = nullptr;
58     bn->get_space_for_insert(idx, key, keylen, size_needed, &r, &maybe_free);
59     if (maybe_free) {
60         toku_free(maybe_free);
61     }
62     resource_assert(r);
63     r->type = LE_CLEAN;
64     r->u.clean.vallen = vallen;
65     memcpy(r->u.clean.val, val, vallen);
66 }
67 
long_key_cmp(DB * UU (e),const DBT * a,const DBT * b)68 static int long_key_cmp(DB *UU(e), const DBT *a, const DBT *b) {
69     const long *CAST_FROM_VOIDP(x, a->data);
70     const long *CAST_FROM_VOIDP(y, b->data);
71     return (*x > *y) - (*x < *y);
72 }
73 
test_serialize_leaf(int valsize,int nelts,double entropy,int ser_runs,int deser_runs)74 static void test_serialize_leaf(int valsize,
75                                 int nelts,
76                                 double entropy,
77                                 int ser_runs,
78                                 int deser_runs) {
79     //    struct ft_handle source_ft;
80     struct ftnode *sn, *dn;
81 
82     int fd = open(TOKU_TEST_FILENAME,
83                   O_RDWR | O_CREAT | O_BINARY,
84                   S_IRWXU | S_IRWXG | S_IRWXO);
85     invariant(fd >= 0);
86 
87     int r;
88 
89     XCALLOC(sn);
90 
91     sn->max_msn_applied_to_node_on_disk.msn = 0;
92     sn->flags = 0x11223344;
93     sn->blocknum.b = 20;
94     sn->layout_version = FT_LAYOUT_VERSION;
95     sn->layout_version_original = FT_LAYOUT_VERSION;
96     sn->height = 0;
97     sn->n_children = 8;
98     sn->set_dirty();
99     sn->oldest_referenced_xid_known = TXNID_NONE;
100     MALLOC_N(sn->n_children, sn->bp);
101     sn->pivotkeys.create_empty();
102     for (int i = 0; i < sn->n_children; ++i) {
103         BP_STATE(sn, i) = PT_AVAIL;
104         set_BLB(sn, i, toku_create_empty_bn());
105     }
106     int nperbn = nelts / sn->n_children;
107     for (int ck = 0; ck < sn->n_children; ++ck) {
108         long k;
109         for (long i = 0; i < nperbn; ++i) {
110             k = ck * nperbn + i;
111             char buf[valsize];
112             int c;
113             for (c = 0; c < valsize * entropy;) {
114                 int *p = (int *)&buf[c];
115                 *p = rand();
116                 c += sizeof(*p);
117             }
118             memset(&buf[c], 0, valsize - c);
119             le_add_to_bn(
120                 BLB_DATA(sn, ck), i, (char *)&k, sizeof k, buf, sizeof buf);
121         }
122         if (ck < 7) {
123             DBT pivotkey;
124             sn->pivotkeys.insert_at(toku_fill_dbt(&pivotkey, &k, sizeof(k)),
125                                     ck);
126         }
127     }
128 
129     FT_HANDLE XMALLOC(ft);
130     FT XCALLOC(ft_h);
131     toku_ft_init(ft_h,
132                  make_blocknum(0),
133                  ZERO_LSN,
134                  TXNID_NONE,
135                  4 * 1024 * 1024,
136                  128 * 1024,
137                  TOKU_DEFAULT_COMPRESSION_METHOD,
138                  16);
139     ft_h->cmp.create(long_key_cmp, nullptr);
140     ft->ft = ft_h;
141 
142     ft_h->blocktable.create();
143     {
144         int r_truncate = ftruncate(fd, 0);
145         CKERR(r_truncate);
146     }
147     // Want to use block #20
148     BLOCKNUM b = make_blocknum(0);
149     while (b.b < 20) {
150         ft_h->blocktable.allocate_blocknum(&b, ft_h);
151     }
152     invariant(b.b == 20);
153 
154     {
155         DISKOFF offset;
156         DISKOFF size;
157         ft_h->blocktable.realloc_on_disk(b, 100, &offset, ft_h, fd, false);
158         invariant(offset ==
159                (DISKOFF)BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE);
160 
161         ft_h->blocktable.translate_blocknum_to_offset_size(b, &offset, &size);
162         invariant(offset ==
163                (DISKOFF)BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE);
164         invariant(size == 100);
165     }
166 
167     struct timeval total_start;
168     struct timeval total_end;
169     total_start.tv_sec = total_start.tv_usec = 0;
170     total_end.tv_sec = total_end.tv_usec = 0;
171     struct timeval t[2];
172     FTNODE_DISK_DATA ndd = NULL;
173     for (int i = 0; i < ser_runs; i++) {
174         gettimeofday(&t[0], NULL);
175         ndd = NULL;
176         sn->set_dirty();
177         r = toku_serialize_ftnode_to(
178             fd, make_blocknum(20), sn, &ndd, true, ft->ft, false);
179         invariant(r == 0);
180         gettimeofday(&t[1], NULL);
181         total_start.tv_sec += t[0].tv_sec;
182         total_start.tv_usec += t[0].tv_usec;
183         total_end.tv_sec += t[1].tv_sec;
184         total_end.tv_usec += t[1].tv_usec;
185         toku_free(ndd);
186     }
187     double dt;
188     dt = (total_end.tv_sec - total_start.tv_sec) +
189          ((total_end.tv_usec - total_start.tv_usec) / USECS_PER_SEC);
190     dt *= 1000;
191     dt /= ser_runs;
192     printf(
193         "serialize leaf(ms):   %0.05lf (average of %d runs)\n", dt, ser_runs);
194 
195     // reset
196     total_start.tv_sec = total_start.tv_usec = 0;
197     total_end.tv_sec = total_end.tv_usec = 0;
198 
199     ftnode_fetch_extra bfe;
200     for (int i = 0; i < deser_runs; i++) {
201         bfe.create_for_full_read(ft_h);
202         gettimeofday(&t[0], NULL);
203         FTNODE_DISK_DATA ndd2 = NULL;
204         r = toku_deserialize_ftnode_from(
205             fd, make_blocknum(20), 0 /*pass zero for hash*/, &dn, &ndd2, &bfe);
206         invariant(r == 0);
207         gettimeofday(&t[1], NULL);
208 
209         total_start.tv_sec += t[0].tv_sec;
210         total_start.tv_usec += t[0].tv_usec;
211         total_end.tv_sec += t[1].tv_sec;
212         total_end.tv_usec += t[1].tv_usec;
213 
214         toku_ftnode_free(&dn);
215         toku_free(ndd2);
216     }
217     dt = (total_end.tv_sec - total_start.tv_sec) +
218          ((total_end.tv_usec - total_start.tv_usec) / USECS_PER_SEC);
219     dt *= 1000;
220     dt /= deser_runs;
221     printf(
222         "deserialize leaf(ms): %0.05lf (average of %d runs)\n", dt, deser_runs);
223     printf(
224         "io time(ms) %lf decompress time(ms) %lf deserialize time(ms) %lf "
225         "(average of %d runs)\n",
226         tokutime_to_seconds(bfe.io_time) * 1000,
227         tokutime_to_seconds(bfe.decompress_time) * 1000,
228         tokutime_to_seconds(bfe.deserialize_time) * 1000,
229         deser_runs);
230 
231     toku_ftnode_free(&sn);
232 
233     ft_h->blocktable.block_free(
234         BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE, 100);
235     ft_h->blocktable.destroy();
236     ft_h->cmp.destroy();
237     toku_free(ft_h->h);
238     toku_free(ft_h);
239     toku_free(ft);
240 
241     r = close(fd);
242     invariant(r != -1);
243 }
244 
test_serialize_nonleaf(int valsize,int nelts,double entropy,int ser_runs,int deser_runs)245 static void test_serialize_nonleaf(int valsize,
246                                    int nelts,
247                                    double entropy,
248                                    int ser_runs,
249                                    int deser_runs) {
250     //    struct ft_handle source_ft;
251     struct ftnode sn, *dn;
252 
253     int fd = open(TOKU_TEST_FILENAME,
254                   O_RDWR | O_CREAT | O_BINARY,
255                   S_IRWXU | S_IRWXG | S_IRWXO);
256     invariant(fd >= 0);
257 
258     int r;
259 
260     //    source_ft.fd=fd;
261     sn.max_msn_applied_to_node_on_disk.msn = 0;
262     sn.flags = 0x11223344;
263     sn.blocknum.b = 20;
264     sn.layout_version = FT_LAYOUT_VERSION;
265     sn.layout_version_original = FT_LAYOUT_VERSION;
266     sn.height = 1;
267     sn.n_children = 8;
268     sn.set_dirty();
269     sn.oldest_referenced_xid_known = TXNID_NONE;
270     MALLOC_N(sn.n_children, sn.bp);
271     sn.pivotkeys.create_empty();
272     for (int i = 0; i < sn.n_children; ++i) {
273         BP_BLOCKNUM(&sn, i).b = 30 + (i * 5);
274         BP_STATE(&sn, i) = PT_AVAIL;
275         set_BNC(&sn, i, toku_create_empty_nl());
276     }
277     // Create XIDS
278     XIDS xids_0 = toku_xids_get_root_xids();
279     XIDS xids_123;
280     r = toku_xids_create_child(xids_0, &xids_123, (TXNID)123);
281     CKERR(r);
282     toku::comparator cmp;
283     cmp.create(long_key_cmp, nullptr);
284     int nperchild = nelts / 8;
285     for (int ck = 0; ck < sn.n_children; ++ck) {
286         long k;
287         NONLEAF_CHILDINFO bnc = BNC(&sn, ck);
288         for (long i = 0; i < nperchild; ++i) {
289             k = ck * nperchild + i;
290             char buf[valsize];
291             int c;
292             for (c = 0; c < valsize * entropy;) {
293                 int *p = (int *)&buf[c];
294                 *p = rand();
295                 c += sizeof(*p);
296             }
297             memset(&buf[c], 0, valsize - c);
298 
299             toku_bnc_insert_msg(bnc,
300                                 &k,
301                                 sizeof k,
302                                 buf,
303                                 valsize,
304                                 FT_NONE,
305                                 next_dummymsn(),
306                                 xids_123,
307                                 true,
308                                 cmp);
309         }
310         if (ck < 7) {
311             DBT pivotkey;
312             sn.pivotkeys.insert_at(toku_fill_dbt(&pivotkey, &k, sizeof(k)), ck);
313         }
314     }
315 
316     // Cleanup:
317     toku_xids_destroy(&xids_0);
318     toku_xids_destroy(&xids_123);
319     cmp.destroy();
320 
321     FT_HANDLE XMALLOC(ft);
322     FT XCALLOC(ft_h);
323     toku_ft_init(ft_h,
324                  make_blocknum(0),
325                  ZERO_LSN,
326                  TXNID_NONE,
327                  4 * 1024 * 1024,
328                  128 * 1024,
329                  TOKU_DEFAULT_COMPRESSION_METHOD,
330                  16);
331     ft_h->cmp.create(long_key_cmp, nullptr);
332     ft->ft = ft_h;
333 
334     ft_h->blocktable.create();
335     {
336         int r_truncate = ftruncate(fd, 0);
337         CKERR(r_truncate);
338     }
339     // Want to use block #20
340     BLOCKNUM b = make_blocknum(0);
341     while (b.b < 20) {
342         ft_h->blocktable.allocate_blocknum(&b, ft_h);
343     }
344     invariant(b.b == 20);
345 
346     {
347         DISKOFF offset;
348         DISKOFF size;
349         ft_h->blocktable.realloc_on_disk(b, 100, &offset, ft_h, fd, false);
350         invariant(offset ==
351                (DISKOFF)BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE);
352 
353         ft_h->blocktable.translate_blocknum_to_offset_size(b, &offset, &size);
354         invariant(offset ==
355                (DISKOFF)BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE);
356         invariant(size == 100);
357     }
358 
359     struct timeval t[2];
360     gettimeofday(&t[0], NULL);
361     FTNODE_DISK_DATA ndd = NULL;
362     r = toku_serialize_ftnode_to(
363         fd, make_blocknum(20), &sn, &ndd, true, ft->ft, false);
364     invariant(r == 0);
365     gettimeofday(&t[1], NULL);
366     double dt;
367     dt = (t[1].tv_sec - t[0].tv_sec) +
368          ((t[1].tv_usec - t[0].tv_usec) / USECS_PER_SEC);
369     dt *= 1000;
370     printf(
371         "serialize nonleaf(ms):   %0.05lf (IGNORED RUNS=%d)\n", dt, ser_runs);
372 
373     ftnode_fetch_extra bfe;
374     bfe.create_for_full_read(ft_h);
375     gettimeofday(&t[0], NULL);
376     FTNODE_DISK_DATA ndd2 = NULL;
377     r = toku_deserialize_ftnode_from(
378         fd, make_blocknum(20), 0 /*pass zero for hash*/, &dn, &ndd2, &bfe);
379     invariant(r == 0);
380     gettimeofday(&t[1], NULL);
381     dt = (t[1].tv_sec - t[0].tv_sec) +
382          ((t[1].tv_usec - t[0].tv_usec) / USECS_PER_SEC);
383     dt *= 1000;
384     printf(
385         "deserialize nonleaf(ms): %0.05lf (IGNORED RUNS=%d)\n", dt, deser_runs);
386     printf(
387         "io time(ms) %lf decompress time(ms) %lf deserialize time(ms) %lf "
388         "(IGNORED RUNS=%d)\n",
389         tokutime_to_seconds(bfe.io_time) * 1000,
390         tokutime_to_seconds(bfe.decompress_time) * 1000,
391         tokutime_to_seconds(bfe.deserialize_time) * 1000,
392         deser_runs);
393 
394     toku_ftnode_free(&dn);
395     toku_destroy_ftnode_internals(&sn);
396 
397     ft_h->blocktable.block_free(
398         BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE, 100);
399     ft_h->blocktable.destroy();
400     toku_free(ft_h->h);
401     ft_h->cmp.destroy();
402     toku_free(ft_h);
403     toku_free(ft);
404     toku_free(ndd);
405     toku_free(ndd2);
406 
407     r = close(fd);
408     invariant(r != -1);
409 }
410 
test_main(int argc,const char * argv[])411 int test_main(int argc __attribute__((__unused__)),
412               const char *argv[] __attribute__((__unused__))) {
413     const int DEFAULT_RUNS = 5;
414     long valsize, nelts, ser_runs = DEFAULT_RUNS, deser_runs = DEFAULT_RUNS;
415     double entropy = 0.3;
416 
417     if (argc != 3 && argc != 5) {
418         fprintf(stderr,
419                 "Usage: %s <valsize> <nelts> [<serialize_runs> "
420                 "<deserialize_runs>]\n",
421                 argv[0]);
422         fprintf(stderr, "Default (and min) runs is %d\n", DEFAULT_RUNS);
423         return 2;
424     }
425     valsize = strtol(argv[1], NULL, 0);
426     nelts = strtol(argv[2], NULL, 0);
427     if (argc == 5) {
428         ser_runs = strtol(argv[3], NULL, 0);
429         deser_runs = strtol(argv[4], NULL, 0);
430     }
431 
432     if (ser_runs <= 0) {
433         ser_runs = DEFAULT_RUNS;
434     }
435     if (deser_runs <= 0) {
436         deser_runs = DEFAULT_RUNS;
437     }
438 
439     initialize_dummymsn();
440     test_serialize_leaf(valsize, nelts, entropy, ser_runs, deser_runs);
441     test_serialize_nonleaf(valsize, nelts, entropy, ser_runs, deser_runs);
442 
443     return 0;
444 }
445