Add support for sharded bins within an arena.
This makes it possible to have multiple set of bins in an arena, which improves arena scalability because the bins (especially the small ones) are always the limiting factor in production workload. A bin shard is picked on allocation; each extent tracks the bin shard id for deallocation. The shard size will be determined using runtime options.
This commit is contained in:
@@ -63,8 +63,8 @@ void *arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
||||
void arena_prof_promote(tsdn_t *tsdn, const void *ptr, size_t usize);
|
||||
void arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
|
||||
bool slow_path);
|
||||
void arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena,
|
||||
extent_t *extent, void *ptr);
|
||||
void arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
|
||||
szind_t binind, extent_t *extent, void *ptr);
|
||||
void arena_dalloc_small(tsdn_t *tsdn, void *ptr);
|
||||
bool arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, bool zero, size_t *newsize);
|
||||
@@ -86,6 +86,8 @@ size_t arena_extent_sn_next(arena_t *arena);
|
||||
arena_t *arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
|
||||
bool arena_init_huge(void);
|
||||
arena_t *arena_choose_huge(tsd_t *tsd);
|
||||
bin_t *arena_bin_choose_lock(tsdn_t *tsdn, arena_t *arena, szind_t binind,
|
||||
unsigned *binshard);
|
||||
void arena_boot(sc_data_t *sc_data);
|
||||
void arena_prefork0(tsdn_t *tsdn, arena_t *arena);
|
||||
void arena_prefork1(tsdn_t *tsdn, arena_t *arena);
|
||||
|
@@ -90,6 +90,9 @@ struct arena_s {
|
||||
*/
|
||||
atomic_u_t nthreads[2];
|
||||
|
||||
/* Next bin shard for binding new threads. Synchronization: atomic. */
|
||||
atomic_u_t binshard_next;
|
||||
|
||||
/*
|
||||
* When percpu_arena is enabled, to amortize the cost of reading /
|
||||
* updating the current CPU id, track the most recent thread accessing
|
||||
@@ -204,7 +207,7 @@ struct arena_s {
|
||||
*
|
||||
* Synchronization: internal.
|
||||
*/
|
||||
bin_t bins[SC_NBINS];
|
||||
bins_t bins[SC_NBINS];
|
||||
|
||||
/*
|
||||
* Base allocator, from which arena metadata are allocated.
|
||||
|
@@ -7,6 +7,11 @@
|
||||
#include "jemalloc/internal/mutex.h"
|
||||
#include "jemalloc/internal/sc.h"
|
||||
|
||||
#define BIN_SHARDS_MAX (1 << EXTENT_BITS_BINSHARD_WIDTH)
|
||||
|
||||
extern unsigned opt_bin_shard_maxszind;
|
||||
extern unsigned opt_n_bin_shards;
|
||||
|
||||
/*
|
||||
* A bin contains a set of extents that are currently being used for slab
|
||||
* allocations.
|
||||
@@ -42,6 +47,9 @@ struct bin_info_s {
|
||||
/* Total number of regions in a slab for this bin's size class. */
|
||||
uint32_t nregs;
|
||||
|
||||
/* Number of sharded bins in each arena for this size class. */
|
||||
uint32_t n_shards;
|
||||
|
||||
/*
|
||||
* Metadata used to manipulate bitmaps for slabs associated with this
|
||||
* bin.
|
||||
@@ -51,7 +59,6 @@ struct bin_info_s {
|
||||
|
||||
extern bin_info_t bin_infos[SC_NBINS];
|
||||
|
||||
|
||||
typedef struct bin_s bin_t;
|
||||
struct bin_s {
|
||||
/* All operations on bin_t fields require lock ownership. */
|
||||
@@ -79,6 +86,13 @@ struct bin_s {
|
||||
bin_stats_t stats;
|
||||
};
|
||||
|
||||
/* A set of sharded bins of the same size class. */
|
||||
typedef struct bins_s bins_t;
|
||||
struct bins_s {
|
||||
/* Sharded bins. Dynamically sized. */
|
||||
bin_t *bin_shards;
|
||||
};
|
||||
|
||||
void bin_infos_init(sc_data_t *sc_data, bin_info_t bin_infos[SC_NBINS]);
|
||||
void bin_boot();
|
||||
|
||||
@@ -94,7 +108,7 @@ void bin_postfork_child(tsdn_t *tsdn, bin_t *bin);
|
||||
static inline void
|
||||
bin_stats_merge(tsdn_t *tsdn, bin_stats_t *dst_bin_stats, bin_t *bin) {
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
malloc_mutex_prof_read(tsdn, &dst_bin_stats->mutex_data, &bin->lock);
|
||||
malloc_mutex_prof_accum(tsdn, &dst_bin_stats->mutex_data, &bin->lock);
|
||||
dst_bin_stats->nmalloc += bin->stats.nmalloc;
|
||||
dst_bin_stats->ndalloc += bin->stats.ndalloc;
|
||||
dst_bin_stats->nrequests += bin->stats.nrequests;
|
||||
|
@@ -70,6 +70,14 @@ extent_usize_get(const extent_t *extent) {
|
||||
return sz_index2size(extent_szind_get(extent));
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
extent_binshard_get(const extent_t *extent) {
|
||||
unsigned binshard = (unsigned)((extent->e_bits &
|
||||
EXTENT_BITS_BINSHARD_MASK) >> EXTENT_BITS_BINSHARD_SHIFT);
|
||||
assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
|
||||
return binshard;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
extent_sn_get(const extent_t *extent) {
|
||||
return (size_t)((extent->e_bits & EXTENT_BITS_SN_MASK) >>
|
||||
@@ -190,6 +198,14 @@ extent_arena_set(extent_t *extent, arena_t *arena) {
|
||||
((uint64_t)arena_ind << EXTENT_BITS_ARENA_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_binshard_set(extent_t *extent, unsigned binshard) {
|
||||
/* The assertion assumes szind is set already. */
|
||||
assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
|
||||
extent->e_bits = (extent->e_bits & ~EXTENT_BITS_BINSHARD_MASK) |
|
||||
((uint64_t)binshard << EXTENT_BITS_BINSHARD_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_addr_set(extent_t *extent, void *addr) {
|
||||
extent->e_addr = addr;
|
||||
@@ -252,6 +268,16 @@ extent_nfree_set(extent_t *extent, unsigned nfree) {
|
||||
((uint64_t)nfree << EXTENT_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_nfree_binshard_set(extent_t *extent, unsigned nfree, unsigned binshard) {
|
||||
/* The assertion assumes szind is set already. */
|
||||
assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
|
||||
extent->e_bits = (extent->e_bits &
|
||||
(~EXTENT_BITS_NFREE_MASK & ~EXTENT_BITS_BINSHARD_MASK)) |
|
||||
((uint64_t)binshard << EXTENT_BITS_BINSHARD_SHIFT) |
|
||||
((uint64_t)nfree << EXTENT_BITS_NFREE_SHIFT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
extent_nfree_inc(extent_t *extent) {
|
||||
assert(extent_slab_get(extent));
|
||||
|
@@ -29,9 +29,10 @@ struct extent_s {
|
||||
* t: state
|
||||
* i: szind
|
||||
* f: nfree
|
||||
* s: bin_shard
|
||||
* n: sn
|
||||
*
|
||||
* nnnnnnnn ... nnnnffff ffffffii iiiiiitt zdcbaaaa aaaaaaaa
|
||||
* nnnnnnnn ... nnnnnnss ssssffff ffffffii iiiiiitt zdcbaaaa aaaaaaaa
|
||||
*
|
||||
* arena_ind: Arena from which this extent came, or all 1 bits if
|
||||
* unassociated.
|
||||
@@ -76,6 +77,8 @@ struct extent_s {
|
||||
*
|
||||
* nfree: Number of free regions in slab.
|
||||
*
|
||||
* bin_shard: the shard of the bin from which this extent came.
|
||||
*
|
||||
* sn: Serial number (potentially non-unique).
|
||||
*
|
||||
* Serial numbers may wrap around if !opt_retain, but as long as
|
||||
@@ -121,7 +124,15 @@ struct extent_s {
|
||||
#define EXTENT_BITS_NFREE_SHIFT (EXTENT_BITS_SZIND_WIDTH + EXTENT_BITS_SZIND_SHIFT)
|
||||
#define EXTENT_BITS_NFREE_MASK MASK(EXTENT_BITS_NFREE_WIDTH, EXTENT_BITS_NFREE_SHIFT)
|
||||
|
||||
#define EXTENT_BITS_SN_SHIFT (EXTENT_BITS_NFREE_WIDTH + EXTENT_BITS_NFREE_SHIFT)
|
||||
#define EXTENT_BITS_BINSHARD_WIDTH 6
|
||||
#define EXTENT_BITS_BINSHARD_SHIFT (EXTENT_BITS_NFREE_WIDTH + EXTENT_BITS_NFREE_SHIFT)
|
||||
#define EXTENT_BITS_BINSHARD_MASK MASK(EXTENT_BITS_BINSHARD_WIDTH, EXTENT_BITS_BINSHARD_SHIFT)
|
||||
|
||||
/* Will make dynamic options. */
|
||||
#define OPT_N_BIN_SHARDS (1)
|
||||
#define OPT_BIN_SHARD_MAXSZIND (0)
|
||||
|
||||
#define EXTENT_BITS_SN_SHIFT (EXTENT_BITS_BINSHARD_WIDTH + EXTENT_BITS_BINSHARD_SHIFT)
|
||||
#define EXTENT_BITS_SN_MASK (UINT64_MAX << EXTENT_BITS_SN_SHIFT)
|
||||
|
||||
/* Pointer to the extent that this structure is responsible for. */
|
||||
|
@@ -263,4 +263,26 @@ malloc_mutex_prof_read(tsdn_t *tsdn, mutex_prof_data_t *data,
|
||||
atomic_store_u32(&data->n_waiting_thds, 0, ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static inline void
|
||||
malloc_mutex_prof_accum(tsdn_t *tsdn, mutex_prof_data_t *data,
|
||||
malloc_mutex_t *mutex) {
|
||||
mutex_prof_data_t *source = &mutex->prof_data;
|
||||
/* Can only read holding the mutex. */
|
||||
malloc_mutex_assert_owner(tsdn, mutex);
|
||||
|
||||
nstime_add(&data->tot_wait_time, &source->tot_wait_time);
|
||||
if (nstime_compare(&source->max_wait_time, &data->max_wait_time) > 0) {
|
||||
nstime_copy(&data->max_wait_time, &source->max_wait_time);
|
||||
}
|
||||
data->n_wait_times += source->n_wait_times;
|
||||
data->n_spin_acquired += source->n_spin_acquired;
|
||||
if (data->max_n_thds < source->max_n_thds) {
|
||||
data->max_n_thds = source->max_n_thds;
|
||||
}
|
||||
/* n_wait_thds is not reported. */
|
||||
atomic_store_u32(&data->n_waiting_thds, 0, ATOMIC_RELAXED);
|
||||
data->n_owner_switches += source->n_owner_switches;
|
||||
data->n_lock_ops += source->n_lock_ops;
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_MUTEX_H */
|
||||
|
@@ -74,6 +74,7 @@ typedef void (*test_callback_t)(int *);
|
||||
O(iarena, arena_t *, arena_t *) \
|
||||
O(arena, arena_t *, arena_t *) \
|
||||
O(arenas_tdata, arena_tdata_t *, arena_tdata_t *)\
|
||||
O(binshard, unsigned, unsigned) \
|
||||
O(tcache, tcache_t, tcache_t) \
|
||||
O(witness_tsd, witness_tsd_t, witness_tsdn_t) \
|
||||
MALLOC_TEST_TSD
|
||||
@@ -93,6 +94,7 @@ typedef void (*test_callback_t)(int *);
|
||||
NULL, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
((unsigned)-1), \
|
||||
TCACHE_ZERO_INITIALIZER, \
|
||||
WITNESS_TSD_INITIALIZER \
|
||||
MALLOC_TEST_TSD_INITIALIZER \
|
||||
|
Reference in New Issue
Block a user