4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.gnu.org/licenses/gpl-2.0.html
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Use is subject to license terms.
27 * This file is part of Lustre, http://www.lustre.org/
28 * Lustre is a trademark of Sun Microsystems, Inc.
30 * lustre/tests/it_test.c
32 * Unit test tool for interval tree.
34 * Author: jay <jxiong@clusterfs.com>
45 #include <libcfs/util/list.h>
47 #include <linux/types.h>
50 * it_test.c is built against one of the lustre kernel
51 * files (interval_tree.c). This pulls in kernel specific
52 * definitions which are not of interest for user land.
54 #define EXPORT_SYMBOL(s)
55 #define LASSERT assert
60 #include <../ldlm/interval_tree.c>
62 #define dprintf(fmt, args...) //printf(fmt, ##args)
63 #define error(fmt, args...) do { \
64 fflush(stdout), fflush(stderr); \
65 fprintf(stderr, "\nError:" fmt, ##args); \
69 #define ALIGN_SIZE 4096
70 #define ALIGN_MASK (~(ALIGN_SIZE - 1))
72 static struct it_node {
73 struct interval_node node;
74 struct list_head list;
78 static struct list_head header = LIST_HEAD_INIT(header);
79 static unsigned long max_count = ULONG_MAX & ALIGN_MASK;
80 static int have_wide_lock = 0;
82 static void it_test_clear(void)
85 for (i = 0; i < it_count; i++)
89 static enum interval_iter cb(struct interval_node *n, void *args)
91 struct it_node *node = (struct it_node *)n;
95 error("A duplicate node [%#jx:%#jx] access found\n",
96 (uintmax_t)n->in_extent.start,
97 (uintmax_t)n->in_extent.end);
98 return INTERVAL_ITER_CONT;
101 if (node->valid == 0) {
102 error("A deleted node [%#jx:%#jx] being accessed\n",
103 (uintmax_t)n->in_extent.start,
104 (uintmax_t)n->in_extent.end);
105 return INTERVAL_ITER_STOP;
112 dprintf("[%#jx:%#jx] ", (uintmax_t)n->in_extent.start,
113 (uintmax_t)n->in_extent.end);
117 return INTERVAL_ITER_CONT;
120 static int it_test_search(struct interval_node *root)
123 struct interval_node_extent ext;
124 int times = 10, i, err = 0;
128 ext.start = (random() % max_count) & ALIGN_MASK;
129 ext.end = random() % (max_count - ext.start + 2) + ext.start;
130 ext.end &= ALIGN_MASK;
131 if (ext.end > max_count)
134 dprintf("\n\nSearching the node overlapped [%#jx:%#jx] ..\n",
135 (uintmax_t)ext.start, (uintmax_t)ext.end);
137 interval_search(root, &ext, cb, NULL);
139 dprintf("\nverifing ...");
142 for (i = 0; i < it_count; i++) {
147 if (extent_overlapped(&ext, &n->node.in_extent) &&
149 error("node [%#jx:%#jx] overlaps [%#jx:%#jx],"
150 "but never to be hit.\n",
151 (uintmax_t)n->node.in_extent.start,
152 (uintmax_t)n->node.in_extent.end,
153 (uintmax_t)ext.start, (uintmax_t)ext.end);
155 if (!extent_overlapped(&ext, &n->node.in_extent) &&
157 error("node [%#jx:%#jx] overlaps [%#jx:%#jx], but hit.\n",
158 (uintmax_t)n->node.in_extent.start,
159 (uintmax_t)n->node.in_extent.end,
160 (uintmax_t)ext.start, (uintmax_t)ext.end);
162 if (err) error("search error\n");
169 static int it_test_iterate(struct interval_node *root)
173 dprintf("\n\nIterate testing start..\n");
176 interval_iterate(root, cb, NULL);
179 for (i = 0; i < it_count; i++) {
180 if (it_array[i].valid == 0)
182 if (it_array[i].hit == 0) {
183 error("Node [%#jx:%#jx] is not accessed\n",
184 (uintmax_t)it_array[i].node.in_extent.start,
185 (uintmax_t)it_array[i].node.in_extent.end);
191 static int it_test_iterate_reverse(struct interval_node *root)
195 dprintf("\n\niterate reverse testing start..\n");
197 interval_iterate_reverse(root, cb, NULL);
200 for (i = 0; i < it_count; i++) {
201 if (it_array[i].valid == 0)
203 if (it_array[i].hit == 0)
204 error("Not every extent is accessed\n");
210 static int it_test_find(struct interval_node *root)
213 struct interval_node_extent *ext;
215 dprintf("\ninterval_find testing start ..\n");
216 for (idx = 0; idx < it_count; idx++) {
217 if (it_array[idx].valid == 0)
220 ext = &it_array[idx].node.in_extent;
221 dprintf("Try to find [%#jx:%#jx]\n", (uintmax_t)ext->start,
222 (uintmax_t)ext->end);
223 if (!interval_find(root, ext)) {
224 error("interval_find, try to find [%#jx:%#jx]\n",
225 (uintmax_t)ext->start, (uintmax_t)ext->end);
231 /* sanity test is tightly coupled with implementation, so when you changed
232 * the interval tree implementation, change this code also. */
233 static enum interval_iter sanity_cb(struct interval_node *node, void *args)
235 __u64 max_high = node->in_max_high;
236 struct interval_node *tmp, *parent;
237 int left = 1, has = 0, nr = 0;
239 parent = node->in_parent;
240 node->in_parent = NULL;
241 interval_for_each(tmp, node) {
242 if ((left && node_compare(tmp, node) > 0) ||
243 (!left && node_compare(tmp, node) < 0))
244 error("interval tree sanity test\n");
246 if (tmp->in_max_high > max_high) {
247 dprintf("max high sanity check, max_high is %llu,"
248 "child max_high: %llu[%#jx:%#jx]\n",
249 max_high, tmp->in_max_high,
250 __F(&tmp->in_extent));
252 } else if (tmp->in_max_high == max_high) {
266 dprintf("node[%#jx:%#jx]:%llu Child list:\n",
267 node->in_extent.start,
271 interval_for_each(tmp, node) {
272 dprintf("[%#jx:%#jx]:%llu ",
273 __F(&tmp->in_extent),
281 error("max high sanity check, has == %d\n", has);
283 node->in_parent = parent;
287 if (node_is_black(tmp))
289 else if ((tmp->in_left && node_is_red(tmp->in_left)) ||
290 (tmp->in_right && node_is_red(tmp->in_right)))
291 error("wrong tree, a red node has red child\n");
297 if (node_is_black(tmp))
302 error("wrong tree, unbalanced!\n");
307 static int it_test_sanity(struct interval_node *root)
310 interval_iterate(root, sanity_cb, NULL);
314 static int it_test_search_hole(struct interval_node *root)
317 struct interval_node_extent ext, ext2;
319 __u64 low = 0, high = ~0;
325 ext.start = random() % max_count;
327 } while (interval_is_overlapped(root, &ext));
330 interval_expand(root, &ext, NULL);
331 dprintf("Extending [%#jx:%#jx] to ..[%#jx:%#jx]\n",
332 (uintmax_t)ext2.start, (uintmax_t)ext2.end,
333 (uintmax_t)ext.start, (uintmax_t)ext.end);
334 for (i = 0; i < it_count; i++) {
339 if (extent_overlapped(&ext, &n->node.in_extent)) {
340 error("Extending [%#jx:%#jx] to ..[%#jx:%#jx] overlaps node[%#jx:%#jx]\n",
341 (uintmax_t)ext2.start, (uintmax_t)ext2.end,
342 (uintmax_t)ext.start, (uintmax_t)ext.end,
343 (uintmax_t)n->node.in_extent.start,
344 (uintmax_t)n->node.in_extent.end);
347 if (n->node.in_extent.end < ext2.start)
348 low = max_u64(n->node.in_extent.end + 1, low);
350 if (n->node.in_extent.start > ext2.end)
351 high = min_u64(n->node.in_extent.start - 1, high);
354 /* only expanding high right now */
355 if (ext2.start != ext.start || high != ext.end) {
356 ext2.start = low, ext2.end = high;
357 error("Real extending result:[%#jx:%#jx], expected:[%#jx:%#jx]\n",
358 (uintmax_t)ext.start, (uintmax_t)ext.end,
359 (uintmax_t)ext2.start, (uintmax_t)ext2.end);
365 static int contended_count = 0;
366 #define LOOP_COUNT 1000
367 static enum interval_iter perf_cb(struct interval_node *n, void *args)
369 unsigned long count = LOOP_COUNT;
372 return INTERVAL_ITER_CONT;
375 static inline long tv_delta(struct timeval *s, struct timeval *e)
377 long c = e->tv_sec - s->tv_sec;
379 c += (long int)(e->tv_usec - s->tv_usec) / 1000;
380 dprintf("\tStart: %lu:%lu -> End: %lu:%lu\n",
381 s->tv_sec, s->tv_usec, e->tv_sec, e->tv_usec);
385 static int it_test_performance(struct interval_node *root, unsigned long len)
387 int i = 0, interval_time, list_time;
388 struct interval_node_extent ext;
390 struct timeval start, end;
393 ext.start = (random() % (max_count - len)) & ALIGN_MASK;
394 ext.end = (ext.start + len) & ALIGN_MASK;
395 if (have_wide_lock) {
396 ext.start = (max_count - len) & ALIGN_MASK;
400 dprintf("Extent search[%#jx:%#jx]\n", (uintmax_t)ext.start,
405 gettimeofday(&start, NULL);
406 list_for_each_entry(n, &header, list) {
407 if (extent_overlapped(&ext, &n->node.in_extent)) {
413 gettimeofday(&end, NULL);
414 list_time = tv_delta(&start, &end);
419 gettimeofday(&start, NULL);
420 interval_search(root, &ext, perf_cb, &contended_count);
421 gettimeofday(&end, NULL);
422 interval_time = tv_delta(&start, &end);
424 if (i != contended_count)
425 error("count of contended lock don't match(%d: %d)\n",
428 printf("\tList vs Int. search: \n\t\t"
429 "(%d vs %d)ms, %d contended lock.\n",
430 list_time, interval_time, contended_count);
435 static struct interval_node *it_test_helper(struct interval_node *root)
440 count = random() % it_count;
442 idx = random() % it_count;
445 if (!interval_find(root, &n->node.in_extent))
446 error("Cannot find an existent node\n");
447 dprintf("Erasing a node [%#jx:%#jx]\n",
448 (uintmax_t)n->node.in_extent.start,
449 (uintmax_t)n->node.in_extent.end);
450 interval_erase(&n->node, &root);
452 list_del_init(&n->list);
455 low = (random() % max_count) & ALIGN_MASK;
456 high = ((random() % max_count + 1) & ALIGN_MASK) + low;
457 if (high > max_count)
459 interval_set(&n->node, low, high);
460 while (interval_insert(&n->node, &root))
461 interval_set(&n->node, low, ++high);
462 dprintf("Adding a node [%#jx:%#jx]\n",
463 (uintmax_t)n->node.in_extent.start,
464 (uintmax_t)n->node.in_extent.end);
466 list_add(&n->list, &header);
473 static struct interval_node *it_test_init(int count)
476 uint64_t high, low, len;
478 struct interval_node *root = NULL;
481 it_array = (struct it_node *)malloc(sizeof(struct it_node) * count);
482 if (it_array == NULL)
483 error("it_array == NULL, no memory\n");
486 for (i = 0; i < count; i++) {
489 low = (random() % max_count + 1) & ALIGN_MASK;
490 len = (random() % 256 + 1) * ALIGN_SIZE;
491 if (!have_wide_lock && !(random() % count)) {
496 high = low + (len & ALIGN_MASK);
498 interval_set(&n->node, low, high);
499 } while (interval_insert(&n->node, &root));
503 list_add_tail(&n->list, &header);
505 list_add_tail(&n->list, &it_array[rand()%i].list);
511 static void it_test_fini(void)
519 int main(int argc, char *argv[])
521 int count = 5, perf = 0;
522 struct interval_node *root;
525 gettimeofday(&tv, NULL);
529 if (strcmp(argv[1], "-p"))
530 error("Unknow options, usage: %s [-p]\n", argv[0]);
537 root = it_test_init(1000000);
538 printf("1M locks with 4K request size\n");
539 it_test_performance(root, 4096);
540 printf("1M locks with 128K request size\n");
541 it_test_performance(root, 128 * 1024);
542 printf("1M locks with 256K request size\n");
543 it_test_performance(root, 256 * 1024);
544 printf("1M locks with 1M request size\n");
545 it_test_performance(root, 1 * M);
546 printf("1M locks with 16M request size\n");
547 it_test_performance(root, 16 * M);
548 printf("1M locks with 32M request size\n");
549 it_test_performance(root, 32 * M);
550 printf("1M locks with 64M request size\n");
551 it_test_performance(root, 64 * M);
552 printf("1M locks with 128M request size\n");
553 it_test_performance(root, 128 * M);
554 printf("1M locks with 256M request size\n");
555 it_test_performance(root, 256 * M);
556 printf("1M locks with 512M request size\n");
557 it_test_performance(root, 512 * M);
558 printf("1M locks with 1G request size\n");
559 it_test_performance(root, 1024 * M);
560 printf("1M locks with 2G request size\n");
561 it_test_performance(root, 2048 * M);
562 printf("1M locks with 3G request size\n");
563 it_test_performance(root, 3072 * M);
564 printf("1M locks with 4G request size\n");
565 it_test_performance(root, max_count - 1);
570 root = it_test_init(random() % 100000 + 1000);
572 it_test_sanity(root);
573 it_test_iterate(root);
574 it_test_iterate_reverse(root);
576 it_test_search_hole(root);
577 it_test_search(root);
578 root = it_test_helper(root);