HPA: Use dirtiest-first purging.
This seems to be practically beneficial, despite some pathological corner cases.
This commit is contained in:
committed by
David Goldblatt
parent
0f6c420f83
commit
73ca4b8ef8
@@ -6,42 +6,6 @@
|
||||
#include "jemalloc/internal/ql.h"
|
||||
#include "jemalloc/internal/typed_list.h"
|
||||
|
||||
/*
|
||||
* How badly we want to purge some region of memory. This is a temporary
|
||||
* definition; it gets deleted in the next commit (where we adopt a more
|
||||
* explicit dirtiest-first policy that only considers hugification status).
|
||||
*/
|
||||
enum hpdata_purge_level_e {
|
||||
/*
|
||||
* The level number is important -- we use it as indices into an array
|
||||
* of size 2 (one for each purge level).
|
||||
*/
|
||||
|
||||
/* "Regular" candidates for purging. */
|
||||
hpdata_purge_level_default = 0,
|
||||
|
||||
/*
|
||||
* Candidates for purging, but as a last resort. Practically,
|
||||
* nonpreferred corresponds to hugified regions that are below the
|
||||
* hugification threshold but have not yet reached the dehugification
|
||||
* threshold, while strongly nonpreferred candidates are those which are
|
||||
* above the hugification threshold.
|
||||
*/
|
||||
hpdata_purge_level_nonpreferred = 1,
|
||||
hpdata_purge_level_strongly_nonpreferred = 2,
|
||||
|
||||
/* Don't purge, no matter what. */
|
||||
hpdata_purge_level_never = 2,
|
||||
|
||||
/*
|
||||
* How big an array has to be to accomodate all purge levels. This
|
||||
* relies on the fact that we don't actually keep unpurgable hpdatas in
|
||||
* a container.
|
||||
*/
|
||||
hpdata_purge_level_count = hpdata_purge_level_never
|
||||
};
|
||||
typedef enum hpdata_purge_level_e hpdata_purge_level_t;
|
||||
|
||||
/*
|
||||
* The metadata representation we use for extents in hugepages. While the PAC
|
||||
* uses the edata_t to represent both active and inactive extents, the HP only
|
||||
@@ -87,9 +51,13 @@ struct hpdata_s {
|
||||
bool h_alloc_allowed;
|
||||
bool h_in_psset_alloc_container;
|
||||
|
||||
/* The same, but with purging. */
|
||||
uint8_t h_purge_level;
|
||||
uint8_t h_purge_container_level;
|
||||
/*
|
||||
* The same, but with purging. There's no corresponding
|
||||
* h_in_psset_purge_container, because the psset (currently) always
|
||||
* removes hpdatas from their containers during updates (to implement
|
||||
* LRU for purging).
|
||||
*/
|
||||
bool h_purge_allowed;
|
||||
|
||||
/* And with hugifying. */
|
||||
bool h_hugify_allowed;
|
||||
@@ -200,26 +168,15 @@ hpdata_in_psset_alloc_container_set(hpdata_t *hpdata, bool in_container) {
|
||||
hpdata->h_in_psset_alloc_container = in_container;
|
||||
}
|
||||
|
||||
static inline hpdata_purge_level_t
|
||||
hpdata_purge_level_get(const hpdata_t *hpdata) {
|
||||
return (hpdata_purge_level_t)hpdata->h_purge_level;
|
||||
static inline bool
|
||||
hpdata_purge_allowed_get(const hpdata_t *hpdata) {
|
||||
return hpdata->h_purge_allowed;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_purge_level_set(hpdata_t *hpdata, hpdata_purge_level_t level) {
|
||||
assert(level == hpdata_purge_level_never || !hpdata->h_mid_purge);
|
||||
hpdata->h_purge_level = (uint8_t)level;
|
||||
}
|
||||
|
||||
static inline hpdata_purge_level_t
|
||||
hpdata_purge_container_level_get(const hpdata_t *hpdata) {
|
||||
return (hpdata_purge_level_t)hpdata->h_purge_container_level;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hpdata_purge_container_level_set(hpdata_t *hpdata, hpdata_purge_level_t level) {
|
||||
assert(level != hpdata->h_purge_container_level);
|
||||
hpdata->h_purge_container_level = level;
|
||||
hpdata_purge_allowed_set(hpdata_t *hpdata, bool purge_allowed) {
|
||||
assert(purge_allowed == false || !hpdata->h_mid_purge);
|
||||
hpdata->h_purge_allowed = purge_allowed;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
@@ -357,12 +314,7 @@ hpdata_consistent(hpdata_t *hpdata) {
|
||||
return false;
|
||||
}
|
||||
if (hpdata_changing_state_get(hpdata)
|
||||
&& ((hpdata->h_purge_level != hpdata_purge_level_never)
|
||||
|| hpdata->h_hugify_allowed)) {
|
||||
return false;
|
||||
}
|
||||
if (hpdata_purge_level_get(hpdata)
|
||||
!= hpdata_purge_container_level_get(hpdata)) {
|
||||
&& ((hpdata->h_purge_allowed) || hpdata->h_hugify_allowed)) {
|
||||
return false;
|
||||
}
|
||||
if (hpdata_hugify_allowed_get(hpdata)
|
||||
|
@@ -20,6 +20,14 @@
|
||||
*/
|
||||
#define PSSET_NPSIZES 64
|
||||
|
||||
/*
|
||||
* We keep two purge lists per page size class; one for hugified hpdatas (at
|
||||
* index 2*pszind), and one for the non-hugified hpdatas (at index 2*pszind +
|
||||
* 1). This lets us implement a preference for purging non-hugified hpdatas
|
||||
* among similarly-dirty ones.
|
||||
*/
|
||||
#define PSSET_NPURGE_LISTS (2 * PSSET_NPSIZES)
|
||||
|
||||
typedef struct psset_bin_stats_s psset_bin_stats_t;
|
||||
struct psset_bin_stats_s {
|
||||
/* How many pageslabs are in this bin? */
|
||||
@@ -71,7 +79,9 @@ struct psset_s {
|
||||
*/
|
||||
hpdata_empty_list_t empty;
|
||||
/* Slabs which are available to be purged, ordered by purge level. */
|
||||
hpdata_purge_list_t to_purge[hpdata_purge_level_count];
|
||||
hpdata_purge_list_t to_purge[PSSET_NPURGE_LISTS];
|
||||
/* Bitmap for which set bits correspond to non-empty purge lists. */
|
||||
fb_group_t purge_bitmap[FB_NGROUPS(PSSET_NPURGE_LISTS)];
|
||||
/* Slabs which are available to be hugified. */
|
||||
hpdata_hugify_list_t to_hugify;
|
||||
};
|
||||
|
Reference in New Issue
Block a user