]>
git.proxmox.com Git - ceph.git/blob - ceph/src/include/mempool.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2016 Allen Samuels <allen.samuels@sandisk.com>
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 #ifndef _CEPH_INCLUDE_MEMPOOL_H
16 #define _CEPH_INCLUDE_MEMPOOL_H
20 #include <unordered_map>
28 #include <common/Formatter.h>
29 #include "include/assert.h"
37 A memory pool is a method for accounting the consumption of memory of
40 Memory pools are statically declared (see pool_index_t).
42 Each memory pool tracks the number of bytes and items it contains.
44 Allocators can be declared and associated with a type so that they are
45 tracked independently of the pool total. This additional accounting
46 is optional and only incurs an overhead if the debugging is enabled at
47 runtime. This allows developers to see what types are consuming the
54 Using memory pools is very easy.
56 To create a new memory pool, simply add a new name into the list of
57 memory pools that's defined in "DEFINE_MEMORY_POOLS_HELPER". That's
60 For each memory pool that's created a C++ namespace is also
61 automatically created (name is same as in DEFINE_MEMORY_POOLS_HELPER).
62 That namespace contains a set of common STL containers that are predefined
63 with the appropriate allocators.
65 Thus for mempool "osd" we have automatically available to us:
68 mempool::osd::multimap
70 mempool::osd::multiset
73 mempool::osd::unordered_map
76 Putting objects in a mempool
77 ----------------------------
79 In order to use a memory pool with a particular type, a few additional
80 declarations are needed.
85 MEMPOOL_CLASS_HELPERS();
89 Then, in an appropriate .cc file,
91 MEMPOOL_DEFINE_OBJECT_FACTORY(Foo, foo, osd);
93 The second argument can generally be identical to the first, except
94 when the type contains a nested scope. For example, for
95 BlueStore::Onode, we need to do
97 MEMPOOL_DEFINE_OBJECT_FACTORY(BlueStore::Onode, bluestore_onode,
100 (This is just because we need to name some static variables and we
101 can't use :: in a variable name.)
103 In order to use the STL containers, simply use the namespaced variant
104 of the container type. For example,
106 mempool::osd::map<int> myvec;
111 The simplest way to interrogate the process is with
116 This will dump information about *all* memory pools. When debug mode
117 is enabled, the runtime complexity of dump is O(num_shards *
118 num_types). When debug name is disabled it is O(num_shards).
120 You can also interrogate a specific pool programmatically with
122 size_t bytes = mempool::unittest_2::allocated_bytes();
123 size_t items = mempool::unittest_2::allocated_items();
125 The runtime complexity is O(num_shards).
127 Note that you cannot easily query per-type, primarily because debug
128 mode is optional and you should not rely on that information being
135 // --------------------------------------------------------------
136 // define memory pools
138 #define DEFINE_MEMORY_POOLS_HELPER(f) \
141 f(bluestore_cache_data) \
142 f(bluestore_cache_onode) \
143 f(bluestore_cache_other) \
146 f(bluestore_writing_deferred) \
147 f(bluestore_writing) \
161 // give them integer ids
162 #define P(x) mempool_##x,
164 DEFINE_MEMORY_POOLS_HELPER(P
)
165 num_pools
// Must be last.
169 extern bool debug_mode
;
170 extern void set_debug_mode(bool d
);
172 // --------------------------------------------------------------
175 // we shard pool stats across many shard_t's to reduce the amount
176 // of cacheline ping pong.
181 num_shards
= 1 << num_shard_bits
184 // align shard to a cacheline
186 std::atomic
<size_t> bytes
= {0};
187 std::atomic
<size_t> items
= {0};
188 char __padding
[128 - sizeof(std::atomic
<size_t>)*2];
189 } __attribute__ ((aligned (128)));
191 static_assert(sizeof(shard_t
) == 128, "shard_t should be cacheline-sized");
196 void dump(ceph::Formatter
*f
) const {
197 f
->dump_int("items", items
);
198 f
->dump_int("bytes", bytes
);
201 stats_t
& operator+=(const stats_t
& o
) {
208 pool_t
& get_pool(pool_index_t ix
);
209 const char *get_pool_name(pool_index_t ix
);
212 const char *type_name
;
214 std::atomic
<ssize_t
> items
= {0}; // signed
217 struct type_info_hash
{
218 std::size_t operator()(const std::type_info
& k
) const {
219 return k
.hash_code();
224 shard_t shard
[num_shards
];
226 mutable std::mutex lock
; // only used for types list
227 std::unordered_map
<const char *, type_t
> type_map
;
231 // How much this pool consumes. O(<num_shards>)
233 size_t allocated_bytes() const;
234 size_t allocated_items() const;
236 void adjust_count(ssize_t items
, ssize_t bytes
);
238 shard_t
* pick_a_shard() {
240 // http://fossies.org/dox/glibc-2.24/pthread__self_8c_source.html
241 size_t me
= (size_t)pthread_self();
242 size_t i
= (me
>> 3) & ((1 << num_shard_bits
) - 1);
246 type_t
*get_type(const std::type_info
& ti
, size_t size
) {
247 std::lock_guard
<std::mutex
> l(lock
);
248 auto p
= type_map
.find(ti
.name());
249 if (p
!= type_map
.end()) {
252 type_t
&t
= type_map
[ti
.name()];
253 t
.type_name
= ti
.name();
258 // get pool stats. by_type is not populated if !debug
259 void get_stats(stats_t
*total
,
260 std::map
<std::string
, stats_t
> *by_type
) const;
262 void dump(ceph::Formatter
*f
, stats_t
*ptotal
=0) const;
265 void dump(ceph::Formatter
*f
);
268 // STL allocator for use with containers. All actual state
269 // is stored in the static pool_allocator_base_t, which saves us from
270 // passing the allocator to container constructors.
272 template<pool_index_t pool_ix
, typename T
>
273 class pool_allocator
{
275 type_t
*type
= nullptr;
278 typedef pool_allocator
<pool_ix
, T
> allocator_type
;
279 typedef T value_type
;
280 typedef value_type
*pointer
;
281 typedef const value_type
* const_pointer
;
282 typedef value_type
& reference
;
283 typedef const value_type
& const_reference
;
284 typedef std::size_t size_type
;
285 typedef std::ptrdiff_t difference_type
;
287 template<typename U
> struct rebind
{
288 typedef pool_allocator
<pool_ix
,U
> other
;
291 void init(bool force_register
) {
292 pool
= &get_pool(pool_ix
);
293 if (debug_mode
|| force_register
) {
294 type
= pool
->get_type(typeid(T
), sizeof(T
));
298 pool_allocator(bool force_register
=false) {
299 init(force_register
);
302 pool_allocator(const pool_allocator
<pool_ix
,U
>&) {
306 T
* allocate(size_t n
, void *p
= nullptr) {
307 size_t total
= sizeof(T
) * n
;
308 shard_t
*shard
= pool
->pick_a_shard();
309 shard
->bytes
+= total
;
314 T
* r
= reinterpret_cast<T
*>(new char[total
]);
318 void deallocate(T
* p
, size_t n
) {
319 size_t total
= sizeof(T
) * n
;
320 shard_t
*shard
= pool
->pick_a_shard();
321 shard
->bytes
-= total
;
326 delete[] reinterpret_cast<char*>(p
);
329 T
* allocate_aligned(size_t n
, size_t align
, void *p
= nullptr) {
330 size_t total
= sizeof(T
) * n
;
331 shard_t
*shard
= pool
->pick_a_shard();
332 shard
->bytes
+= total
;
338 int rc
= ::posix_memalign((void**)(void*)&ptr
, align
, total
);
340 throw std::bad_alloc();
341 T
* r
= reinterpret_cast<T
*>(ptr
);
345 void deallocate_aligned(T
* p
, size_t n
) {
346 size_t total
= sizeof(T
) * n
;
347 shard_t
*shard
= pool
->pick_a_shard();
348 shard
->bytes
-= total
;
365 void construct(T
* p
, const T
& val
) {
366 ::new ((void *)p
) T(val
);
369 template<class U
, class... Args
> void construct(U
* p
,Args
&&... args
) {
370 ::new((void *)p
) U(std::forward
<Args
>(args
)...);
373 bool operator==(const pool_allocator
&) const { return true; }
374 bool operator!=(const pool_allocator
&) const { return false; }
382 static const mempool::pool_index_t id = mempool::mempool_##x; \
383 template<typename v> \
384 using pool_allocator = mempool::pool_allocator<id,v>; \
386 using string = std::basic_string<char,std::char_traits<char>, \
387 pool_allocator<char>>; \
389 template<typename k,typename v, typename cmp = std::less<k> > \
390 using map = std::map<k, v, cmp, \
391 pool_allocator<std::pair<const k,v>>>; \
393 template<typename k,typename v, typename cmp = std::less<k> > \
394 using multimap = std::multimap<k,v,cmp, \
395 pool_allocator<std::pair<const k, \
398 template<typename k, typename cmp = std::less<k> > \
399 using set = std::set<k,cmp,pool_allocator<k>>; \
401 template<typename v> \
402 using list = std::list<v,pool_allocator<v>>; \
404 template<typename v> \
405 using vector = std::vector<v,pool_allocator<v>>; \
407 template<typename k, typename v, \
408 typename h=std::hash<k>, \
409 typename eq = std::equal_to<k>> \
410 using unordered_map = \
411 std::unordered_map<k,v,h,eq,pool_allocator<std::pair<const k,v>>>;\
413 inline size_t allocated_bytes() { \
414 return mempool::get_pool(id).allocated_bytes(); \
416 inline size_t allocated_items() { \
417 return mempool::get_pool(id).allocated_items(); \
421 DEFINE_MEMORY_POOLS_HELPER(P
)
429 // Use this for any type that is contained by a container (unless it
430 // is a class you defined; see below).
431 #define MEMPOOL_DECLARE_FACTORY(obj, factoryname, pool) \
432 namespace mempool { \
434 extern pool_allocator<obj> alloc_##factoryname; \
438 #define MEMPOOL_DEFINE_FACTORY(obj, factoryname, pool) \
439 namespace mempool { \
441 pool_allocator<obj> alloc_##factoryname = {true}; \
445 // Use this for each class that belongs to a mempool. For example,
448 // MEMPOOL_CLASS_HELPERS();
452 #define MEMPOOL_CLASS_HELPERS() \
453 void *operator new(size_t size); \
454 void *operator new[](size_t size) noexcept { \
455 assert(0 == "no array new"); \
457 void operator delete(void *); \
458 void operator delete[](void *) { assert(0 == "no array delete"); }
461 // Use this in some particular .cc file to match each class with a
462 // MEMPOOL_CLASS_HELPERS().
463 #define MEMPOOL_DEFINE_OBJECT_FACTORY(obj,factoryname,pool) \
464 MEMPOOL_DEFINE_FACTORY(obj, factoryname, pool) \
465 void *obj::operator new(size_t size) { \
466 return mempool::pool::alloc_##factoryname.allocate(1); \
468 void obj::operator delete(void *p) { \
469 return mempool::pool::alloc_##factoryname.deallocate((obj*)p, 1); \