mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-27 20:25:55 -04:00
Make isc_mem_isovermem() probabilistic
Replace the hysteretic hi_water/lo_water switch with a stochastic check: always false below lo_water, always true at or above hi_water, linearly ramped probability in between. This spreads cache cleaning across many inserts instead of triggering a thundering herd once the hi_water mark is crossed (which causes every addrdataset to enter the LRU purge path simultaneously and serializes lookups behind the node write locks). The is_overmem atomic and its stores are no longer needed and are removed. The existing tests that asserted specific hysteretic state transitions are simplified to check only the deterministic boundaries. (cherry picked from commit ee24d2a1c3361dcc1c48fb29bb2e0b91bc3405e8)
This commit is contained in:
parent
4c1547e4b5
commit
ddd5586a51
3 changed files with 42 additions and 52 deletions
|
|
@ -29,6 +29,7 @@
|
|||
#include <isc/once.h>
|
||||
#include <isc/os.h>
|
||||
#include <isc/overflow.h>
|
||||
#include <isc/random.h>
|
||||
#include <isc/refcount.h>
|
||||
#include <isc/strerr.h>
|
||||
#include <isc/string.h>
|
||||
|
|
@ -131,7 +132,6 @@ struct isc_mem {
|
|||
char name[16];
|
||||
atomic_size_t inuse;
|
||||
atomic_bool hi_called;
|
||||
atomic_bool is_overmem;
|
||||
atomic_size_t hi_water;
|
||||
atomic_size_t lo_water;
|
||||
ISC_LIST(isc_mempool_t) pools;
|
||||
|
|
@ -570,7 +570,6 @@ mem_create(isc_mem_t **ctxp, unsigned int debugging, unsigned int flags,
|
|||
atomic_init(&ctx->hi_water, 0);
|
||||
atomic_init(&ctx->lo_water, 0);
|
||||
atomic_init(&ctx->hi_called, false);
|
||||
atomic_init(&ctx->is_overmem, false);
|
||||
|
||||
ISC_LIST_INIT(ctx->pools);
|
||||
|
||||
|
|
@ -1017,48 +1016,30 @@ bool
|
|||
isc_mem_isovermem(isc_mem_t *ctx) {
|
||||
REQUIRE(VALID_CONTEXT(ctx));
|
||||
|
||||
bool is_overmem = atomic_load_relaxed(&ctx->is_overmem);
|
||||
|
||||
if (!is_overmem) {
|
||||
/* We are not overmem, check whether we should be? */
|
||||
size_t hiwater = atomic_load_relaxed(&ctx->hi_water);
|
||||
if (hiwater == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t inuse = atomic_load_relaxed(&ctx->inuse);
|
||||
if (inuse <= hiwater) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) {
|
||||
fprintf(stderr,
|
||||
"overmem mctx %p inuse %zu hi_water %zu\n", ctx,
|
||||
inuse, hiwater);
|
||||
}
|
||||
|
||||
atomic_store_relaxed(&ctx->is_overmem, true);
|
||||
return true;
|
||||
} else {
|
||||
/* We are overmem, check whether we should not be? */
|
||||
size_t lowater = atomic_load_relaxed(&ctx->lo_water);
|
||||
if (lowater == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t inuse = atomic_load_relaxed(&ctx->inuse);
|
||||
if (inuse >= lowater) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) {
|
||||
fprintf(stderr,
|
||||
"overmem mctx %p inuse %zu lo_water %zu\n", ctx,
|
||||
inuse, lowater);
|
||||
}
|
||||
atomic_store_relaxed(&ctx->is_overmem, false);
|
||||
size_t hiwater = atomic_load_relaxed(&ctx->hi_water);
|
||||
if (hiwater == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t inuse = atomic_load_relaxed(&ctx->inuse);
|
||||
if (inuse >= hiwater) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t lowater = atomic_load_relaxed(&ctx->lo_water);
|
||||
if (inuse <= lowater) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Between lo_water and hi_water, return true with a probability
|
||||
* that ramps linearly from 0 at lo_water to 1 at hi_water. This
|
||||
* spreads cache cleaning across many inserts instead of triggering
|
||||
* a thundering herd once the hi_water mark is crossed.
|
||||
*/
|
||||
uint32_t prob = (uint32_t)(((uint64_t)(inuse - lowater) * 256) /
|
||||
(hiwater - lowater));
|
||||
return isc_random8() < prob;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -137,7 +137,6 @@ ISC_LOOP_TEST_IMPL(overmempurge_bigrdata) {
|
|||
for (i = 0; !isc_mem_isovermem(mctx2) && i < (maxcache / 10); i++) {
|
||||
overmempurge_addrdataset(db, now, i, 50053, 0, false);
|
||||
}
|
||||
assert_true(isc_mem_isovermem(mctx2));
|
||||
|
||||
/*
|
||||
* Then try to add the same number of entries, each has very large data.
|
||||
|
|
@ -188,7 +187,6 @@ ISC_LOOP_TEST_IMPL(overmempurge_longname) {
|
|||
for (i = 0; !isc_mem_isovermem(mctx2) && i < (maxcache / 10); i++) {
|
||||
overmempurge_addrdataset(db, now, i, 50053, 0, false);
|
||||
}
|
||||
assert_true(isc_mem_isovermem(mctx2));
|
||||
|
||||
/*
|
||||
* Then try to add the same number of entries, each has very long name.
|
||||
|
|
|
|||
|
|
@ -291,6 +291,17 @@ ISC_RUN_TEST_IMPL(isc_mem_reallocate) {
|
|||
isc_mem_free(mctx, data);
|
||||
}
|
||||
|
||||
static bool
|
||||
at_least_one_overmem(isc_mem_t *omctx) {
|
||||
for (size_t i = 0; i < UINT16_MAX; i++) {
|
||||
/* The overmem is probability based in this range */
|
||||
if (isc_mem_isovermem(omctx)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(isc_mem_overmem) {
|
||||
isc_mem_t *omctx = NULL;
|
||||
isc_mem_create(&omctx);
|
||||
|
|
@ -298,27 +309,27 @@ ISC_RUN_TEST_IMPL(isc_mem_overmem) {
|
|||
|
||||
isc_mem_setwater(omctx, 1024, 512);
|
||||
|
||||
/* inuse < lo_water */
|
||||
/* inuse <= lo_water is always false */
|
||||
void *data1 = isc_mem_allocate(omctx, 256);
|
||||
assert_false(isc_mem_isovermem(omctx));
|
||||
|
||||
/* lo_water < inuse < hi_water */
|
||||
/* lo_water < inuse < hi_water might be true or false */
|
||||
void *data2 = isc_mem_allocate(omctx, 512);
|
||||
assert_false(isc_mem_isovermem(omctx));
|
||||
assert_true(at_least_one_overmem(omctx));
|
||||
|
||||
/* hi_water < inuse */
|
||||
/* hi_water <= inuse is always true */
|
||||
void *data3 = isc_mem_allocate(omctx, 512);
|
||||
assert_true(isc_mem_isovermem(omctx));
|
||||
|
||||
/* lo_water < inuse < hi_water */
|
||||
/* lo_water < inuse < hi_water might be true or false */
|
||||
isc_mem_free(omctx, data2);
|
||||
assert_true(isc_mem_isovermem(omctx));
|
||||
assert_true(at_least_one_overmem(omctx));
|
||||
|
||||
/* inuse < lo_water */
|
||||
/* inuse <= lo_water is always false */
|
||||
isc_mem_free(omctx, data3);
|
||||
assert_false(isc_mem_isovermem(omctx));
|
||||
|
||||
/* inuse == 0 */
|
||||
/* inuse == 0 is always false */
|
||||
isc_mem_free(omctx, data1);
|
||||
assert_false(isc_mem_isovermem(omctx));
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue