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