summaryrefslogtreecommitdiffstats
path: root/include/jemalloc/internal/tcache.h
diff options
context:
space:
mode:
authorJason Evans <jasone@canonware.com>2012-05-12 00:48:33 (GMT)
committerJason Evans <jasone@canonware.com>2012-05-12 00:48:33 (GMT)
commitfc9b1dbf69f59d7ecfc4ac68da9847e017e1d046 (patch)
tree7f843c7c51cd5df5d3be1ca48f504325f0536c0d /include/jemalloc/internal/tcache.h
parentfc1bb70e5f0d9a58b39efa39cc549b5af5104760 (diff)
parentcbb71caceb1e53d0fd21284ce298885327c211b4 (diff)
downloadjemalloc-3.0.0.zip
jemalloc-3.0.0.tar.gz
jemalloc-3.0.0.tar.bz2
Merge branch 'dev'3.0.0
Conflicts: ChangeLog include/jemalloc/internal/chunk.h src/chunk.c src/huge.c src/jemalloc.c test/rallocm.c
Diffstat (limited to 'include/jemalloc/internal/tcache.h')
-rw-r--r--include/jemalloc/internal/tcache.h405
1 files changed, 207 insertions, 198 deletions
diff --git a/include/jemalloc/internal/tcache.h b/include/jemalloc/internal/tcache.h
index da3c68c..38d735c 100644
--- a/include/jemalloc/internal/tcache.h
+++ b/include/jemalloc/internal/tcache.h
@@ -1,4 +1,3 @@
-#ifdef JEMALLOC_TCACHE
/******************************************************************************/
#ifdef JEMALLOC_H_TYPES
@@ -7,6 +6,16 @@ typedef struct tcache_bin_s tcache_bin_t;
typedef struct tcache_s tcache_t;
/*
+ * tcache pointers close to NULL are used to encode state information that is
+ * used for two purposes: preventing thread caching on a per thread basis and
+ * cleaning up during thread shutdown.
+ */
+#define TCACHE_STATE_DISABLED ((tcache_t *)(uintptr_t)1)
+#define TCACHE_STATE_REINCARNATED ((tcache_t *)(uintptr_t)2)
+#define TCACHE_STATE_PURGATORY ((tcache_t *)(uintptr_t)3)
+#define TCACHE_STATE_MAX TCACHE_STATE_PURGATORY
+
+/*
* Absolute maximum number of cache slots for each small bin in the thread
* cache. This is an additional constraint beyond that imposed as: twice the
* number of regions per run for this size class.
@@ -22,17 +31,26 @@ typedef struct tcache_s tcache_t;
#define LG_TCACHE_MAXCLASS_DEFAULT 15
/*
- * (1U << opt_lg_tcache_gc_sweep) is the approximate number of allocation
- * events between full GC sweeps (-1: disabled). Integer rounding may cause
- * the actual number to be slightly higher, since GC is performed
- * incrementally.
+ * TCACHE_GC_SWEEP is the approximate number of allocation events between
+ * full GC sweeps. Integer rounding may cause the actual number to be
+ * slightly higher, since GC is performed incrementally.
*/
-#define LG_TCACHE_GC_SWEEP_DEFAULT 13
+#define TCACHE_GC_SWEEP 8192
+
+/* Number of tcache allocation/deallocation events between incremental GCs. */
+#define TCACHE_GC_INCR \
+ ((TCACHE_GC_SWEEP / NBINS) + ((TCACHE_GC_SWEEP / NBINS == 0) ? 0 : 1))
#endif /* JEMALLOC_H_TYPES */
/******************************************************************************/
#ifdef JEMALLOC_H_STRUCTS
+typedef enum {
+ tcache_enabled_false = 0, /* Enable cast to/from bool. */
+ tcache_enabled_true = 1,
+ tcache_enabled_default = 2
+} tcache_enabled_t;
+
/*
* Read-only information associated with each element of tcache_t's tbins array
* is stored separately, mainly to reduce memory usage.
@@ -42,9 +60,7 @@ struct tcache_bin_info_s {
};
struct tcache_bin_s {
-# ifdef JEMALLOC_STATS
tcache_bin_stats_t tstats;
-# endif
int low_water; /* Min # cached since last GC. */
unsigned lg_fill_div; /* Fill (ncached_max >> lg_fill_div). */
unsigned ncached; /* # of cached objects. */
@@ -52,12 +68,8 @@ struct tcache_bin_s {
};
struct tcache_s {
-# ifdef JEMALLOC_STATS
ql_elm(tcache_t) link; /* Used for aggregating stats. */
-# endif
-# ifdef JEMALLOC_PROF
uint64_t prof_accumbytes;/* Cleared after arena_prof_accum() */
-# endif
arena_t *arena; /* This thread's arena. */
unsigned ev_cnt; /* Event count since incremental GC. */
unsigned next_gc_bin; /* Next bin to GC. */
@@ -76,29 +88,11 @@ struct tcache_s {
extern bool opt_tcache;
extern ssize_t opt_lg_tcache_max;
-extern ssize_t opt_lg_tcache_gc_sweep;
extern tcache_bin_info_t *tcache_bin_info;
-/* Map of thread-specific caches. */
-#ifndef NO_TLS
-extern __thread tcache_t *tcache_tls
- JEMALLOC_ATTR(tls_model("initial-exec"));
-# define TCACHE_GET() tcache_tls
-# define TCACHE_SET(v) do { \
- tcache_tls = (tcache_t *)(v); \
- pthread_setspecific(tcache_tsd, (void *)(v)); \
-} while (0)
-#else
-# define TCACHE_GET() ((tcache_t *)pthread_getspecific(tcache_tsd))
-# define TCACHE_SET(v) do { \
- pthread_setspecific(tcache_tsd, (void *)(v)); \
-} while (0)
-#endif
-extern pthread_key_t tcache_tsd;
-
/*
- * Number of tcache bins. There are nbins small-object bins, plus 0 or more
+ * Number of tcache bins. There are NBINS small-object bins, plus 0 or more
* large-object bins.
*/
extern size_t nhbins;
@@ -106,68 +100,159 @@ extern size_t nhbins;
/* Maximum cached size class. */
extern size_t tcache_maxclass;
-/* Number of tcache allocation/deallocation events between incremental GCs. */
-extern unsigned tcache_gc_incr;
-
-void tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem
-#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
- , tcache_t *tcache
-#endif
- );
-void tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem
-#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
- , tcache_t *tcache
-#endif
- );
-tcache_t *tcache_create(arena_t *arena);
+size_t tcache_salloc(const void *ptr);
+void tcache_event_hard(tcache_t *tcache);
void *tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin,
size_t binind);
+void tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem,
+ tcache_t *tcache);
+void tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,
+ tcache_t *tcache);
+void tcache_arena_associate(tcache_t *tcache, arena_t *arena);
+void tcache_arena_dissociate(tcache_t *tcache);
+tcache_t *tcache_create(arena_t *arena);
void tcache_destroy(tcache_t *tcache);
-#ifdef JEMALLOC_STATS
+void tcache_thread_cleanup(void *arg);
void tcache_stats_merge(tcache_t *tcache, arena_t *arena);
-#endif
-bool tcache_boot(void);
+bool tcache_boot0(void);
+bool tcache_boot1(void);
#endif /* JEMALLOC_H_EXTERNS */
/******************************************************************************/
#ifdef JEMALLOC_H_INLINES
#ifndef JEMALLOC_ENABLE_INLINE
+malloc_tsd_protos(JEMALLOC_ATTR(unused), tcache, tcache_t *)
+malloc_tsd_protos(JEMALLOC_ATTR(unused), tcache_enabled, tcache_enabled_t)
+
void tcache_event(tcache_t *tcache);
-tcache_t *tcache_get(void);
+void tcache_flush(void);
+bool tcache_enabled_get(void);
+tcache_t *tcache_get(bool create);
+void tcache_enabled_set(bool enabled);
void *tcache_alloc_easy(tcache_bin_t *tbin);
void *tcache_alloc_small(tcache_t *tcache, size_t size, bool zero);
void *tcache_alloc_large(tcache_t *tcache, size_t size, bool zero);
-void tcache_dalloc_small(tcache_t *tcache, void *ptr);
+void tcache_dalloc_small(tcache_t *tcache, void *ptr, size_t binind);
void tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size);
#endif
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TCACHE_C_))
+/* Map of thread-specific caches. */
+malloc_tsd_externs(tcache, tcache_t *)
+malloc_tsd_funcs(JEMALLOC_INLINE, tcache, tcache_t *, NULL,
+ tcache_thread_cleanup)
+/* Per thread flag that allows thread caches to be disabled. */
+malloc_tsd_externs(tcache_enabled, tcache_enabled_t)
+malloc_tsd_funcs(JEMALLOC_INLINE, tcache_enabled, tcache_enabled_t,
+ tcache_enabled_default, malloc_tsd_no_cleanup)
+
+JEMALLOC_INLINE void
+tcache_flush(void)
+{
+ tcache_t *tcache;
+
+ cassert(config_tcache);
+
+ tcache = *tcache_tsd_get();
+ if ((uintptr_t)tcache <= (uintptr_t)TCACHE_STATE_MAX)
+ return;
+ tcache_destroy(tcache);
+ tcache = NULL;
+ tcache_tsd_set(&tcache);
+}
+
+JEMALLOC_INLINE bool
+tcache_enabled_get(void)
+{
+ tcache_enabled_t tcache_enabled;
+
+ cassert(config_tcache);
+
+ tcache_enabled = *tcache_enabled_tsd_get();
+ if (tcache_enabled == tcache_enabled_default) {
+ tcache_enabled = (tcache_enabled_t)opt_tcache;
+ tcache_enabled_tsd_set(&tcache_enabled);
+ }
+
+ return ((bool)tcache_enabled);
+}
+
+JEMALLOC_INLINE void
+tcache_enabled_set(bool enabled)
+{
+ tcache_enabled_t tcache_enabled;
+ tcache_t *tcache;
+
+ cassert(config_tcache);
+
+ tcache_enabled = (tcache_enabled_t)enabled;
+ tcache_enabled_tsd_set(&tcache_enabled);
+ tcache = *tcache_tsd_get();
+ if (enabled) {
+ if (tcache == TCACHE_STATE_DISABLED) {
+ tcache = NULL;
+ tcache_tsd_set(&tcache);
+ }
+ } else /* disabled */ {
+ if (tcache > TCACHE_STATE_MAX) {
+ tcache_destroy(tcache);
+ tcache = NULL;
+ }
+ if (tcache == NULL) {
+ tcache = TCACHE_STATE_DISABLED;
+ tcache_tsd_set(&tcache);
+ }
+ }
+}
+
JEMALLOC_INLINE tcache_t *
-tcache_get(void)
+tcache_get(bool create)
{
tcache_t *tcache;
- if ((isthreaded & opt_tcache) == false)
+ if (config_tcache == false)
+ return (NULL);
+ if (config_lazy_lock && isthreaded == false)
return (NULL);
- tcache = TCACHE_GET();
- if ((uintptr_t)tcache <= (uintptr_t)2) {
+ tcache = *tcache_tsd_get();
+ if ((uintptr_t)tcache <= (uintptr_t)TCACHE_STATE_MAX) {
+ if (tcache == TCACHE_STATE_DISABLED)
+ return (NULL);
if (tcache == NULL) {
- tcache = tcache_create(choose_arena());
- if (tcache == NULL)
- return (NULL);
- } else {
- if (tcache == (void *)(uintptr_t)1) {
+ if (create == false) {
/*
- * Make a note that an allocator function was
- * called after the tcache_thread_cleanup() was
- * called.
+ * Creating a tcache here would cause
+ * allocation as a side effect of free().
+ * Ordinarily that would be okay since
+ * tcache_create() failure is a soft failure
+ * that doesn't propagate. However, if TLS
+ * data are freed via free() as in glibc,
+ * subtle corruption could result from setting
+ * a TLS variable after its backing memory is
+ * freed.
*/
- TCACHE_SET((uintptr_t)2);
+ return (NULL);
+ }
+ if (tcache_enabled_get() == false) {
+ tcache_enabled_set(false); /* Memoize. */
+ return (NULL);
}
+ return (tcache_create(choose_arena(NULL)));
+ }
+ if (tcache == TCACHE_STATE_PURGATORY) {
+ /*
+ * Make a note that an allocator function was called
+ * after tcache_thread_cleanup() was called.
+ */
+ tcache = TCACHE_STATE_REINCARNATED;
+ tcache_tsd_set(&tcache);
return (NULL);
}
+ if (tcache == TCACHE_STATE_REINCARNATED)
+ return (NULL);
+ not_reached();
}
return (tcache);
@@ -177,60 +262,13 @@ JEMALLOC_INLINE void
tcache_event(tcache_t *tcache)
{
- if (tcache_gc_incr == 0)
+ if (TCACHE_GC_INCR == 0)
return;
tcache->ev_cnt++;
- assert(tcache->ev_cnt <= tcache_gc_incr);
- if (tcache->ev_cnt == tcache_gc_incr) {
- size_t binind = tcache->next_gc_bin;
- tcache_bin_t *tbin = &tcache->tbins[binind];
- tcache_bin_info_t *tbin_info = &tcache_bin_info[binind];
-
- if (tbin->low_water > 0) {
- /*
- * Flush (ceiling) 3/4 of the objects below the low
- * water mark.
- */
- if (binind < nbins) {
- tcache_bin_flush_small(tbin, binind,
- tbin->ncached - tbin->low_water +
- (tbin->low_water >> 2)
-#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
- , tcache
-#endif
- );
- } else {
- tcache_bin_flush_large(tbin, binind,
- tbin->ncached - tbin->low_water +
- (tbin->low_water >> 2)
-#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
- , tcache
-#endif
- );
- }
- /*
- * Reduce fill count by 2X. Limit lg_fill_div such that
- * the fill count is always at least 1.
- */
- if ((tbin_info->ncached_max >> (tbin->lg_fill_div+1))
- >= 1)
- tbin->lg_fill_div++;
- } else if (tbin->low_water < 0) {
- /*
- * Increase fill count by 2X. Make sure lg_fill_div
- * stays greater than 0.
- */
- if (tbin->lg_fill_div > 1)
- tbin->lg_fill_div--;
- }
- tbin->low_water = tbin->ncached;
-
- tcache->next_gc_bin++;
- if (tcache->next_gc_bin == nhbins)
- tcache->next_gc_bin = 0;
- tcache->ev_cnt = 0;
- }
+ assert(tcache->ev_cnt <= TCACHE_GC_INCR);
+ if (tcache->ev_cnt == TCACHE_GC_INCR)
+ tcache_event_hard(tcache);
}
JEMALLOC_INLINE void *
@@ -257,7 +295,7 @@ tcache_alloc_small(tcache_t *tcache, size_t size, bool zero)
tcache_bin_t *tbin;
binind = SMALL_SIZE2BIN(size);
- assert(binind < nbins);
+ assert(binind < NBINS);
tbin = &tcache->tbins[binind];
ret = tcache_alloc_easy(tbin);
if (ret == NULL) {
@@ -265,24 +303,29 @@ tcache_alloc_small(tcache_t *tcache, size_t size, bool zero)
if (ret == NULL)
return (NULL);
}
- assert(arena_salloc(ret) == arena_bin_info[binind].reg_size);
+ assert(tcache_salloc(ret) == arena_bin_info[binind].reg_size);
if (zero == false) {
-#ifdef JEMALLOC_FILL
- if (opt_junk)
- memset(ret, 0xa5, size);
- else if (opt_zero)
- memset(ret, 0, size);
-#endif
- } else
+ if (config_fill) {
+ if (opt_junk) {
+ arena_alloc_junk_small(ret,
+ &arena_bin_info[binind], false);
+ } else if (opt_zero)
+ memset(ret, 0, size);
+ }
+ } else {
+ if (config_fill && opt_junk) {
+ arena_alloc_junk_small(ret, &arena_bin_info[binind],
+ true);
+ }
+ VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
memset(ret, 0, size);
+ }
-#ifdef JEMALLOC_STATS
- tbin->tstats.nrequests++;
-#endif
-#ifdef JEMALLOC_PROF
- tcache->prof_accumbytes += arena_bin_info[binind].reg_size;
-#endif
+ if (config_stats)
+ tbin->tstats.nrequests++;
+ if (config_prof)
+ tcache->prof_accumbytes += arena_bin_info[binind].reg_size;
tcache_event(tcache);
return (ret);
}
@@ -296,7 +339,7 @@ tcache_alloc_large(tcache_t *tcache, size_t size, bool zero)
size = PAGE_CEILING(size);
assert(size <= tcache_maxclass);
- binind = nbins + (size >> PAGE_SHIFT) - 1;
+ binind = NBINS + (size >> LG_PAGE) - 1;
assert(binind < nhbins);
tbin = &tcache->tbins[binind];
ret = tcache_alloc_easy(tbin);
@@ -309,28 +352,30 @@ tcache_alloc_large(tcache_t *tcache, size_t size, bool zero)
if (ret == NULL)
return (NULL);
} else {
-#ifdef JEMALLOC_PROF
- arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ret);
- size_t pageind = (((uintptr_t)ret - (uintptr_t)chunk) >>
- PAGE_SHIFT);
- chunk->map[pageind-map_bias].bits &= ~CHUNK_MAP_CLASS_MASK;
-#endif
+ if (config_prof && prof_promote && size == PAGE) {
+ arena_chunk_t *chunk =
+ (arena_chunk_t *)CHUNK_ADDR2BASE(ret);
+ size_t pageind = (((uintptr_t)ret - (uintptr_t)chunk) >>
+ LG_PAGE);
+ arena_mapbits_large_binind_set(chunk, pageind,
+ BININD_INVALID);
+ }
if (zero == false) {
-#ifdef JEMALLOC_FILL
- if (opt_junk)
- memset(ret, 0xa5, size);
- else if (opt_zero)
- memset(ret, 0, size);
-#endif
- } else
+ if (config_fill) {
+ if (opt_junk)
+ memset(ret, 0xa5, size);
+ else if (opt_zero)
+ memset(ret, 0, size);
+ }
+ } else {
+ VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
memset(ret, 0, size);
+ }
-#ifdef JEMALLOC_STATS
- tbin->tstats.nrequests++;
-#endif
-#ifdef JEMALLOC_PROF
- tcache->prof_accumbytes += size;
-#endif
+ if (config_stats)
+ tbin->tstats.nrequests++;
+ if (config_prof)
+ tcache->prof_accumbytes += size;
}
tcache_event(tcache);
@@ -338,45 +383,21 @@ tcache_alloc_large(tcache_t *tcache, size_t size, bool zero)
}
JEMALLOC_INLINE void
-tcache_dalloc_small(tcache_t *tcache, void *ptr)
+tcache_dalloc_small(tcache_t *tcache, void *ptr, size_t binind)
{
- arena_t *arena;
- arena_chunk_t *chunk;
- arena_run_t *run;
- arena_bin_t *bin;
tcache_bin_t *tbin;
tcache_bin_info_t *tbin_info;
- size_t pageind, binind;
- arena_chunk_map_t *mapelm;
-
- assert(arena_salloc(ptr) <= small_maxclass);
-
- chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
- arena = chunk->arena;
- pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT;
- mapelm = &chunk->map[pageind-map_bias];
- run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind -
- (mapelm->bits >> PAGE_SHIFT)) << PAGE_SHIFT));
- dassert(run->magic == ARENA_RUN_MAGIC);
- bin = run->bin;
- binind = ((uintptr_t)bin - (uintptr_t)&arena->bins) /
- sizeof(arena_bin_t);
- assert(binind < nbins);
-
-#ifdef JEMALLOC_FILL
- if (opt_junk)
- memset(ptr, 0x5a, arena_bin_info[binind].reg_size);
-#endif
+
+ assert(tcache_salloc(ptr) <= SMALL_MAXCLASS);
+
+ if (config_fill && opt_junk)
+ arena_dalloc_junk_small(ptr, &arena_bin_info[binind]);
tbin = &tcache->tbins[binind];
tbin_info = &tcache_bin_info[binind];
if (tbin->ncached == tbin_info->ncached_max) {
tcache_bin_flush_small(tbin, binind, (tbin_info->ncached_max >>
- 1)
-#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
- , tcache
-#endif
- );
+ 1), tcache);
}
assert(tbin->ncached < tbin_info->ncached_max);
tbin->avail[tbin->ncached] = ptr;
@@ -388,35 +409,24 @@ tcache_dalloc_small(tcache_t *tcache, void *ptr)
JEMALLOC_INLINE void
tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size)
{
- arena_t *arena;
- arena_chunk_t *chunk;
- size_t pageind, binind;
+ size_t binind;
tcache_bin_t *tbin;
tcache_bin_info_t *tbin_info;
assert((size & PAGE_MASK) == 0);
- assert(arena_salloc(ptr) > small_maxclass);
- assert(arena_salloc(ptr) <= tcache_maxclass);
+ assert(tcache_salloc(ptr) > SMALL_MAXCLASS);
+ assert(tcache_salloc(ptr) <= tcache_maxclass);
- chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
- arena = chunk->arena;
- pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT;
- binind = nbins + (size >> PAGE_SHIFT) - 1;
+ binind = NBINS + (size >> LG_PAGE) - 1;
-#ifdef JEMALLOC_FILL
- if (opt_junk)
+ if (config_fill && opt_junk)
memset(ptr, 0x5a, size);
-#endif
tbin = &tcache->tbins[binind];
tbin_info = &tcache_bin_info[binind];
if (tbin->ncached == tbin_info->ncached_max) {
tcache_bin_flush_large(tbin, binind, (tbin_info->ncached_max >>
- 1)
-#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
- , tcache
-#endif
- );
+ 1), tcache);
}
assert(tbin->ncached < tbin_info->ncached_max);
tbin->avail[tbin->ncached] = ptr;
@@ -428,4 +438,3 @@ tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size)
#endif /* JEMALLOC_H_INLINES */
/******************************************************************************/
-#endif /* JEMALLOC_TCACHE */