diff options
author | Thomas Graf <tgraf@suug.ch> | 2015-04-30 18:37:41 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-05-03 23:08:53 -0400 |
commit | 1aa661f5c3df15432530f01f1023d556fa81b95d (patch) | |
tree | 55a7bdd26921f3c58159358c0fdea130721c82d6 /lib | |
parent | f54e84b6e9f07a93a5f27f55bf28982c06f45109 (diff) |
rhashtable-test: Measure time to insert, remove & traverse entries
Make test configurable by allowing to specify all relevant knobs
through module parameters.
Do several test runs and measure the average time it takes to
insert & remove all entries. Note, a deferred resize might still
continue to run in the background.
Signed-off-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/test_rhashtable.c | 100 |
1 files changed, 69 insertions, 31 deletions
diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c index c60fd5d9eb6b..e3d31bf527a9 100644 --- a/lib/test_rhashtable.c +++ b/lib/test_rhashtable.c | |||
@@ -1,14 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * Resizable, Scalable, Concurrent Hash Table | 2 | * Resizable, Scalable, Concurrent Hash Table |
3 | * | 3 | * |
4 | * Copyright (c) 2014 Thomas Graf <tgraf@suug.ch> | 4 | * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch> |
5 | * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net> | 5 | * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net> |
6 | * | 6 | * |
7 | * Based on the following paper: | ||
8 | * https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf | ||
9 | * | ||
10 | * Code partially derived from nft_hash | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
13 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
14 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
@@ -27,9 +22,28 @@ | |||
27 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
28 | 23 | ||
29 | 24 | ||
30 | #define TEST_HT_SIZE 8 | ||
31 | #define TEST_ENTRIES 2048 | ||
32 | #define TEST_PTR ((void *) 0xdeadbeef) | 25 | #define TEST_PTR ((void *) 0xdeadbeef) |
26 | #define MAX_ENTRIES 1000000 | ||
27 | |||
28 | static int entries = 50000; | ||
29 | module_param(entries, int, 0); | ||
30 | MODULE_PARM_DESC(entries, "Number of entries to add (default: 50000)"); | ||
31 | |||
32 | static int runs = 4; | ||
33 | module_param(runs, int, 0); | ||
34 | MODULE_PARM_DESC(runs, "Number of test runs per variant (default: 4)"); | ||
35 | |||
36 | static int max_size = 65536; | ||
37 | module_param(max_size, int, 0); | ||
38 | MODULE_PARM_DESC(runs, "Maximum table size (default: 65536)"); | ||
39 | |||
40 | static bool shrinking = false; | ||
41 | module_param(shrinking, bool, 0); | ||
42 | MODULE_PARM_DESC(shrinking, "Enable automatic shrinking (default: off)"); | ||
43 | |||
44 | static int size = 8; | ||
45 | module_param(size, int, 0); | ||
46 | MODULE_PARM_DESC(size, "Initial size hint of table (default: 8)"); | ||
33 | 47 | ||
34 | struct test_obj { | 48 | struct test_obj { |
35 | void *ptr; | 49 | void *ptr; |
@@ -37,8 +51,7 @@ struct test_obj { | |||
37 | struct rhash_head node; | 51 | struct rhash_head node; |
38 | }; | 52 | }; |
39 | 53 | ||
40 | static const struct rhashtable_params test_rht_params = { | 54 | static struct rhashtable_params test_rht_params = { |
41 | .nelem_hint = TEST_HT_SIZE, | ||
42 | .head_offset = offsetof(struct test_obj, node), | 55 | .head_offset = offsetof(struct test_obj, node), |
43 | .key_offset = offsetof(struct test_obj, value), | 56 | .key_offset = offsetof(struct test_obj, value), |
44 | .key_len = sizeof(int), | 57 | .key_len = sizeof(int), |
@@ -50,7 +63,7 @@ static int __init test_rht_lookup(struct rhashtable *ht) | |||
50 | { | 63 | { |
51 | unsigned int i; | 64 | unsigned int i; |
52 | 65 | ||
53 | for (i = 0; i < TEST_ENTRIES * 2; i++) { | 66 | for (i = 0; i < entries * 2; i++) { |
54 | struct test_obj *obj; | 67 | struct test_obj *obj; |
55 | bool expected = !(i % 2); | 68 | bool expected = !(i % 2); |
56 | u32 key = i; | 69 | u32 key = i; |
@@ -110,26 +123,28 @@ static void test_bucket_stats(struct rhashtable *ht, bool quiet) | |||
110 | } | 123 | } |
111 | 124 | ||
112 | pr_info(" Traversal complete: counted=%u, nelems=%u, entries=%d\n", | 125 | pr_info(" Traversal complete: counted=%u, nelems=%u, entries=%d\n", |
113 | total, atomic_read(&ht->nelems), TEST_ENTRIES); | 126 | total, atomic_read(&ht->nelems), entries); |
114 | 127 | ||
115 | if (total != atomic_read(&ht->nelems) || total != TEST_ENTRIES) | 128 | if (total != atomic_read(&ht->nelems) || total != entries) |
116 | pr_warn("Test failed: Total count mismatch ^^^"); | 129 | pr_warn("Test failed: Total count mismatch ^^^"); |
117 | } | 130 | } |
118 | 131 | ||
119 | static int __init test_rhashtable(struct rhashtable *ht) | 132 | static s64 __init test_rhashtable(struct rhashtable *ht) |
120 | { | 133 | { |
121 | struct bucket_table *tbl; | 134 | struct bucket_table *tbl; |
122 | struct test_obj *obj; | 135 | struct test_obj *obj; |
123 | struct rhash_head *pos, *next; | 136 | struct rhash_head *pos, *next; |
124 | int err; | 137 | int err; |
125 | unsigned int i; | 138 | unsigned int i; |
139 | s64 start, end; | ||
126 | 140 | ||
127 | /* | 141 | /* |
128 | * Insertion Test: | 142 | * Insertion Test: |
129 | * Insert TEST_ENTRIES into table with all keys even numbers | 143 | * Insert entries into table with all keys even numbers |
130 | */ | 144 | */ |
131 | pr_info(" Adding %d keys\n", TEST_ENTRIES); | 145 | pr_info(" Adding %d keys\n", entries); |
132 | for (i = 0; i < TEST_ENTRIES; i++) { | 146 | start = ktime_get_ns(); |
147 | for (i = 0; i < entries; i++) { | ||
133 | struct test_obj *obj; | 148 | struct test_obj *obj; |
134 | 149 | ||
135 | obj = kzalloc(sizeof(*obj), GFP_KERNEL); | 150 | obj = kzalloc(sizeof(*obj), GFP_KERNEL); |
@@ -157,8 +172,8 @@ static int __init test_rhashtable(struct rhashtable *ht) | |||
157 | test_bucket_stats(ht, true); | 172 | test_bucket_stats(ht, true); |
158 | rcu_read_unlock(); | 173 | rcu_read_unlock(); |
159 | 174 | ||
160 | pr_info(" Deleting %d keys\n", TEST_ENTRIES); | 175 | pr_info(" Deleting %d keys\n", entries); |
161 | for (i = 0; i < TEST_ENTRIES; i++) { | 176 | for (i = 0; i < entries; i++) { |
162 | u32 key = i * 2; | 177 | u32 key = i * 2; |
163 | 178 | ||
164 | obj = rhashtable_lookup_fast(ht, &key, test_rht_params); | 179 | obj = rhashtable_lookup_fast(ht, &key, test_rht_params); |
@@ -168,7 +183,10 @@ static int __init test_rhashtable(struct rhashtable *ht) | |||
168 | kfree(obj); | 183 | kfree(obj); |
169 | } | 184 | } |
170 | 185 | ||
171 | return 0; | 186 | end = ktime_get_ns(); |
187 | pr_info(" Duration of test: %lld ns\n", end - start); | ||
188 | |||
189 | return end - start; | ||
172 | 190 | ||
173 | error: | 191 | error: |
174 | tbl = rht_dereference_rcu(ht->tbl, ht); | 192 | tbl = rht_dereference_rcu(ht->tbl, ht); |
@@ -183,22 +201,42 @@ static struct rhashtable ht; | |||
183 | 201 | ||
184 | static int __init test_rht_init(void) | 202 | static int __init test_rht_init(void) |
185 | { | 203 | { |
186 | int err; | 204 | int i, err; |
205 | u64 total_time = 0; | ||
187 | 206 | ||
188 | pr_info("Running resizable hashtable tests...\n"); | 207 | entries = min(entries, MAX_ENTRIES); |
189 | 208 | ||
190 | err = rhashtable_init(&ht, &test_rht_params); | 209 | test_rht_params.automatic_shrinking = shrinking; |
191 | if (err < 0) { | 210 | test_rht_params.max_size = max_size; |
192 | pr_warn("Test failed: Unable to initialize hashtable: %d\n", | 211 | test_rht_params.nelem_hint = size; |
193 | err); | ||
194 | return err; | ||
195 | } | ||
196 | 212 | ||
197 | err = test_rhashtable(&ht); | 213 | pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n", |
214 | size, max_size, shrinking); | ||
198 | 215 | ||
199 | rhashtable_destroy(&ht); | 216 | for (i = 0; i < runs; i++) { |
217 | s64 time; | ||
200 | 218 | ||
201 | return err; | 219 | pr_info("Test %02d:\n", i); |
220 | err = rhashtable_init(&ht, &test_rht_params); | ||
221 | if (err < 0) { | ||
222 | pr_warn("Test failed: Unable to initialize hashtable: %d\n", | ||
223 | err); | ||
224 | continue; | ||
225 | } | ||
226 | |||
227 | time = test_rhashtable(&ht); | ||
228 | rhashtable_destroy(&ht); | ||
229 | if (time < 0) { | ||
230 | pr_warn("Test failed: return code %lld\n", time); | ||
231 | return -EINVAL; | ||
232 | } | ||
233 | |||
234 | total_time += time; | ||
235 | } | ||
236 | |||
237 | pr_info("Average test time: %llu\n", total_time / runs); | ||
238 | |||
239 | return 0; | ||
202 | } | 240 | } |
203 | 241 | ||
204 | static void __exit test_rht_exit(void) | 242 | static void __exit test_rht_exit(void) |