Expose psset state stats

When evaluating changes in HPA logic, it is useful to know internal
`hpa_shard` state. Great deal of this state is `psset`. Some of the
`psset` stats was available, but in disaggregated form, which is not
very convenient. This commit exposed `psset` counters to `mallctl`
and malloc stats dumps.

Example of how malloc stats dump will look like after the change.

HPA shard stats:
  Pageslabs: 14899 (4354 huge, 10545 nonhuge)
  Active pages: 6708166 (2228917 huge, 4479249 nonhuge)
  Dirty pages: 233816 (331 huge, 233485 nonhuge)
  Retained pages: 686306
  Purge passes: 8730 (10 / sec)
  Purges: 127501 (146 / sec)
  Hugeifies: 4358 (5 / sec)
  Dehugifies: 4 (0 / sec)

Pageslabs, active pages, dirty pages and retained pages are rows added
by this change.
This commit is contained in:
Dmitry Ilvokhin
2024-11-14 10:52:50 -08:00
committed by stanjo74
parent 3820e38dc1
commit 6092c980a6
6 changed files with 458 additions and 58 deletions

View File

@@ -64,6 +64,24 @@ test_psset_alloc_reuse(psset_t *psset, edata_t *r_edata, size_t size) {
return false;
}
static hpdata_t *
test_psset_hugify(psset_t *psset, edata_t *edata) {
hpdata_t *ps = edata_ps_get(edata);
psset_update_begin(psset, ps);
hpdata_hugify(ps);
psset_update_end(psset, ps);
return ps;
}
static hpdata_t *
test_psset_dehugify(psset_t *psset, edata_t *edata) {
hpdata_t *ps = edata_ps_get(edata);
psset_update_begin(psset, ps);
hpdata_dehugify(ps);
psset_update_end(psset, ps);
return ps;
}
static hpdata_t *
test_psset_dalloc(psset_t *psset, edata_t *edata) {
hpdata_t *ps = edata_ps_get(edata);
@@ -339,6 +357,149 @@ TEST_BEGIN(test_multi_pageslab) {
}
TEST_END
TEST_BEGIN(test_stats_merged) {
hpdata_t pageslab;
hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
edata_t alloc[HUGEPAGE_PAGES];
psset_t psset;
psset_init(&psset);
expect_zu_eq(0, psset.stats.merged.npageslabs, "");
expect_zu_eq(0, psset.stats.merged.nactive, "");
expect_zu_eq(0, psset.stats.merged.ndirty, "");
edata_init_test(&alloc[0]);
test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
expect_zu_eq(1, psset.stats.merged.npageslabs, "");
expect_zu_eq(i, psset.stats.merged.nactive, "");
expect_zu_eq(0, psset.stats.merged.ndirty, "");
edata_init_test(&alloc[i]);
bool err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
expect_false(err, "Nonempty psset failed page allocation.");
}
expect_zu_eq(1, psset.stats.merged.npageslabs, "");
expect_zu_eq(HUGEPAGE_PAGES, psset.stats.merged.nactive, "");
expect_zu_eq(0, psset.stats.merged.ndirty, "");
for (ssize_t i = HUGEPAGE_PAGES - 1; i > 0; i--) {
test_psset_dalloc(&psset, &alloc[i]);
expect_zu_eq(1, psset.stats.merged.npageslabs, "");
expect_zu_eq(i, psset.stats.merged.nactive, "");
expect_zu_eq(HUGEPAGE_PAGES - i, psset.stats.merged.ndirty, "");
}
/* No allocations have left. */
test_psset_dalloc(&psset, &alloc[0]);
expect_zu_eq(0, psset.stats.merged.npageslabs, "");
expect_zu_eq(0, psset.stats.merged.nactive, "");
/*
* Last test_psset_dalloc call removed empty pageslab from psset, so
* nothing has left there, even no dirty pages.
*/
expect_zu_eq(0, psset.stats.merged.ndirty, "");
test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
expect_zu_eq(1, psset.stats.merged.npageslabs, "");
expect_zu_eq(1, psset.stats.merged.nactive, "");
expect_zu_eq(0, psset.stats.merged.ndirty, "");
psset_update_begin(&psset, &pageslab);
expect_zu_eq(0, psset.stats.merged.npageslabs, "");
expect_zu_eq(0, psset.stats.merged.nactive, "");
expect_zu_eq(0, psset.stats.merged.ndirty, "");
psset_update_end(&psset, &pageslab);
expect_zu_eq(1, psset.stats.merged.npageslabs, "");
expect_zu_eq(1, psset.stats.merged.nactive, "");
expect_zu_eq(0, psset.stats.merged.ndirty, "");
}
TEST_END
TEST_BEGIN(test_stats_huge) {
test_skip_if(!config_stats);
hpdata_t pageslab;
hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
edata_t alloc[HUGEPAGE_PAGES];
psset_t psset;
psset_init(&psset);
for (int huge = 0; huge < PSSET_NHUGE; ++huge) {
expect_zu_eq(0, psset.stats.slabs[huge].npageslabs, "");
expect_zu_eq(0, psset.stats.slabs[huge].nactive, "");
expect_zu_eq(0, psset.stats.slabs[huge].ndirty, "");
}
edata_init_test(&alloc[0]);
test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
expect_zu_eq(1, psset.stats.slabs[0].npageslabs, "");
expect_zu_eq(i, psset.stats.slabs[0].nactive, "");
expect_zu_eq(0, psset.stats.slabs[0].ndirty, "");
expect_zu_eq(0, psset.stats.slabs[1].npageslabs, "");
expect_zu_eq(0, psset.stats.slabs[1].nactive, "");
expect_zu_eq(0, psset.stats.slabs[1].ndirty, "");
edata_init_test(&alloc[i]);
bool err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
expect_false(err, "Nonempty psset failed page allocation.");
}
expect_zu_eq(1, psset.stats.slabs[0].npageslabs, "");
expect_zu_eq(HUGEPAGE_PAGES, psset.stats.slabs[0].nactive, "");
expect_zu_eq(0, psset.stats.slabs[0].ndirty, "");
expect_zu_eq(0, psset.stats.slabs[1].npageslabs, "");
expect_zu_eq(0, psset.stats.slabs[1].nactive, "");
expect_zu_eq(0, psset.stats.slabs[1].ndirty, "");
test_psset_hugify(&psset, &alloc[0]);
/* All stats should been moved from nonhuge to huge. */
expect_zu_eq(0, psset.stats.slabs[0].npageslabs, "");
expect_zu_eq(0, psset.stats.slabs[0].nactive, "");
expect_zu_eq(0, psset.stats.slabs[0].ndirty, "");
expect_zu_eq(1, psset.stats.slabs[1].npageslabs, "");
expect_zu_eq(HUGEPAGE_PAGES, psset.stats.slabs[1].nactive, "");
expect_zu_eq(0, psset.stats.slabs[1].ndirty, "");
test_psset_dehugify(&psset, &alloc[0]);
/* And back from huge to nonhuge after dehugification. */
expect_zu_eq(1, psset.stats.slabs[0].npageslabs, "");
expect_zu_eq(HUGEPAGE_PAGES, psset.stats.slabs[0].nactive, "");
expect_zu_eq(0, psset.stats.slabs[0].ndirty, "");
expect_zu_eq(0, psset.stats.slabs[1].npageslabs, "");
expect_zu_eq(0, psset.stats.slabs[1].nactive, "");
expect_zu_eq(0, psset.stats.slabs[1].ndirty, "");
for (ssize_t i = HUGEPAGE_PAGES - 1; i > 0; i--) {
test_psset_dalloc(&psset, &alloc[i]);
expect_zu_eq(1, psset.stats.slabs[0].npageslabs, "");
expect_zu_eq(i, psset.stats.slabs[0].nactive, "");
expect_zu_eq(HUGEPAGE_PAGES - i, psset.stats.slabs[0].ndirty, "");
expect_zu_eq(0, psset.stats.slabs[1].npageslabs, "");
expect_zu_eq(0, psset.stats.slabs[1].nactive, "");
expect_zu_eq(0, psset.stats.slabs[1].ndirty, "");
}
test_psset_dalloc(&psset, &alloc[0]);
for (int huge = 0; huge < PSSET_NHUGE; huge++) {
expect_zu_eq(0, psset.stats.slabs[huge].npageslabs, "");
expect_zu_eq(0, psset.stats.slabs[huge].nactive, "");
expect_zu_eq(0, psset.stats.slabs[huge].ndirty, "");
}
}
TEST_END
static void
stats_expect_empty(psset_bin_stats_t *stats) {
assert_zu_eq(0, stats->npageslabs,
@@ -379,7 +540,9 @@ stats_expect(psset_t *psset, size_t nactive) {
expect_zu_eq(nactive, psset_nactive(psset), "");
}
TEST_BEGIN(test_stats) {
TEST_BEGIN(test_stats_fullness) {
test_skip_if(!config_stats);
bool err;
hpdata_t pageslab;
@@ -739,7 +902,9 @@ main(void) {
test_reuse,
test_evict,
test_multi_pageslab,
test_stats,
test_stats_merged,
test_stats_huge,
test_stats_fullness,
test_oldest_fit,
test_insert_remove,
test_purge_prefers_nonhuge,