]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/test_mempool.cc
99b87d11f265ae85b99e32bbff27e5ded7fc24f1
[ceph.git] / ceph / src / test / test_mempool.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph distributed storage system
5 *
6 * Copyright (C) 2016 Western Digital Corporation
7 *
8 * Author: Allen Samuels <allen.samuels@sandisk.com>
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 */
16
17 #include <stdio.h>
18
19 #include "global/global_init.h"
20 #include "common/ceph_argparse.h"
21 #include "global/global_context.h"
22 #include "gtest/gtest.h"
23 #include "include/btree_map.h"
24 #include "include/mempool.h"
25
26 void check_usage(mempool::pool_index_t ix)
27 {
28 mempool::pool_t *pool = &mempool::get_pool(ix);
29 mempool::stats_t total;
30 map<std::string,mempool::stats_t> m;
31 pool->get_stats(&total, &m);
32 size_t usage = pool->allocated_bytes();
33 size_t sum = 0;
34 for (auto& p : m) {
35 sum += p.second.bytes;
36 }
37 if (sum != usage) {
38 ceph::TableFormatter jf;
39 pool->dump(&jf);
40 jf.flush(std::cout);
41 }
42 EXPECT_EQ(sum, usage);
43 }
44
45 template<typename A, typename B>
46 void eq_elements(const A& a, const B& b)
47 {
48 auto lhs = a.begin();
49 auto rhs = b.begin();
50 while (lhs != a.end()) {
51 EXPECT_EQ(*lhs,*rhs);
52 lhs++;
53 rhs++;
54 }
55 EXPECT_EQ(rhs,b.end());
56 }
57
58 template<typename A, typename B>
59 void eq_pairs(const A& a, const B& b)
60 {
61 auto lhs = a.begin();
62 auto rhs = b.begin();
63 while (lhs != a.end()) {
64 EXPECT_EQ(lhs->first,rhs->first);
65 EXPECT_EQ(lhs->second,rhs->second);
66 lhs++;
67 rhs++;
68 }
69 EXPECT_EQ(rhs,b.end());
70 }
71
72 #define MAKE_INSERTER(inserter) \
73 template<typename A,typename B> \
74 void do_##inserter(A& a, B& b, int count, int base) { \
75 for (int i = 0; i < count; ++i) { \
76 a.inserter(base + i); \
77 b.inserter(base + i); \
78 } \
79 }
80
81 MAKE_INSERTER(push_back);
82 MAKE_INSERTER(insert);
83
84 template<typename A,typename B>
85 void do_insert_key(A& a, B& b, int count, int base)
86 {
87 for (int i = 0; i < count; ++i) {
88 a.insert(make_pair(base+i,base+i));
89 b.insert(make_pair(base+i,base+i));
90 check_usage(mempool::osd::id);
91 }
92 }
93
94 TEST(mempool, vector_context)
95 {
96 check_usage(mempool::osd::id);
97 EXPECT_EQ(mempool::osd::allocated_bytes(), 0u);
98 EXPECT_EQ(mempool::osd::allocated_items(), 0u);
99 for (unsigned i = 0; i < 10; ++i) {
100 vector<int> a;
101 mempool::osd::vector<int> b,c;
102 eq_elements(a,b);
103 do_push_back(a,b,i,i);
104 eq_elements(a,b);
105 check_usage(mempool::osd::id);
106
107 mempool::stats_t total;
108 map<std::string,mempool::stats_t> by_type;
109 mempool::get_pool(mempool::osd::id).get_stats(&total, &by_type);
110 EXPECT_GE(mempool::osd::allocated_bytes(), i * 4u);
111 EXPECT_GE(mempool::osd::allocated_items(), i);
112
113 c.swap(b);
114 eq_elements(a,c);
115 check_usage(mempool::osd::id);
116 a.clear();
117 b.clear();
118 c.clear();
119 }
120 }
121
122 TEST(mempool, list_context)
123 {
124 for (unsigned i = 1; i < 10; ++i) {
125 list<int> a;
126 mempool::osd::list<int> b,c;
127 eq_elements(a,b);
128 do_push_back(a,b,i,i);
129 eq_elements(a,b);
130 c.swap(b);
131 eq_elements(a,c);
132 a.erase(a.begin());
133 c.erase(c.begin());
134 eq_elements(a,c);
135 a.clear();
136 b.clear();
137 c.clear();
138 do_push_back(a,b,i,i);
139 c.splice(c.begin(),b,b.begin(),b.end());
140
141 mempool::stats_t total;
142 map<std::string,mempool::stats_t> by_type;
143 mempool::get_pool(mempool::osd::id).get_stats(&total, &by_type);
144 EXPECT_GE(mempool::osd::allocated_bytes(), i * 4u);
145 EXPECT_EQ(mempool::osd::allocated_items(), i);
146
147 eq_elements(a,c);
148 check_usage(mempool::osd::id);
149 }
150 }
151
152 TEST(mempool, set_context)
153 {
154 for (int i = 0; i < 10; ++i) {
155 set<int> a;
156 mempool::osd::set<int> b;
157 do_insert(a,b,i,i);
158 eq_elements(a,b);
159 check_usage(mempool::osd::id);
160 }
161
162 for (int i = 1; i < 10; ++i) {
163 set<int> a;
164 mempool::osd::set<int> b;
165 do_insert(a,b,i,0);
166 EXPECT_NE(a.find(i/2),a.end());
167 EXPECT_NE(b.find(i/2),b.end());
168 a.erase(a.find(i/2));
169 b.erase(b.find(i/2));
170 eq_elements(a,b);
171 check_usage(mempool::osd::id);
172 }
173 }
174
175 struct obj {
176 MEMPOOL_CLASS_HELPERS();
177 int a;
178 int b;
179 obj() : a(1), b(1) {}
180 explicit obj(int _a) : a(_a), b(2) {}
181 obj(int _a,int _b) : a(_a), b(_b) {}
182 friend inline bool operator<(const obj& l, const obj& r) {
183 return l.a < r.a;
184 }
185 };
186 MEMPOOL_DEFINE_OBJECT_FACTORY(obj, obj, osdmap);
187
188 TEST(mempool, test_factory)
189 {
190 obj *o1 = new obj();
191 obj *o2 = new obj(10);
192 obj *o3 = new obj(20,30);
193 check_usage(mempool::osdmap::id);
194 EXPECT_NE(o1,nullptr);
195 EXPECT_EQ(o1->a,1);
196 EXPECT_EQ(o1->b,1);
197 EXPECT_EQ(o2->a,10);
198 EXPECT_EQ(o2->b,2);
199 EXPECT_EQ(o3->a,20);
200 EXPECT_EQ(o3->b,30);
201
202 delete o1;
203 delete o2;
204 delete o3;
205 check_usage(mempool::osdmap::id);
206 }
207
208 TEST(mempool, vector)
209 {
210 {
211 mempool::osd::vector<int> v;
212 v.push_back(1);
213 v.push_back(2);
214 }
215 {
216 mempool::osdmap::vector<obj> v;
217 v.push_back(obj());
218 v.push_back(obj(1));
219 }
220 }
221
222 TEST(mempool, set)
223 {
224 mempool::osd::set<int> set_int;
225 set_int.insert(1);
226 set_int.insert(2);
227 mempool::osdmap::set<obj> set_obj;
228 set_obj.insert(obj());
229 set_obj.insert(obj(1));
230 set_obj.insert(obj(1, 2));
231 }
232
233 TEST(mempool, map)
234 {
235 {
236 mempool::osd::map<int,int> v;
237 v[1] = 2;
238 v[3] = 4;
239 }
240 {
241 mempool::osdmap::map<int,obj> v;
242 v[1] = obj();
243 v[2] = obj(2);
244 v[3] = obj(2, 3);
245 }
246 }
247
248 TEST(mempool, list)
249 {
250 {
251 mempool::osd::list<int> v;
252 v.push_back(1);
253 v.push_back(2);
254 }
255 {
256 mempool::osdmap::list<obj> v;
257 v.push_back(obj());
258 v.push_back(obj(1));
259 }
260
261 }
262
263 TEST(mempool, dump)
264 {
265 ostringstream ostr;
266
267 Formatter* f = Formatter::create("xml-pretty", "xml-pretty", "xml-pretty");
268 mempool::dump(f);
269 f->flush(ostr);
270
271 delete f;
272 ASSERT_NE(ostr.str().find(mempool::get_pool_name((mempool::pool_index_t)0)),
273 std::string::npos);
274
275 ostr.str("");
276
277 f = Formatter::create("html-pretty", "html-pretty", "html-pretty");
278 mempool::dump(f);
279 f->flush(ostr);
280
281 delete f;
282 ASSERT_NE(ostr.str().find(mempool::get_pool_name((mempool::pool_index_t)0)),
283 std::string::npos);
284
285 ostr.str("");
286 f = Formatter::create("table", "table", "table");
287 mempool::dump(f);
288 f->flush(ostr);
289
290 delete f;
291 ASSERT_NE(ostr.str().find(mempool::get_pool_name((mempool::pool_index_t)0)),
292 std::string::npos);
293
294 ostr.str("");
295
296 f = Formatter::create("json-pretty", "json-pretty", "json-pretty");
297 mempool::dump(f);
298 f->flush(ostr);
299 delete f;
300
301 ASSERT_NE(ostr.str().find(mempool::get_pool_name((mempool::pool_index_t)0)),
302 std::string::npos);
303 }
304
305 TEST(mempool, unordered_map)
306 {
307 mempool::osdmap::unordered_map<int,obj> h;
308 h[1] = obj();
309 h[2] = obj(1);
310 }
311
312 TEST(mempool, string_test)
313 {
314 mempool::osdmap::string s;
315 s.reserve(100);
316 EXPECT_GE(mempool::osdmap::allocated_items(), s.capacity() + 1u); // +1 for zero-byte termination :
317 for (size_t i = 0; i < 10; ++i) {
318 s += '1';
319 s.append(s);
320 EXPECT_GE(mempool::osdmap::allocated_items(), s.capacity() + 1u);
321 }
322 }
323
324 TEST(mempool, bufferlist)
325 {
326 bufferlist bl;
327 int len = 1048576;
328 size_t before = mempool::buffer_anon::allocated_bytes();
329 cout << "before " << before << std::endl;
330 bl.append(buffer::create_aligned(len, 4096));
331 size_t after = mempool::buffer_anon::allocated_bytes();
332 cout << "after " << after << std::endl;
333 ASSERT_GE(after, before + len);
334 }
335
336 TEST(mempool, bufferlist_reassign)
337 {
338 bufferlist bl;
339 size_t items_before = mempool::buffer_anon::allocated_items();
340 size_t bytes_before = mempool::buffer_anon::allocated_bytes();
341 bl.append("fooo");
342 ASSERT_EQ(items_before + 1, mempool::buffer_anon::allocated_items());
343 ASSERT_LT(bytes_before, mempool::buffer_anon::allocated_bytes());
344
345 // move existing bl
346 bl.reassign_to_mempool(mempool::mempool_osd);
347 ASSERT_EQ(items_before, mempool::buffer_anon::allocated_items());
348 ASSERT_EQ(bytes_before, mempool::buffer_anon::allocated_bytes());
349
350 // additional appends should go to the same pool
351 items_before = mempool::osd::allocated_items();
352 bytes_before = mempool::osd::allocated_bytes();
353 cout << "anon b " << mempool::buffer_anon::allocated_bytes() << std::endl;
354 for (unsigned i = 0; i < 1000; ++i) {
355 bl.append("asdfddddddddddddddddddddddasfdasdfasdfasdfasdfasdf");
356 }
357 cout << "anon a " << mempool::buffer_anon::allocated_bytes() << std::endl;
358 ASSERT_LT(items_before, mempool::osd::allocated_items());
359 ASSERT_LT(bytes_before, mempool::osd::allocated_bytes());
360
361 // try_.. won't
362 items_before = mempool::osd::allocated_items();
363 bytes_before = mempool::osd::allocated_bytes();
364 bl.try_assign_to_mempool(mempool::mempool_bloom_filter);
365 ASSERT_EQ(items_before, mempool::osd::allocated_items());
366 ASSERT_EQ(bytes_before, mempool::osd::allocated_bytes());
367 }
368
369 TEST(mempool, bufferlist_c_str)
370 {
371 bufferlist bl;
372 int len = 1048576;
373 size_t before = mempool::osd::allocated_bytes();
374 bl.append(buffer::create_aligned(len, 4096));
375 bl.append(buffer::create_aligned(len, 4096));
376 bl.reassign_to_mempool(mempool::mempool_osd);
377 size_t after = mempool::osd::allocated_bytes();
378 ASSERT_GE(after, before + len * 2);
379 bl.c_str();
380 size_t after_c_str = mempool::osd::allocated_bytes();
381 ASSERT_EQ(after, after_c_str);
382 }
383
384 TEST(mempool, btree_map_test)
385 {
386 typedef mempool::pool_allocator<mempool::mempool_osd,
387 pair<const uint64_t,uint64_t>> allocator_t;
388 typedef btree::btree_map<uint64_t,uint64_t,std::less<uint64_t>,allocator_t> btree_t;
389
390 {
391 btree_t btree;
392 ASSERT_EQ(0, mempool::osd::allocated_items());
393 ASSERT_EQ(0, mempool::osd::allocated_bytes());
394 for (size_t i = 0; i < 1000; ++i) {
395 btree[rand()] = rand();
396 }
397 ASSERT_LT(0, mempool::osd::allocated_items());
398 ASSERT_LT(0, mempool::osd::allocated_bytes());
399 }
400
401 ASSERT_EQ(0, mempool::osd::allocated_items());
402 ASSERT_EQ(0, mempool::osd::allocated_bytes());
403 }
404
405 TEST(mempool, check_shard_select)
406 {
407 const size_t samples = 100;
408 std::atomic_int shards[mempool::num_shards] = {0};
409 std::vector<std::thread> workers;
410 for (size_t i = 0; i < samples; i++) {
411 workers.push_back(
412 std::thread([&](){
413 size_t i = mempool::pool_t::pick_a_shard_int();
414 shards[i]++;
415 }));
416 }
417 for (auto& t:workers) {
418 t.join();
419 }
420 workers.clear();
421
422 double EX = (double)samples / (double)mempool::num_shards;
423 double VarX = 0;
424 for (size_t i = 0; i < mempool::num_shards; i++) {
425 VarX += (EX - shards[i]) * (EX - shards[i]);
426 }
427 //random gives VarX below 200
428 //when half slots are 0, we get ~300
429 //when all samples go into one slot, we get ~9000
430 EXPECT_LT(VarX, 200);
431 }
432
433
434 int main(int argc, char **argv)
435 {
436 vector<const char*> args;
437 argv_to_vec(argc, (const char **)argv, args);
438
439 auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
440 CODE_ENVIRONMENT_UTILITY,
441 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
442 common_init_finish(g_ceph_context);
443
444 // enable debug mode for the tests
445 mempool::set_debug_mode(true);
446
447 ::testing::InitGoogleTest(&argc, argv);
448 return RUN_ALL_TESTS();
449 }
450
451
452 /*
453 * Local Variables:
454 * compile-command: "cd ../../build ; make -j4 &&
455 * make unittest_mempool &&
456 * valgrind --tool=memcheck ./unittest_mempool --gtest_filter=*.*"
457 * End:
458 */