tcache.c revision dafde14e08ddfda747aabb2045b350848b601b2e
1#define JEMALLOC_TCACHE_C_ 2#include "jemalloc/internal/jemalloc_internal.h" 3#ifdef JEMALLOC_TCACHE 4/******************************************************************************/ 5/* Data. */ 6 7bool opt_tcache = true; 8ssize_t opt_lg_tcache_maxclass = LG_TCACHE_MAXCLASS_DEFAULT; 9ssize_t opt_lg_tcache_gc_sweep = LG_TCACHE_GC_SWEEP_DEFAULT; 10 11/* Map of thread-specific caches. */ 12__thread tcache_t *tcache_tls JEMALLOC_ATTR(tls_model("initial-exec")); 13 14/* 15 * Same contents as tcache, but initialized such that the TSD destructor is 16 * called when a thread exits, so that the cache can be cleaned up. 17 */ 18static pthread_key_t tcache_tsd; 19 20size_t nhbins; 21size_t tcache_maxclass; 22unsigned tcache_gc_incr; 23 24/******************************************************************************/ 25/* Function prototypes for non-inline static functions. */ 26 27static void tcache_thread_cleanup(void *arg); 28 29/******************************************************************************/ 30 31void * 32tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin, size_t binind) 33{ 34 void *ret; 35 36 arena_tcache_fill_small(tcache->arena, tbin, binind 37#ifdef JEMALLOC_PROF 38 , tcache->prof_accumbytes 39#endif 40 ); 41#ifdef JEMALLOC_PROF 42 tcache->prof_accumbytes = 0; 43#endif 44 ret = tcache_alloc_easy(tbin); 45 46 return (ret); 47} 48 49void 50tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem 51#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) 52 , tcache_t *tcache 53#endif 54 ) 55{ 56 void *flush, *deferred, *ptr; 57 unsigned i, nflush, ndeferred; 58 59 assert(binind < nbins); 60 assert(rem <= tbin->ncached); 61 62 for (flush = tbin->avail, nflush = tbin->ncached - rem; flush != NULL; 63 flush = deferred, nflush = ndeferred) { 64 /* Lock the arena bin associated with the first object. */ 65 arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(flush); 66 arena_t *arena = chunk->arena; 67 arena_bin_t *bin = &arena->bins[binind]; 68 69#ifdef JEMALLOC_PROF 70 if (arena == tcache->arena) { 71 malloc_mutex_lock(&arena->lock); 72 arena_prof_accum(arena, tcache->prof_accumbytes); 73 malloc_mutex_unlock(&arena->lock); 74 tcache->prof_accumbytes = 0; 75 } 76#endif 77 78 malloc_mutex_lock(&bin->lock); 79#ifdef JEMALLOC_STATS 80 if (arena == tcache->arena) { 81 bin->stats.nflushes++; 82 bin->stats.nrequests += tbin->tstats.nrequests; 83 tbin->tstats.nrequests = 0; 84 } 85#endif 86 deferred = NULL; 87 ndeferred = 0; 88 for (i = 0; i < nflush; i++) { 89 ptr = flush; 90 assert(ptr != NULL); 91 flush = *(void **)ptr; 92 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 93 if (chunk->arena == arena) { 94 size_t pageind = (((uintptr_t)ptr - 95 (uintptr_t)chunk) >> PAGE_SHIFT); 96 arena_chunk_map_t *mapelm = 97 &chunk->map[pageind]; 98 arena_dalloc_bin(arena, chunk, ptr, mapelm); 99 } else { 100 /* 101 * This object was allocated via a different 102 * arena bin than the one that is currently 103 * locked. Stash the object, so that it can be 104 * handled in a future pass. 105 */ 106 *(void **)ptr = deferred; 107 deferred = ptr; 108 ndeferred++; 109 } 110 } 111 malloc_mutex_unlock(&bin->lock); 112 113 if (flush != NULL) { 114 /* 115 * This was the first pass, and rem cached objects 116 * remain. 117 */ 118 tbin->avail = flush; 119 } 120 } 121 122 tbin->ncached = rem; 123 if (tbin->ncached < tbin->low_water) 124 tbin->low_water = tbin->ncached; 125} 126 127void 128tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem 129#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) 130 , tcache_t *tcache 131#endif 132 ) 133{ 134 void *flush, *deferred, *ptr; 135 unsigned i, nflush, ndeferred; 136 137 assert(binind < nhbins); 138 assert(rem <= tbin->ncached); 139 140 for (flush = tbin->avail, nflush = tbin->ncached - rem; flush != NULL; 141 flush = deferred, nflush = ndeferred) { 142 /* Lock the arena associated with the first object. */ 143 arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(flush); 144 arena_t *arena = chunk->arena; 145 146 malloc_mutex_lock(&arena->lock); 147#if (defined(JEMALLOC_PROF) || defined(JEMALLOC_STATS)) 148 if (arena == tcache->arena) { 149#endif 150#ifdef JEMALLOC_PROF 151 arena_prof_accum(arena, tcache->prof_accumbytes); 152 tcache->prof_accumbytes = 0; 153#endif 154#ifdef JEMALLOC_STATS 155 arena->stats.nrequests_large += tbin->tstats.nrequests; 156 arena->stats.lstats[binind - nbins].nrequests += 157 tbin->tstats.nrequests; 158 tbin->tstats.nrequests = 0; 159#endif 160#if (defined(JEMALLOC_PROF) || defined(JEMALLOC_STATS)) 161 } 162#endif 163 deferred = NULL; 164 ndeferred = 0; 165 for (i = 0; i < nflush; i++) { 166 ptr = flush; 167 assert(ptr != NULL); 168 flush = *(void **)ptr; 169 chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 170 if (chunk->arena == arena) 171 arena_dalloc_large(arena, chunk, ptr); 172 else { 173 /* 174 * This object was allocated via a different 175 * arena than the one that is currently locked. 176 * Stash the object, so that it can be handled 177 * in a future pass. 178 */ 179 *(void **)ptr = deferred; 180 deferred = ptr; 181 ndeferred++; 182 } 183 } 184 malloc_mutex_unlock(&arena->lock); 185 186 if (flush != NULL) { 187 /* 188 * This was the first pass, and rem cached objects 189 * remain. 190 */ 191 tbin->avail = flush; 192 } 193 } 194 195 tbin->ncached = rem; 196 if (tbin->ncached < tbin->low_water) 197 tbin->low_water = tbin->ncached; 198} 199 200tcache_t * 201tcache_create(arena_t *arena) 202{ 203 tcache_t *tcache; 204 size_t size; 205 unsigned i; 206 207 size = sizeof(tcache_t) + (sizeof(tcache_bin_t) * (nhbins - 1)); 208 /* 209 * Round up to the nearest multiple of the cacheline size, in order to 210 * avoid the possibility of false cacheline sharing. 211 * 212 * That this works relies on the same logic as in ipalloc(). 213 */ 214 size = (size + CACHELINE_MASK) & (-CACHELINE); 215 216 if (size <= small_maxclass) 217 tcache = (tcache_t *)arena_malloc_small(arena, size, true); 218 else 219 tcache = (tcache_t *)icalloc(size); 220 221 if (tcache == NULL) 222 return (NULL); 223 224#ifdef JEMALLOC_STATS 225 /* Link into list of extant tcaches. */ 226 malloc_mutex_lock(&arena->lock); 227 ql_elm_new(tcache, link); 228 ql_tail_insert(&arena->tcache_ql, tcache, link); 229 malloc_mutex_unlock(&arena->lock); 230#endif 231 232 tcache->arena = arena; 233 assert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0); 234 for (i = 0; i < nbins; i++) { 235 if ((arena->bins[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MAX) { 236 tcache->tbins[i].ncached_max = (arena->bins[i].nregs << 237 1); 238 } else 239 tcache->tbins[i].ncached_max = TCACHE_NSLOTS_SMALL_MAX; 240 } 241 for (; i < nhbins; i++) 242 tcache->tbins[i].ncached_max = TCACHE_NSLOTS_LARGE; 243 244 tcache_tls = tcache; 245 pthread_setspecific(tcache_tsd, tcache); 246 247 return (tcache); 248} 249 250void 251tcache_destroy(tcache_t *tcache) 252{ 253 unsigned i; 254 255#ifdef JEMALLOC_STATS 256 /* Unlink from list of extant tcaches. */ 257 malloc_mutex_lock(&tcache->arena->lock); 258 ql_remove(&tcache->arena->tcache_ql, tcache, link); 259 malloc_mutex_unlock(&tcache->arena->lock); 260 tcache_stats_merge(tcache, tcache->arena); 261#endif 262 263 for (i = 0; i < nbins; i++) { 264 tcache_bin_t *tbin = &tcache->tbins[i]; 265 tcache_bin_flush_small(tbin, i, 0 266#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) 267 , tcache 268#endif 269 ); 270 271#ifdef JEMALLOC_STATS 272 if (tbin->tstats.nrequests != 0) { 273 arena_t *arena = tcache->arena; 274 arena_bin_t *bin = &arena->bins[i]; 275 malloc_mutex_lock(&bin->lock); 276 bin->stats.nrequests += tbin->tstats.nrequests; 277 malloc_mutex_unlock(&bin->lock); 278 } 279#endif 280 } 281 282 for (; i < nhbins; i++) { 283 tcache_bin_t *tbin = &tcache->tbins[i]; 284 tcache_bin_flush_large(tbin, i, 0 285#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF)) 286 , tcache 287#endif 288 ); 289 290#ifdef JEMALLOC_STATS 291 if (tbin->tstats.nrequests != 0) { 292 arena_t *arena = tcache->arena; 293 malloc_mutex_lock(&arena->lock); 294 arena->stats.nrequests_large += tbin->tstats.nrequests; 295 arena->stats.lstats[i - nbins].nrequests += 296 tbin->tstats.nrequests; 297 malloc_mutex_unlock(&arena->lock); 298 } 299#endif 300 } 301 302#ifdef JEMALLOC_PROF 303 if (tcache->prof_accumbytes > 0) { 304 malloc_mutex_lock(&tcache->arena->lock); 305 arena_prof_accum(tcache->arena, tcache->prof_accumbytes); 306 malloc_mutex_unlock(&tcache->arena->lock); 307 } 308#endif 309 310 if (arena_salloc(tcache) <= small_maxclass) { 311 arena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache); 312 arena_t *arena = chunk->arena; 313 size_t pageind = (((uintptr_t)tcache - (uintptr_t)chunk) >> 314 PAGE_SHIFT); 315 arena_chunk_map_t *mapelm = &chunk->map[pageind]; 316 arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + 317 (uintptr_t)((pageind - ((mapelm->bits & CHUNK_MAP_PG_MASK) 318 >> CHUNK_MAP_PG_SHIFT)) << PAGE_SHIFT)); 319 arena_bin_t *bin = run->bin; 320 321 malloc_mutex_lock(&bin->lock); 322 arena_dalloc_bin(arena, chunk, tcache, mapelm); 323 malloc_mutex_unlock(&bin->lock); 324 } else 325 idalloc(tcache); 326} 327 328static void 329tcache_thread_cleanup(void *arg) 330{ 331 tcache_t *tcache = (tcache_t *)arg; 332 333 assert(tcache == tcache_tls); 334 if (tcache != NULL) { 335 assert(tcache != (void *)(uintptr_t)1); 336 tcache_destroy(tcache); 337 tcache_tls = (void *)(uintptr_t)1; 338 } 339} 340 341#ifdef JEMALLOC_STATS 342void 343tcache_stats_merge(tcache_t *tcache, arena_t *arena) 344{ 345 unsigned i; 346 347 /* Merge and reset tcache stats. */ 348 for (i = 0; i < nbins; i++) { 349 arena_bin_t *bin = &arena->bins[i]; 350 tcache_bin_t *tbin = &tcache->tbins[i]; 351 malloc_mutex_lock(&bin->lock); 352 bin->stats.nrequests += tbin->tstats.nrequests; 353 malloc_mutex_unlock(&bin->lock); 354 tbin->tstats.nrequests = 0; 355 } 356 357 for (; i < nhbins; i++) { 358 malloc_large_stats_t *lstats = &arena->stats.lstats[i - nbins]; 359 tcache_bin_t *tbin = &tcache->tbins[i]; 360 arena->stats.nrequests_large += tbin->tstats.nrequests; 361 lstats->nrequests += tbin->tstats.nrequests; 362 tbin->tstats.nrequests = 0; 363 } 364} 365#endif 366 367void 368tcache_boot(void) 369{ 370 371 if (opt_tcache) { 372 /* 373 * If necessary, clamp opt_lg_tcache_maxclass, now that 374 * small_maxclass and arena_maxclass are known. 375 */ 376 if (opt_lg_tcache_maxclass < 0 || (1U << 377 opt_lg_tcache_maxclass) < small_maxclass) 378 tcache_maxclass = small_maxclass; 379 else if ((1U << opt_lg_tcache_maxclass) > arena_maxclass) 380 tcache_maxclass = arena_maxclass; 381 else 382 tcache_maxclass = (1U << opt_lg_tcache_maxclass); 383 384 nhbins = nbins + (tcache_maxclass >> PAGE_SHIFT); 385 386 /* Compute incremental GC event threshold. */ 387 if (opt_lg_tcache_gc_sweep >= 0) { 388 tcache_gc_incr = ((1U << opt_lg_tcache_gc_sweep) / 389 nbins) + (((1U << opt_lg_tcache_gc_sweep) % nbins == 390 0) ? 0 : 1); 391 } else 392 tcache_gc_incr = 0; 393 394 if (pthread_key_create(&tcache_tsd, tcache_thread_cleanup) != 395 0) { 396 malloc_write( 397 "<jemalloc>: Error in pthread_key_create()\n"); 398 abort(); 399 } 400 } 401} 402/******************************************************************************/ 403#endif /* JEMALLOC_TCACHE */ 404