summaryrefslogtreecommitdiffstats
path: root/lib/libc
diff options
context:
space:
mode:
authorjasone <jasone@FreeBSD.org>2006-03-20 04:05:05 +0000
committerjasone <jasone@FreeBSD.org>2006-03-20 04:05:05 +0000
commit8a77abffbc0d913256cad5750a5adf7f169aeb9c (patch)
treeb43294c72327eb0b33cded5559f68e0c5f4a389d /lib/libc
parent020594940d4ea38301162c3f1b4d10d33b69c6b3 (diff)
downloadFreeBSD-src-8a77abffbc0d913256cad5750a5adf7f169aeb9c.zip
FreeBSD-src-8a77abffbc0d913256cad5750a5adf7f169aeb9c.tar.gz
Separate completely full runs from runs that are merely almost full, so
that no linear searching is necessary if we resort to allocating from a run that is known to be mostly full. There are pathological edge cases that could have caused severely degraded performance, and this change fixes that.
Diffstat (limited to 'lib/libc')
-rw-r--r--lib/libc/stdlib/malloc.c132
1 files changed, 71 insertions, 61 deletions
diff --git a/lib/libc/stdlib/malloc.c b/lib/libc/stdlib/malloc.c
index 5572089..0374bc0 100644
--- a/lib/libc/stdlib/malloc.c
+++ b/lib/libc/stdlib/malloc.c
@@ -465,14 +465,15 @@ struct arena_run_s {
unsigned nfree:(RUN_MIN_REGS_2POW + 1);
/*
- * Current quartile for this run, one of: {RUN_Q0, RUN_25, RUN_Q50,
- * RUN_Q75, RUN_Q100}.
+ * Current quartile for this run, one of: {RUN_QEMPTY, RUN_Q0, RUN_25,
+ * RUN_Q50, RUN_Q75, RUN_Q100}.
*/
-#define RUN_Q0 0
-#define RUN_Q25 1
-#define RUN_Q50 2
-#define RUN_Q75 3
-#define RUN_Q100 4
+#define RUN_QEMPTY 0
+#define RUN_Q0 1
+#define RUN_Q25 2
+#define RUN_Q50 3
+#define RUN_Q75 4
+#define RUN_Q100 5
unsigned quartile:3;
/*
@@ -492,35 +493,38 @@ struct arena_bin_s {
arena_run_t *runcur;
/*
- * Links into rings of runs, of various fullnesses. A new run
- * conceptually starts off in runs0, and when it reaches 25% full, it
- * is moved to the runs25 ring. For the run to be moved again, it must
- * become either empty or 50% full. Thus, each ring contains runs that
- * are within 25% of the advertised fullness for the ring. This
- * provides a low-overhead mechanism for segregating runs into
- * approximate fullness classes.
+ * Links into rings of runs, of various fullnesses (names indicate
+ * approximate lower bounds). A new run conceptually starts off in
+ * runsempty, and it isn't inserted into the runs0 ring until it
+ * reaches 25% full (hysteresis mechanism). For the run to be moved
+ * again, it must become either empty or 50% full. Thus, each ring
+ * contains runs that are within 50% above the advertised fullness for
+ * the ring. This provides a low-overhead mechanism for segregating
+ * runs into approximate fullness classes.
*
- * These rings are useful when looking for an existing run
- * to use when runcur is no longer usable. We look for usable runs in
- * the following order:
+ * Conceptually, there is a runs100 that contains completely full runs.
+ * Since we don't need to search for these runs though, no runs100 ring
+ * is actually maintained.
*
- * 1) runs75
- * 2) runs50
- * 3) runs25
- * 4) runs100
+ * These rings are useful when looking for an existing run to use when
+ * runcur is no longer usable. We look for usable runs in the
+ * following order:
*
- * We never look in runs0 because it never has more than one run in it,
- * and in such cases runcur already points to that run.
+ * 1) runs50
+ * 2) runs25
+ * 3) runs0
+ * 4) runs75
*
- * runs100 isn't a good place to look, because it contains runs that
- * may be completely full. Still, we look there as a last resort in
- * order to avoid allocating a new run if at all possible.
+ * runs75 isn't a good place to look, because it contains runs that
+ * may be nearly completely full. Still, we look there as a last
+ * resort in order to avoid allocating a new run if at all possible.
*/
- /* arena_run_t runs0; 0% <= fullness < 25% */
- arena_run_t runs25; /* 0% < fullness < 50% */
- arena_run_t runs50; /* 25% < fullness < 75% */
- arena_run_t runs75; /* 50% < fullness < 100% */
- arena_run_t runs100; /* 75% < fullness <= 100% */
+ /* arena_run_t runsempty; 0% <= fullness < 25% */
+ arena_run_t runs0; /* 0% < fullness < 50% */
+ arena_run_t runs25; /* 25% < fullness < 75% */
+ arena_run_t runs50; /* 50% < fullness < 100% */
+ arena_run_t runs75; /* 75% < fullness < 100% */
+ /* arena_run_t runs100; fullness == 100% */
/* Size of regions in a run for this bin's size class. */
size_t reg_size;
@@ -1551,13 +1555,25 @@ arena_bin_run_refile(arena_t *arena, arena_bin_t *bin, arena_run_t *run,
assert(run->free_min > run->nfree);
assert(run->quartile < RUN_Q100);
run->quartile++;
+ if (run->quartile == RUN_Q75) {
+ /*
+ * Skip RUN_Q75 during promotion from RUN_Q50.
+ * Separate handling of RUN_Q75 and RUN_Q100 allows
+ * us to keep completely full runs in RUN_Q100, thus
+ * guaranteeing that runs in RUN_Q75 are only mostly
+ * full. This provides a method for avoiding a linear
+ * search for non-full runs, which avoids some
+ * pathological edge cases.
+ */
+ run->quartile++;
+ }
#ifdef MALLOC_STATS
bin->stats.npromote++;
#endif
} else {
/* Demote. */
assert(run->free_max < run->nfree);
- assert(run->quartile > RUN_Q0);
+ assert(run->quartile > RUN_QEMPTY);
run->quartile--;
#ifdef MALLOC_STATS
bin->stats.ndemote++;
@@ -1567,7 +1583,7 @@ arena_bin_run_refile(arena_t *arena, arena_bin_t *bin, arena_run_t *run,
/* Re-file run. */
qr_remove(run, link);
switch (run->quartile) {
- case RUN_Q0:
+ case RUN_QEMPTY:
#ifdef MALLOC_STATS
bin->stats.curruns--;
#endif
@@ -1578,26 +1594,30 @@ arena_bin_run_refile(arena_t *arena, arena_bin_t *bin, arena_run_t *run,
#endif
arena_run_dalloc(arena, run, bin->run_size);
break;
- case RUN_Q25:
- qr_before_insert(&bin->runs25, run, link);
+ case RUN_Q0:
+ qr_before_insert(&bin->runs0, run, link);
run->free_max = run->bin->nregs - 1;
run->free_min = (run->bin->nregs >> 1) + 1;
break;
- case RUN_Q50:
- qr_before_insert(&bin->runs50, run, link);
+ case RUN_Q25:
+ qr_before_insert(&bin->runs25, run, link);
run->free_max = ((run->bin->nregs >> 2) * 3) - 1;
run->free_min = (run->bin->nregs >> 2) + 1;
break;
+ case RUN_Q50:
+ qr_before_insert(&bin->runs50, run, link);
+ run->free_max = (run->bin->nregs >> 1) - 1;
+ run->free_min = 1;
+ break;
case RUN_Q75:
qr_before_insert(&bin->runs75, run, link);
- run->free_max = (run->bin->nregs >> 1) - 1;
+ run->free_max = (run->bin->nregs >> 2) - 1;
run->free_min = 1;
break;
case RUN_Q100:
assert(bin->runcur == run);
bin->runcur = NULL;
- qr_before_insert(&bin->runs100, run, link);
- run->free_max = (run->bin->nregs >> 2) - 1;
+ run->free_max = 0;
run->free_min = 0;
break;
default:
@@ -1752,26 +1772,15 @@ arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin, size_t size)
unsigned i, remainder;
/* Look for a usable run. */
- if ((run = qr_next(&bin->runs75, link)) != &bin->runs75
- || (run = qr_next(&bin->runs50, link)) != &bin->runs50
- || (run = qr_next(&bin->runs25, link)) != &bin->runs25) {
- /* Use a run that is guaranteed to have available space. */
+ if ((run = qr_next(&bin->runs50, link)) != &bin->runs50
+ || (run = qr_next(&bin->runs25, link)) != &bin->runs25
+ || (run = qr_next(&bin->runs0, link)) != &bin->runs0
+ || (run = qr_next(&bin->runs75, link)) != &bin->runs75) {
+ /* run is guaranteed to have available space. */
qr_remove(run, link);
return (run);
}
- /* Look for a run in runs100 that has available space. */
- if ((run = qr_next(&bin->runs100, link)) != &bin->runs100) {
- do {
- if (run->nfree > 0) {
- qr_remove(run, link);
- return (run);
- }
-
- run = qr_next(run, link);
- } while (run != &bin->runs100);
- }
-
/* Allocate a new run. */
run = arena_run_alloc(arena, false, bin->run_size);
if (run == NULL)
@@ -1795,7 +1804,7 @@ arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin, size_t size)
run->regs_minelm = 0;
run->nfree = bin->nregs;
- run->quartile = RUN_Q0;
+ run->quartile = RUN_QEMPTY;
run->free_max = bin->nregs;
run->free_min = ((bin->nregs >> 2) * 3) + 1;
#ifdef MALLOC_DEBUG
@@ -1819,6 +1828,7 @@ arena_bin_malloc_easy(arena_t *arena, arena_bin_t *bin, arena_run_t *run,
unsigned regind;
assert(run->magic == ARENA_RUN_MAGIC);
+ assert(run->nfree > 0);
regind = arena_run_search(run);
assert(regind != UINT_MAX);
@@ -1887,7 +1897,7 @@ arena_malloc(arena_t *arena, size_t size)
}
assert(size == bin->reg_size);
- if ((run = bin->runcur) != NULL && run->nfree > 0)
+ if ((run = bin->runcur) != NULL)
ret = arena_bin_malloc_easy(arena, bin, run, size);
else
ret = arena_bin_malloc_hard(arena, bin, size);
@@ -2098,10 +2108,10 @@ arena_new(arena_t *arena)
for (i = 0; i < ntbins; i++) {
bin = &arena->bins[i];
bin->runcur = NULL;
+ qr_new(&bin->runs0, link);
qr_new(&bin->runs25, link);
qr_new(&bin->runs50, link);
qr_new(&bin->runs75, link);
- qr_new(&bin->runs100, link);
bin->reg_size = (1 << (TINY_MIN_2POW + i));
@@ -2135,10 +2145,10 @@ arena_new(arena_t *arena)
for (; i < ntbins + nqbins; i++) {
bin = &arena->bins[i];
bin->runcur = NULL;
+ qr_new(&bin->runs0, link);
qr_new(&bin->runs25, link);
qr_new(&bin->runs50, link);
qr_new(&bin->runs75, link);
- qr_new(&bin->runs100, link);
bin->reg_size = quantum * (i - ntbins + 1);
@@ -2169,10 +2179,10 @@ arena_new(arena_t *arena)
for (; i < ntbins + nqbins + npbins; i++) {
bin = &arena->bins[i];
bin->runcur = NULL;
+ qr_new(&bin->runs0, link);
qr_new(&bin->runs25, link);
qr_new(&bin->runs50, link);
qr_new(&bin->runs75, link);
- qr_new(&bin->runs100, link);
bin->reg_size = (small_max << (i - (ntbins + nqbins) + 1));
OpenPOWER on IntegriCloud