Use SEC in PAC to reduce lock contention on the ecaches

Add a small extent cache in front of the PAC ecaches. Allocs and dallocs
that fit are served from per-shard SEC bins without taking the ecache
mutex; overflow falls through to the backing ecaches, including
ecache_pinned for pinned extents.

The feature is gated behind experimental_pac_sec_nshards (default 0,
disabled). To support independent HPA and PAC SEC instances,
sec_alloc/sec_dalloc/sec_fill take an explicit shard argument, with HPA
and PAC using separate TSD shard slots.
This commit is contained in:
Bin Liu 2026-05-19 00:11:15 -07:00
parent 11b99d7a21
commit 6b13adf375
19 changed files with 680 additions and 59 deletions

View file

@ -22,6 +22,7 @@ extern bool opt_confirm_conf;
extern bool opt_hpa;
extern hpa_shard_opts_t opt_hpa_opts;
extern sec_opts_t opt_hpa_sec_opts;
extern sec_opts_t opt_pac_sec_opts;
extern const char *opt_junk;
extern bool opt_junk_alloc;

View file

@ -37,7 +37,8 @@ typedef enum {
OP(tcache_list) \
OP(hpa_shard) \
OP(hpa_shard_grow) \
OP(hpa_sec)
OP(hpa_sec) \
OP(pac_sec)
typedef enum {
#define OP(mtx) arena_prof_mutex_##mtx,

View file

@ -8,6 +8,7 @@
#include "jemalloc/internal/edata_cache.h"
#include "jemalloc/internal/exp_grow.h"
#include "jemalloc/internal/lockedint.h"
#include "jemalloc/internal/sec.h"
#include "jemalloc/internal/tsd_types.h"
#include "san_bump.h"
@ -84,12 +85,21 @@ struct pac_stats_s {
/* VM space had to be leaked (undocumented). Normally 0. */
atomic_zu_t abandoned_vm;
/* PAC SEC stats. Derived. */
sec_stats_t pac_sec_stats;
};
typedef struct pac_s pac_t;
struct pac_s {
/* Small extent cache in front of PAC ecaches to reduce contention. */
sec_t sec;
/* 0 disables PAC SEC; otherwise max size SEC will cache. */
atomic_zu_t sec_max_alloc;
/* True once pinned memory has been seen. */
atomic_b_t has_pinned;
/*
* Collections of extents that were previously allocated. These are
* used when allocating extents, in an attempt to re-use address space.
@ -237,4 +247,6 @@ ssize_t pac_decay_ms_get(pac_t *pac, extent_state_t state);
void pac_reset(tsdn_t *tsdn, pac_t *pac);
void pac_destroy(tsdn_t *tsdn, pac_t *pac);
void pac_sec_flush(tsdn_t *tsdn, pac_t *pac);
#endif /* JEMALLOC_INTERNAL_PAC_H */

View file

@ -111,10 +111,17 @@ sec_size_supported(sec_t *sec, size_t size) {
void sec_calc_nallocs_for_size(
sec_t *sec, size_t size, size_t *min_nallocs, size_t *max_nallocs);
/*
* Lazily picks (and caches in *idxp) a shard for the calling thread. Different
* SEC instances pass independent per-thread uint8_t slots, initialized to
* (uint8_t)-1.
*/
uint8_t sec_shard_pick(tsd_t *tsd, sec_t *sec, uint8_t *idxp);
/* If sec does not have extent available, it will return NULL. */
edata_t *sec_alloc(tsdn_t *tsdn, sec_t *sec, size_t size);
edata_t *sec_alloc(tsdn_t *tsdn, sec_t *sec, size_t size, uint8_t shard);
void sec_fill(tsdn_t *tsdn, sec_t *sec, size_t size,
edata_list_active_t *result, size_t nallocs);
edata_list_active_t *result, size_t nallocs, uint8_t shard);
/*
* Upon return dalloc_list may be empty if edata is consumed by sec or non-empty
@ -124,7 +131,8 @@ void sec_fill(tsdn_t *tsdn, sec_t *sec, size_t size,
* considered "hot" and preserved in the cache, while "colder" ones are
* returned).
*/
void sec_dalloc(tsdn_t *tsdn, sec_t *sec, edata_list_active_t *dalloc_list);
void sec_dalloc(tsdn_t *tsdn, sec_t *sec, edata_list_active_t *dalloc_list,
uint8_t shard);
bool sec_init(tsdn_t *tsdn, sec_t *sec, base_t *base, const sec_opts_t *opts);

View file

@ -76,6 +76,7 @@ typedef void (*test_callback_t)(int *);
O(arena, arena_t *, arena_t *) \
O(arena_decay_ticker, ticker_geom_t, ticker_geom_t) \
O(sec_shard, uint8_t, uint8_t) \
O(pac_sec_shard, uint8_t, uint8_t) \
O(binshards, tsd_binshards_t, tsd_binshards_t) \
O(peak, peak_t, peak_t) \
O(tcache_slow, tcache_slow_t, tcache_slow_t) \
@ -95,6 +96,7 @@ typedef void (*test_callback_t)(int *);
/* arena */ NULL, /* arena_decay_ticker */ \
TICKER_GEOM_INIT(ARENA_DECAY_NTICKS_PER_UPDATE), \
/* sec_shard */ (uint8_t) - 1, \
/* pac_sec_shard */ (uint8_t) - 1, \
/* binshards */ TSD_BINSHARDS_ZERO_INITIALIZER, \
/* peak */ PEAK_INITIALIZER, /* tcache_slow */ \
TCACHE_SLOW_ZERO_INITIALIZER, \