2626#include < boost/container/flat_set.hpp>
2727#include < boost/container/flat_map.hpp>
2828
29- #if defined(_GNU_SOURCE) && defined(WITH_SEASTAR)
29+ #if defined(_GNU_SOURCE)
30+ # define MEMPOOL_SCHED_GETCPU
3031# include < sched.h>
3132#endif
3233
3334#include " common/Formatter.h"
34- #include " common/ceph_atomic.h"
3535#include " include/ceph_assert.h"
3636#include " include/compact_map.h"
3737#include " include/compact_set.h"
@@ -196,54 +196,23 @@ extern void set_debug_mode(bool d);
196196// --------------------------------------------------------------
197197class pool_t ;
198198
199- // we shard pool stats across many shard_t's to reduce the amount
200- // of cacheline ping pong.
201199enum {
202- num_shard_bits = 5
203- };
204- enum {
205- num_shards = 1 << num_shard_bits
206- };
207-
208- static size_t pick_a_shard_int () {
209- #if defined(_GNU_SOURCE) && defined(WITH_SEASTAR)
210- // a thread local storage is actually just an approximation;
211- // what we truly want is a _cpu local storage_.
212- //
213- // on the architectures we care about sched_getcpu() is
214- // a syscall-handled-in-userspace (vdso!). it grabs the cpu
215- // id kernel exposes to a task on context switch.
216- return sched_getcpu () & ((1 << num_shard_bits) - 1 );
200+ #if defined(MEMPOOL_SCHED_GETCPU)
201+ MIN_SHARDS = 1 , // 1
217202#else
218- // Dirt cheap, see:
219- // https://fossies.org/dox/glibc-2.32/pthread__self_8c_source.html
220- size_t me = (size_t )pthread_self ();
221- size_t i = (me >> CEPH_PAGE_SHIFT) & ((1 << num_shard_bits) - 1 );
222- return i;
203+ MIN_SHARDS = 1 <<5 , // 32
223204#endif
224- }
225-
226- //
227- // Align shard to a cacheline.
228- //
229- // It would be possible to retrieve the value at runtime (for instance
230- // with getconf LEVEL1_DCACHE_LINESIZE or grep -m1 cache_alignment
231- // /proc/cpuinfo). It is easier to hard code the largest cache
232- // linesize for all known processors (128 bytes). If the actual cache
233- // linesize is smaller on a given processor, it will just waste a few
234- // bytes.
235- //
236- struct shard_t {
237- ceph::atomic<size_t > bytes = {0 };
238- ceph::atomic<size_t > items = {0 };
239- char __padding[128 - sizeof (ceph::atomic<size_t >)*2 ];
240- } __attribute__ ((aligned (128 )));
205+ DEFAULT_SHARDS = 1 <<5 , // 32
206+ MAX_SHARDS = 1 <<7 // 128
207+ };
241208
242- static_assert (sizeof (shard_t ) == 128 , " shard_t should be cacheline-sized" );
209+ int pick_a_shard_int (void );
210+ size_t get_num_shards (void );
243211
244212struct stats_t {
245- ssize_t items = 0 ;
246- ssize_t bytes = 0 ;
213+ std::atomic<size_t > items = {0 };
214+ std::atomic<size_t > bytes = {0 };
215+
247216 void dump (ceph::Formatter *f) const {
248217 f->dump_int (" items" , items);
249218 f->dump_int (" bytes" , bytes);
@@ -256,24 +225,36 @@ struct stats_t {
256225 }
257226};
258227
228+ // Align shard to a cacheline, group stats for all mempools in the
229+ // same shard to improve cache line density.
230+ //
231+ // It would be possible to retrieve the value at runtime (for instance
232+ // with getconf LEVEL1_DCACHE_LINESIZE or grep -m1 cache_alignment
233+ // /proc/cpuinfo). It is easier to hard code the largest cache
234+ // linesize for all known processors (128 bytes). If the actual cache
235+ // linesize is smaller on a given processor, it will just waste a few
236+ // bytes.
237+ //
238+ struct shard_t {
239+ stats_t pool[num_pools];
240+ } __attribute__ ((aligned (128 )));
241+ static_assert (sizeof (shard_t )%128 == 0 , " shard_t should be cacheline-sized" );
242+
243+ extern std::unique_ptr<shard_t []> shards;
244+
259245pool_t & get_pool (pool_index_t ix);
260246const char *get_pool_name (pool_index_t ix);
261247
262248struct type_t {
263249 const char *type_name;
264250 size_t item_size;
265- #ifdef WITH_SEASTAR
266251 struct type_shard_t {
267- ceph ::atomic<ssize_t > items = {0 }; // signed
268- char __padding[128 - sizeof (ceph ::atomic<ssize_t >)];
252+ std ::atomic<ssize_t > items = {0 }; // signed
253+ char __padding[128 - sizeof (std ::atomic<ssize_t >)];
269254 } __attribute__ ((aligned (128 )));
270255 static_assert (sizeof (type_shard_t ) == 128 ,
271256 " type_shard_t should be cacheline-sized" );
272- type_shard_t shards[num_shards];
273- #else
274- // XXX: consider dropping this case for classic with perf tests
275- ceph::atomic<ssize_t > items = {0 }; // signed
276- #endif
257+ std::unique_ptr<type_shard_t []> shards = std::make_unique<type_shard_t []>(get_num_shards());
277258};
278259
279260struct type_info_hash {
@@ -283,14 +264,14 @@ struct type_info_hash {
283264};
284265
285266class pool_t {
286- shard_t shard[num_shards];
287-
288267 mutable std::mutex lock; // only used for types list
289268 std::unordered_map<const char *, type_t > type_map;
290269
291270 template <pool_index_t , typename T>
292271 friend class pool_allocator ;
293272public:
273+ pool_index_t pool_index;
274+
294275 //
295276 // How much this pool consumes. O(<num_shards>)
296277 //
@@ -362,15 +343,11 @@ class pool_allocator {
362343 T* allocate (size_t n, void *p = nullptr ) {
363344 size_t total = sizeof (T) * n;
364345 const auto shid = pick_a_shard_int ();
365- auto & shard = pool-> shard [shid ];
346+ auto & shard = shards[shid]. pool [pool-> pool_index ];
366347 shard.bytes += total;
367348 shard.items += n;
368349 if (type) {
369- #ifdef WITH_SEASTAR
370350 type->shards [shid].items += n;
371- #else
372- type->items += n;
373- #endif
374351 }
375352 T* r = reinterpret_cast <T*>(new char [total]);
376353 return r;
@@ -379,31 +356,23 @@ class pool_allocator {
379356 void deallocate (T* p, size_t n) {
380357 size_t total = sizeof (T) * n;
381358 const auto shid = pick_a_shard_int ();
382- auto & shard = pool-> shard [shid ];
359+ auto & shard = shards[shid]. pool [pool-> pool_index ];
383360 shard.bytes -= total;
384361 shard.items -= n;
385362 if (type) {
386- #ifdef WITH_SEASTAR
387363 type->shards [shid].items -= n;
388- #else
389- type->items -= n;
390- #endif
391364 }
392365 delete[] reinterpret_cast <char *>(p);
393366 }
394367
395368 T* allocate_aligned (size_t n, size_t align, void *p = nullptr ) {
396369 size_t total = sizeof (T) * n;
397370 const auto shid = pick_a_shard_int ();
398- auto & shard = pool-> shard [shid ];
371+ auto & shard = shards[shid]. pool [pool-> pool_index ];
399372 shard.bytes += total;
400373 shard.items += n;
401374 if (type) {
402- #ifdef WITH_SEASTAR
403375 type->shards [shid].items += n;
404- #else
405- type->items += n;
406- #endif
407376 }
408377 char *ptr;
409378 int rc = ::posix_memalign ((void **)(void *)&ptr, align, total);
@@ -416,15 +385,11 @@ class pool_allocator {
416385 void deallocate_aligned (T* p, size_t n) {
417386 size_t total = sizeof (T) * n;
418387 const auto shid = pick_a_shard_int ();
419- auto & shard = pool-> shard [shid ];
388+ auto & shard = shards[shid]. pool [pool-> pool_index ];
420389 shard.bytes -= total;
421390 shard.items -= n;
422391 if (type) {
423- #ifdef WITH_SEASTAR
424392 type->shards [shid].items -= n;
425- #else
426- type->items -= n;
427- #endif
428393 }
429394 aligned_free (p);
430395 }
0 commit comments