1a4bd5210SJason Evans #define JEMALLOC_TCACHE_C_ 2a4bd5210SJason Evans #include "jemalloc/internal/jemalloc_internal.h" 3a4bd5210SJason Evans 4a4bd5210SJason Evans /******************************************************************************/ 5a4bd5210SJason Evans /* Data. */ 6a4bd5210SJason Evans 7a4bd5210SJason Evans bool opt_tcache = true; 8a4bd5210SJason Evans ssize_t opt_lg_tcache_max = LG_TCACHE_MAXCLASS_DEFAULT; 9a4bd5210SJason Evans 10a4bd5210SJason Evans tcache_bin_info_t *tcache_bin_info; 11a4bd5210SJason Evans static unsigned stack_nelms; /* Total stack elms per tcache. */ 12a4bd5210SJason Evans 13df0d881dSJason Evans unsigned nhbins; 14a4bd5210SJason Evans size_t tcache_maxclass; 15a4bd5210SJason Evans 16d0e79aa3SJason Evans tcaches_t *tcaches; 17d0e79aa3SJason Evans 18d0e79aa3SJason Evans /* Index of first element within tcaches that has never been used. */ 19d0e79aa3SJason Evans static unsigned tcaches_past; 20d0e79aa3SJason Evans 21d0e79aa3SJason Evans /* Head of singly linked list tracking available tcaches elements. */ 22d0e79aa3SJason Evans static tcaches_t *tcaches_avail; 23d0e79aa3SJason Evans 24*8244f2aaSJason Evans /* Protects tcaches{,_past,_avail}. */ 25*8244f2aaSJason Evans static malloc_mutex_t tcaches_mtx; 26*8244f2aaSJason Evans 27a4bd5210SJason Evans /******************************************************************************/ 28a4bd5210SJason Evans 291f0a49e8SJason Evans size_t 301f0a49e8SJason Evans tcache_salloc(tsdn_t *tsdn, const void *ptr) 318ed34ab0SJason Evans { 328ed34ab0SJason Evans 331f0a49e8SJason Evans return (arena_salloc(tsdn, ptr, false)); 348ed34ab0SJason Evans } 358ed34ab0SJason Evans 36e722f8f8SJason Evans void 37d0e79aa3SJason Evans tcache_event_hard(tsd_t *tsd, tcache_t *tcache) 38e722f8f8SJason Evans { 39536b3538SJason Evans szind_t binind = tcache->next_gc_bin; 40e722f8f8SJason Evans tcache_bin_t *tbin = &tcache->tbins[binind]; 41e722f8f8SJason Evans tcache_bin_info_t *tbin_info = &tcache_bin_info[binind]; 42e722f8f8SJason Evans 43e722f8f8SJason Evans if (tbin->low_water > 0) { 44e722f8f8SJason Evans /* 45e722f8f8SJason Evans * Flush (ceiling) 3/4 of the objects below the low water mark. 46e722f8f8SJason Evans */ 47e722f8f8SJason Evans if (binind < NBINS) { 48d0e79aa3SJason Evans tcache_bin_flush_small(tsd, tcache, tbin, binind, 49d0e79aa3SJason Evans tbin->ncached - tbin->low_water + (tbin->low_water 50d0e79aa3SJason Evans >> 2)); 51e722f8f8SJason Evans } else { 52d0e79aa3SJason Evans tcache_bin_flush_large(tsd, tbin, binind, tbin->ncached 53d0e79aa3SJason Evans - tbin->low_water + (tbin->low_water >> 2), tcache); 54e722f8f8SJason Evans } 55e722f8f8SJason Evans /* 56e722f8f8SJason Evans * Reduce fill count by 2X. Limit lg_fill_div such that the 57e722f8f8SJason Evans * fill count is always at least 1. 58e722f8f8SJason Evans */ 59e722f8f8SJason Evans if ((tbin_info->ncached_max >> (tbin->lg_fill_div+1)) >= 1) 60e722f8f8SJason Evans tbin->lg_fill_div++; 61e722f8f8SJason Evans } else if (tbin->low_water < 0) { 62e722f8f8SJason Evans /* 63e722f8f8SJason Evans * Increase fill count by 2X. Make sure lg_fill_div stays 64e722f8f8SJason Evans * greater than 0. 65e722f8f8SJason Evans */ 66e722f8f8SJason Evans if (tbin->lg_fill_div > 1) 67e722f8f8SJason Evans tbin->lg_fill_div--; 68e722f8f8SJason Evans } 69e722f8f8SJason Evans tbin->low_water = tbin->ncached; 70e722f8f8SJason Evans 71e722f8f8SJason Evans tcache->next_gc_bin++; 72e722f8f8SJason Evans if (tcache->next_gc_bin == nhbins) 73e722f8f8SJason Evans tcache->next_gc_bin = 0; 74e722f8f8SJason Evans } 75e722f8f8SJason Evans 76a4bd5210SJason Evans void * 771f0a49e8SJason Evans tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache, 78df0d881dSJason Evans tcache_bin_t *tbin, szind_t binind, bool *tcache_success) 79a4bd5210SJason Evans { 80a4bd5210SJason Evans void *ret; 81a4bd5210SJason Evans 821f0a49e8SJason Evans arena_tcache_fill_small(tsdn, arena, tbin, binind, config_prof ? 83d0e79aa3SJason Evans tcache->prof_accumbytes : 0); 84a4bd5210SJason Evans if (config_prof) 85a4bd5210SJason Evans tcache->prof_accumbytes = 0; 86df0d881dSJason Evans ret = tcache_alloc_easy(tbin, tcache_success); 87a4bd5210SJason Evans 88a4bd5210SJason Evans return (ret); 89a4bd5210SJason Evans } 90a4bd5210SJason Evans 91a4bd5210SJason Evans void 92d0e79aa3SJason Evans tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin, 93536b3538SJason Evans szind_t binind, unsigned rem) 94a4bd5210SJason Evans { 95d0e79aa3SJason Evans arena_t *arena; 96a4bd5210SJason Evans void *ptr; 97a4bd5210SJason Evans unsigned i, nflush, ndeferred; 98a4bd5210SJason Evans bool merged_stats = false; 99a4bd5210SJason Evans 100a4bd5210SJason Evans assert(binind < NBINS); 101a4bd5210SJason Evans assert(rem <= tbin->ncached); 102a4bd5210SJason Evans 103d0e79aa3SJason Evans arena = arena_choose(tsd, NULL); 104d0e79aa3SJason Evans assert(arena != NULL); 105a4bd5210SJason Evans for (nflush = tbin->ncached - rem; nflush > 0; nflush = ndeferred) { 106a4bd5210SJason Evans /* Lock the arena bin associated with the first object. */ 107a4bd5210SJason Evans arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE( 108df0d881dSJason Evans *(tbin->avail - 1)); 109d0e79aa3SJason Evans arena_t *bin_arena = extent_node_arena_get(&chunk->node); 110d0e79aa3SJason Evans arena_bin_t *bin = &bin_arena->bins[binind]; 111a4bd5210SJason Evans 112d0e79aa3SJason Evans if (config_prof && bin_arena == arena) { 1131f0a49e8SJason Evans if (arena_prof_accum(tsd_tsdn(tsd), arena, 1141f0a49e8SJason Evans tcache->prof_accumbytes)) 1151f0a49e8SJason Evans prof_idump(tsd_tsdn(tsd)); 116a4bd5210SJason Evans tcache->prof_accumbytes = 0; 117a4bd5210SJason Evans } 118a4bd5210SJason Evans 1191f0a49e8SJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock); 120d0e79aa3SJason Evans if (config_stats && bin_arena == arena) { 121d0e79aa3SJason Evans assert(!merged_stats); 122a4bd5210SJason Evans merged_stats = true; 123a4bd5210SJason Evans bin->stats.nflushes++; 124a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 125a4bd5210SJason Evans tbin->tstats.nrequests = 0; 126a4bd5210SJason Evans } 127a4bd5210SJason Evans ndeferred = 0; 128a4bd5210SJason Evans for (i = 0; i < nflush; i++) { 129df0d881dSJason Evans ptr = *(tbin->avail - 1 - i); 130a4bd5210SJason Evans assert(ptr != NULL); 131a4bd5210SJason Evans chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 132d0e79aa3SJason Evans if (extent_node_arena_get(&chunk->node) == bin_arena) { 133a4bd5210SJason Evans size_t pageind = ((uintptr_t)ptr - 134a4bd5210SJason Evans (uintptr_t)chunk) >> LG_PAGE; 135d0e79aa3SJason Evans arena_chunk_map_bits_t *bitselm = 1361f0a49e8SJason Evans arena_bitselm_get_mutable(chunk, pageind); 1371f0a49e8SJason Evans arena_dalloc_bin_junked_locked(tsd_tsdn(tsd), 1381f0a49e8SJason Evans bin_arena, chunk, ptr, bitselm); 139a4bd5210SJason Evans } else { 140a4bd5210SJason Evans /* 141a4bd5210SJason Evans * This object was allocated via a different 142a4bd5210SJason Evans * arena bin than the one that is currently 143a4bd5210SJason Evans * locked. Stash the object, so that it can be 144a4bd5210SJason Evans * handled in a future pass. 145a4bd5210SJason Evans */ 146df0d881dSJason Evans *(tbin->avail - 1 - ndeferred) = ptr; 147a4bd5210SJason Evans ndeferred++; 148a4bd5210SJason Evans } 149a4bd5210SJason Evans } 1501f0a49e8SJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock); 1511f0a49e8SJason Evans arena_decay_ticks(tsd_tsdn(tsd), bin_arena, nflush - ndeferred); 152a4bd5210SJason Evans } 153d0e79aa3SJason Evans if (config_stats && !merged_stats) { 154a4bd5210SJason Evans /* 155a4bd5210SJason Evans * The flush loop didn't happen to flush to this thread's 156a4bd5210SJason Evans * arena, so the stats didn't get merged. Manually do so now. 157a4bd5210SJason Evans */ 158d0e79aa3SJason Evans arena_bin_t *bin = &arena->bins[binind]; 1591f0a49e8SJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock); 160a4bd5210SJason Evans bin->stats.nflushes++; 161a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 162a4bd5210SJason Evans tbin->tstats.nrequests = 0; 1631f0a49e8SJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock); 164a4bd5210SJason Evans } 165a4bd5210SJason Evans 166df0d881dSJason Evans memmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem * 167df0d881dSJason Evans sizeof(void *)); 168a4bd5210SJason Evans tbin->ncached = rem; 169a4bd5210SJason Evans if ((int)tbin->ncached < tbin->low_water) 170a4bd5210SJason Evans tbin->low_water = tbin->ncached; 171a4bd5210SJason Evans } 172a4bd5210SJason Evans 173a4bd5210SJason Evans void 174536b3538SJason Evans tcache_bin_flush_large(tsd_t *tsd, tcache_bin_t *tbin, szind_t binind, 175d0e79aa3SJason Evans unsigned rem, tcache_t *tcache) 176a4bd5210SJason Evans { 177d0e79aa3SJason Evans arena_t *arena; 178a4bd5210SJason Evans void *ptr; 179a4bd5210SJason Evans unsigned i, nflush, ndeferred; 180a4bd5210SJason Evans bool merged_stats = false; 181a4bd5210SJason Evans 182a4bd5210SJason Evans assert(binind < nhbins); 183a4bd5210SJason Evans assert(rem <= tbin->ncached); 184a4bd5210SJason Evans 185d0e79aa3SJason Evans arena = arena_choose(tsd, NULL); 186d0e79aa3SJason Evans assert(arena != NULL); 187a4bd5210SJason Evans for (nflush = tbin->ncached - rem; nflush > 0; nflush = ndeferred) { 188a4bd5210SJason Evans /* Lock the arena associated with the first object. */ 189a4bd5210SJason Evans arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE( 190df0d881dSJason Evans *(tbin->avail - 1)); 191d0e79aa3SJason Evans arena_t *locked_arena = extent_node_arena_get(&chunk->node); 192f8ca2db1SJason Evans UNUSED bool idump; 193a4bd5210SJason Evans 194f8ca2db1SJason Evans if (config_prof) 195f8ca2db1SJason Evans idump = false; 1961f0a49e8SJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &locked_arena->lock); 197d0e79aa3SJason Evans if ((config_prof || config_stats) && locked_arena == arena) { 198a4bd5210SJason Evans if (config_prof) { 199f8ca2db1SJason Evans idump = arena_prof_accum_locked(arena, 200a4bd5210SJason Evans tcache->prof_accumbytes); 201a4bd5210SJason Evans tcache->prof_accumbytes = 0; 202a4bd5210SJason Evans } 203a4bd5210SJason Evans if (config_stats) { 204a4bd5210SJason Evans merged_stats = true; 205a4bd5210SJason Evans arena->stats.nrequests_large += 206a4bd5210SJason Evans tbin->tstats.nrequests; 207a4bd5210SJason Evans arena->stats.lstats[binind - NBINS].nrequests += 208a4bd5210SJason Evans tbin->tstats.nrequests; 209a4bd5210SJason Evans tbin->tstats.nrequests = 0; 210a4bd5210SJason Evans } 211a4bd5210SJason Evans } 212a4bd5210SJason Evans ndeferred = 0; 213a4bd5210SJason Evans for (i = 0; i < nflush; i++) { 214df0d881dSJason Evans ptr = *(tbin->avail - 1 - i); 215a4bd5210SJason Evans assert(ptr != NULL); 216a4bd5210SJason Evans chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); 217d0e79aa3SJason Evans if (extent_node_arena_get(&chunk->node) == 218d0e79aa3SJason Evans locked_arena) { 2191f0a49e8SJason Evans arena_dalloc_large_junked_locked(tsd_tsdn(tsd), 2201f0a49e8SJason Evans locked_arena, chunk, ptr); 221d0e79aa3SJason Evans } else { 222a4bd5210SJason Evans /* 223a4bd5210SJason Evans * This object was allocated via a different 224a4bd5210SJason Evans * arena than the one that is currently locked. 225a4bd5210SJason Evans * Stash the object, so that it can be handled 226a4bd5210SJason Evans * in a future pass. 227a4bd5210SJason Evans */ 228df0d881dSJason Evans *(tbin->avail - 1 - ndeferred) = ptr; 229a4bd5210SJason Evans ndeferred++; 230a4bd5210SJason Evans } 231a4bd5210SJason Evans } 2321f0a49e8SJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &locked_arena->lock); 233f8ca2db1SJason Evans if (config_prof && idump) 2341f0a49e8SJason Evans prof_idump(tsd_tsdn(tsd)); 2351f0a49e8SJason Evans arena_decay_ticks(tsd_tsdn(tsd), locked_arena, nflush - 2361f0a49e8SJason Evans ndeferred); 237a4bd5210SJason Evans } 238d0e79aa3SJason Evans if (config_stats && !merged_stats) { 239a4bd5210SJason Evans /* 240a4bd5210SJason Evans * The flush loop didn't happen to flush to this thread's 241a4bd5210SJason Evans * arena, so the stats didn't get merged. Manually do so now. 242a4bd5210SJason Evans */ 2431f0a49e8SJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &arena->lock); 244a4bd5210SJason Evans arena->stats.nrequests_large += tbin->tstats.nrequests; 245a4bd5210SJason Evans arena->stats.lstats[binind - NBINS].nrequests += 246a4bd5210SJason Evans tbin->tstats.nrequests; 247a4bd5210SJason Evans tbin->tstats.nrequests = 0; 2481f0a49e8SJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &arena->lock); 249a4bd5210SJason Evans } 250a4bd5210SJason Evans 251df0d881dSJason Evans memmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem * 252df0d881dSJason Evans sizeof(void *)); 253a4bd5210SJason Evans tbin->ncached = rem; 254a4bd5210SJason Evans if ((int)tbin->ncached < tbin->low_water) 255a4bd5210SJason Evans tbin->low_water = tbin->ncached; 256a4bd5210SJason Evans } 257a4bd5210SJason Evans 2581f0a49e8SJason Evans static void 2591f0a49e8SJason Evans tcache_arena_associate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) 260a4bd5210SJason Evans { 261a4bd5210SJason Evans 262a4bd5210SJason Evans if (config_stats) { 263a4bd5210SJason Evans /* Link into list of extant tcaches. */ 2641f0a49e8SJason Evans malloc_mutex_lock(tsdn, &arena->lock); 265a4bd5210SJason Evans ql_elm_new(tcache, link); 266a4bd5210SJason Evans ql_tail_insert(&arena->tcache_ql, tcache, link); 2671f0a49e8SJason Evans malloc_mutex_unlock(tsdn, &arena->lock); 268a4bd5210SJason Evans } 269a4bd5210SJason Evans } 270a4bd5210SJason Evans 2711f0a49e8SJason Evans static void 2721f0a49e8SJason Evans tcache_arena_dissociate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) 273a4bd5210SJason Evans { 274a4bd5210SJason Evans 275a4bd5210SJason Evans if (config_stats) { 276a4bd5210SJason Evans /* Unlink from list of extant tcaches. */ 2771f0a49e8SJason Evans malloc_mutex_lock(tsdn, &arena->lock); 278d0e79aa3SJason Evans if (config_debug) { 279d0e79aa3SJason Evans bool in_ql = false; 280d0e79aa3SJason Evans tcache_t *iter; 281d0e79aa3SJason Evans ql_foreach(iter, &arena->tcache_ql, link) { 282d0e79aa3SJason Evans if (iter == tcache) { 283d0e79aa3SJason Evans in_ql = true; 284d0e79aa3SJason Evans break; 285d0e79aa3SJason Evans } 286d0e79aa3SJason Evans } 287d0e79aa3SJason Evans assert(in_ql); 288d0e79aa3SJason Evans } 289d0e79aa3SJason Evans ql_remove(&arena->tcache_ql, tcache, link); 2901f0a49e8SJason Evans tcache_stats_merge(tsdn, tcache, arena); 2911f0a49e8SJason Evans malloc_mutex_unlock(tsdn, &arena->lock); 292a4bd5210SJason Evans } 293a4bd5210SJason Evans } 294a4bd5210SJason Evans 2951f0a49e8SJason Evans void 2961f0a49e8SJason Evans tcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache, arena_t *oldarena, 2971f0a49e8SJason Evans arena_t *newarena) 2981f0a49e8SJason Evans { 2991f0a49e8SJason Evans 3001f0a49e8SJason Evans tcache_arena_dissociate(tsdn, tcache, oldarena); 3011f0a49e8SJason Evans tcache_arena_associate(tsdn, tcache, newarena); 3021f0a49e8SJason Evans } 3031f0a49e8SJason Evans 304a4bd5210SJason Evans tcache_t * 305d0e79aa3SJason Evans tcache_get_hard(tsd_t *tsd) 306d0e79aa3SJason Evans { 307d0e79aa3SJason Evans arena_t *arena; 308d0e79aa3SJason Evans 309d0e79aa3SJason Evans if (!tcache_enabled_get()) { 310d0e79aa3SJason Evans if (tsd_nominal(tsd)) 311d0e79aa3SJason Evans tcache_enabled_set(false); /* Memoize. */ 312d0e79aa3SJason Evans return (NULL); 313d0e79aa3SJason Evans } 314d0e79aa3SJason Evans arena = arena_choose(tsd, NULL); 315d0e79aa3SJason Evans if (unlikely(arena == NULL)) 316d0e79aa3SJason Evans return (NULL); 3171f0a49e8SJason Evans return (tcache_create(tsd_tsdn(tsd), arena)); 318d0e79aa3SJason Evans } 319d0e79aa3SJason Evans 320d0e79aa3SJason Evans tcache_t * 3211f0a49e8SJason Evans tcache_create(tsdn_t *tsdn, arena_t *arena) 322a4bd5210SJason Evans { 323a4bd5210SJason Evans tcache_t *tcache; 324a4bd5210SJason Evans size_t size, stack_offset; 325a4bd5210SJason Evans unsigned i; 326a4bd5210SJason Evans 327a4bd5210SJason Evans size = offsetof(tcache_t, tbins) + (sizeof(tcache_bin_t) * nhbins); 328a4bd5210SJason Evans /* Naturally align the pointer stacks. */ 329a4bd5210SJason Evans size = PTR_CEILING(size); 330a4bd5210SJason Evans stack_offset = size; 331a4bd5210SJason Evans size += stack_nelms * sizeof(void *); 332d0e79aa3SJason Evans /* Avoid false cacheline sharing. */ 333d0e79aa3SJason Evans size = sa2u(size, CACHELINE); 334a4bd5210SJason Evans 3351f0a49e8SJason Evans tcache = ipallocztm(tsdn, size, CACHELINE, true, NULL, true, 3361f0a49e8SJason Evans arena_get(TSDN_NULL, 0, true)); 337a4bd5210SJason Evans if (tcache == NULL) 338a4bd5210SJason Evans return (NULL); 339a4bd5210SJason Evans 3401f0a49e8SJason Evans tcache_arena_associate(tsdn, tcache, arena); 341a4bd5210SJason Evans 342df0d881dSJason Evans ticker_init(&tcache->gc_ticker, TCACHE_GC_INCR); 343df0d881dSJason Evans 344a4bd5210SJason Evans assert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0); 345a4bd5210SJason Evans for (i = 0; i < nhbins; i++) { 346a4bd5210SJason Evans tcache->tbins[i].lg_fill_div = 1; 347df0d881dSJason Evans stack_offset += tcache_bin_info[i].ncached_max * sizeof(void *); 348df0d881dSJason Evans /* 349df0d881dSJason Evans * avail points past the available space. Allocations will 350df0d881dSJason Evans * access the slots toward higher addresses (for the benefit of 351df0d881dSJason Evans * prefetch). 352df0d881dSJason Evans */ 353a4bd5210SJason Evans tcache->tbins[i].avail = (void **)((uintptr_t)tcache + 354a4bd5210SJason Evans (uintptr_t)stack_offset); 355a4bd5210SJason Evans } 356a4bd5210SJason Evans 357a4bd5210SJason Evans return (tcache); 358a4bd5210SJason Evans } 359a4bd5210SJason Evans 360d0e79aa3SJason Evans static void 361d0e79aa3SJason Evans tcache_destroy(tsd_t *tsd, tcache_t *tcache) 362a4bd5210SJason Evans { 363d0e79aa3SJason Evans arena_t *arena; 364a4bd5210SJason Evans unsigned i; 365a4bd5210SJason Evans 366d0e79aa3SJason Evans arena = arena_choose(tsd, NULL); 3671f0a49e8SJason Evans tcache_arena_dissociate(tsd_tsdn(tsd), tcache, arena); 368a4bd5210SJason Evans 369a4bd5210SJason Evans for (i = 0; i < NBINS; i++) { 370a4bd5210SJason Evans tcache_bin_t *tbin = &tcache->tbins[i]; 371d0e79aa3SJason Evans tcache_bin_flush_small(tsd, tcache, tbin, i, 0); 372a4bd5210SJason Evans 373a4bd5210SJason Evans if (config_stats && tbin->tstats.nrequests != 0) { 374a4bd5210SJason Evans arena_bin_t *bin = &arena->bins[i]; 3751f0a49e8SJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock); 376a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 3771f0a49e8SJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock); 378a4bd5210SJason Evans } 379a4bd5210SJason Evans } 380a4bd5210SJason Evans 381a4bd5210SJason Evans for (; i < nhbins; i++) { 382a4bd5210SJason Evans tcache_bin_t *tbin = &tcache->tbins[i]; 383d0e79aa3SJason Evans tcache_bin_flush_large(tsd, tbin, i, 0, tcache); 384a4bd5210SJason Evans 385a4bd5210SJason Evans if (config_stats && tbin->tstats.nrequests != 0) { 3861f0a49e8SJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &arena->lock); 387a4bd5210SJason Evans arena->stats.nrequests_large += tbin->tstats.nrequests; 388a4bd5210SJason Evans arena->stats.lstats[i - NBINS].nrequests += 389a4bd5210SJason Evans tbin->tstats.nrequests; 3901f0a49e8SJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &arena->lock); 391a4bd5210SJason Evans } 392a4bd5210SJason Evans } 393a4bd5210SJason Evans 394f8ca2db1SJason Evans if (config_prof && tcache->prof_accumbytes > 0 && 3951f0a49e8SJason Evans arena_prof_accum(tsd_tsdn(tsd), arena, tcache->prof_accumbytes)) 3961f0a49e8SJason Evans prof_idump(tsd_tsdn(tsd)); 397a4bd5210SJason Evans 3981f0a49e8SJason Evans idalloctm(tsd_tsdn(tsd), tcache, NULL, true, true); 399a4bd5210SJason Evans } 400a4bd5210SJason Evans 401a4bd5210SJason Evans void 402d0e79aa3SJason Evans tcache_cleanup(tsd_t *tsd) 403a4bd5210SJason Evans { 404d0e79aa3SJason Evans tcache_t *tcache; 405a4bd5210SJason Evans 406d0e79aa3SJason Evans if (!config_tcache) 407d0e79aa3SJason Evans return; 408d0e79aa3SJason Evans 409d0e79aa3SJason Evans if ((tcache = tsd_tcache_get(tsd)) != NULL) { 410d0e79aa3SJason Evans tcache_destroy(tsd, tcache); 411d0e79aa3SJason Evans tsd_tcache_set(tsd, NULL); 412a4bd5210SJason Evans } 413a4bd5210SJason Evans } 414a4bd5210SJason Evans 415d0e79aa3SJason Evans void 416d0e79aa3SJason Evans tcache_enabled_cleanup(tsd_t *tsd) 417d0e79aa3SJason Evans { 418d0e79aa3SJason Evans 419d0e79aa3SJason Evans /* Do nothing. */ 420d0e79aa3SJason Evans } 421d0e79aa3SJason Evans 422a4bd5210SJason Evans void 4231f0a49e8SJason Evans tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) 424a4bd5210SJason Evans { 425a4bd5210SJason Evans unsigned i; 426a4bd5210SJason Evans 427f921d10fSJason Evans cassert(config_stats); 428f921d10fSJason Evans 4291f0a49e8SJason Evans malloc_mutex_assert_owner(tsdn, &arena->lock); 4301f0a49e8SJason Evans 431a4bd5210SJason Evans /* Merge and reset tcache stats. */ 432a4bd5210SJason Evans for (i = 0; i < NBINS; i++) { 433a4bd5210SJason Evans arena_bin_t *bin = &arena->bins[i]; 434a4bd5210SJason Evans tcache_bin_t *tbin = &tcache->tbins[i]; 4351f0a49e8SJason Evans malloc_mutex_lock(tsdn, &bin->lock); 436a4bd5210SJason Evans bin->stats.nrequests += tbin->tstats.nrequests; 4371f0a49e8SJason Evans malloc_mutex_unlock(tsdn, &bin->lock); 438a4bd5210SJason Evans tbin->tstats.nrequests = 0; 439a4bd5210SJason Evans } 440a4bd5210SJason Evans 441a4bd5210SJason Evans for (; i < nhbins; i++) { 442a4bd5210SJason Evans malloc_large_stats_t *lstats = &arena->stats.lstats[i - NBINS]; 443a4bd5210SJason Evans tcache_bin_t *tbin = &tcache->tbins[i]; 444a4bd5210SJason Evans arena->stats.nrequests_large += tbin->tstats.nrequests; 445a4bd5210SJason Evans lstats->nrequests += tbin->tstats.nrequests; 446a4bd5210SJason Evans tbin->tstats.nrequests = 0; 447a4bd5210SJason Evans } 448a4bd5210SJason Evans } 449a4bd5210SJason Evans 450*8244f2aaSJason Evans static bool 451*8244f2aaSJason Evans tcaches_create_prep(tsd_t *tsd) { 452*8244f2aaSJason Evans bool err; 453*8244f2aaSJason Evans 454*8244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 455d0e79aa3SJason Evans 456d0e79aa3SJason Evans if (tcaches == NULL) { 457bde95144SJason Evans tcaches = base_alloc(tsd_tsdn(tsd), sizeof(tcache_t *) * 458d0e79aa3SJason Evans (MALLOCX_TCACHE_MAX+1)); 459*8244f2aaSJason Evans if (tcaches == NULL) { 460*8244f2aaSJason Evans err = true; 461*8244f2aaSJason Evans goto label_return; 462*8244f2aaSJason Evans } 463d0e79aa3SJason Evans } 464d0e79aa3SJason Evans 465*8244f2aaSJason Evans if (tcaches_avail == NULL && tcaches_past > MALLOCX_TCACHE_MAX) { 466*8244f2aaSJason Evans err = true; 467*8244f2aaSJason Evans goto label_return; 468*8244f2aaSJason Evans } 469d0e79aa3SJason Evans 470*8244f2aaSJason Evans err = false; 471*8244f2aaSJason Evans label_return: 472*8244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 473*8244f2aaSJason Evans return err; 474*8244f2aaSJason Evans } 475*8244f2aaSJason Evans 476*8244f2aaSJason Evans bool 477*8244f2aaSJason Evans tcaches_create(tsd_t *tsd, unsigned *r_ind) { 478*8244f2aaSJason Evans bool err; 479*8244f2aaSJason Evans arena_t *arena; 480*8244f2aaSJason Evans tcache_t *tcache; 481*8244f2aaSJason Evans tcaches_t *elm; 482*8244f2aaSJason Evans 483*8244f2aaSJason Evans if (tcaches_create_prep(tsd)) { 484*8244f2aaSJason Evans err = true; 485*8244f2aaSJason Evans goto label_return; 486*8244f2aaSJason Evans } 487*8244f2aaSJason Evans 488*8244f2aaSJason Evans arena = arena_ichoose(tsd, NULL); 489*8244f2aaSJason Evans if (unlikely(arena == NULL)) { 490*8244f2aaSJason Evans err = true; 491*8244f2aaSJason Evans goto label_return; 492*8244f2aaSJason Evans } 493*8244f2aaSJason Evans tcache = tcache_create(tsd_tsdn(tsd), arena); 494*8244f2aaSJason Evans if (tcache == NULL) { 495*8244f2aaSJason Evans err = true; 496*8244f2aaSJason Evans goto label_return; 497*8244f2aaSJason Evans } 498*8244f2aaSJason Evans 499*8244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 500d0e79aa3SJason Evans if (tcaches_avail != NULL) { 501d0e79aa3SJason Evans elm = tcaches_avail; 502d0e79aa3SJason Evans tcaches_avail = tcaches_avail->next; 503d0e79aa3SJason Evans elm->tcache = tcache; 504df0d881dSJason Evans *r_ind = (unsigned)(elm - tcaches); 505d0e79aa3SJason Evans } else { 506d0e79aa3SJason Evans elm = &tcaches[tcaches_past]; 507d0e79aa3SJason Evans elm->tcache = tcache; 508d0e79aa3SJason Evans *r_ind = tcaches_past; 509d0e79aa3SJason Evans tcaches_past++; 510d0e79aa3SJason Evans } 511*8244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 512d0e79aa3SJason Evans 513*8244f2aaSJason Evans err = false; 514*8244f2aaSJason Evans label_return: 515*8244f2aaSJason Evans malloc_mutex_assert_not_owner(tsd_tsdn(tsd), &tcaches_mtx); 516*8244f2aaSJason Evans return err; 517d0e79aa3SJason Evans } 518d0e79aa3SJason Evans 519d0e79aa3SJason Evans static void 520*8244f2aaSJason Evans tcaches_elm_flush(tsd_t *tsd, tcaches_t *elm) { 521*8244f2aaSJason Evans malloc_mutex_assert_owner(tsd_tsdn(tsd), &tcaches_mtx); 522d0e79aa3SJason Evans 523*8244f2aaSJason Evans if (elm->tcache == NULL) { 524d0e79aa3SJason Evans return; 525*8244f2aaSJason Evans } 526d0e79aa3SJason Evans tcache_destroy(tsd, elm->tcache); 527d0e79aa3SJason Evans elm->tcache = NULL; 528d0e79aa3SJason Evans } 529d0e79aa3SJason Evans 530d0e79aa3SJason Evans void 531*8244f2aaSJason Evans tcaches_flush(tsd_t *tsd, unsigned ind) { 532*8244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 533d0e79aa3SJason Evans tcaches_elm_flush(tsd, &tcaches[ind]); 534*8244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 535d0e79aa3SJason Evans } 536d0e79aa3SJason Evans 537d0e79aa3SJason Evans void 538*8244f2aaSJason Evans tcaches_destroy(tsd_t *tsd, unsigned ind) { 539*8244f2aaSJason Evans tcaches_t *elm; 540*8244f2aaSJason Evans 541*8244f2aaSJason Evans malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); 542*8244f2aaSJason Evans elm = &tcaches[ind]; 543d0e79aa3SJason Evans tcaches_elm_flush(tsd, elm); 544d0e79aa3SJason Evans elm->next = tcaches_avail; 545d0e79aa3SJason Evans tcaches_avail = elm; 546*8244f2aaSJason Evans malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx); 547d0e79aa3SJason Evans } 548d0e79aa3SJason Evans 549d0e79aa3SJason Evans bool 550*8244f2aaSJason Evans tcache_boot(tsdn_t *tsdn) { 551a4bd5210SJason Evans unsigned i; 552a4bd5210SJason Evans 553*8244f2aaSJason Evans cassert(config_tcache); 554*8244f2aaSJason Evans 555a4bd5210SJason Evans /* 556536b3538SJason Evans * If necessary, clamp opt_lg_tcache_max, now that large_maxclass is 557a4bd5210SJason Evans * known. 558a4bd5210SJason Evans */ 5597fa7f12fSJason Evans if (opt_lg_tcache_max < 0 || (ZU(1) << opt_lg_tcache_max) < SMALL_MAXCLASS) 560a4bd5210SJason Evans tcache_maxclass = SMALL_MAXCLASS; 5617fa7f12fSJason Evans else if ((ZU(1) << opt_lg_tcache_max) > large_maxclass) 562536b3538SJason Evans tcache_maxclass = large_maxclass; 563a4bd5210SJason Evans else 5647fa7f12fSJason Evans tcache_maxclass = (ZU(1) << opt_lg_tcache_max); 565a4bd5210SJason Evans 566*8244f2aaSJason Evans if (malloc_mutex_init(&tcaches_mtx, "tcaches", WITNESS_RANK_TCACHES)) { 567*8244f2aaSJason Evans return true; 568*8244f2aaSJason Evans } 569*8244f2aaSJason Evans 570d0e79aa3SJason Evans nhbins = size2index(tcache_maxclass) + 1; 571a4bd5210SJason Evans 572a4bd5210SJason Evans /* Initialize tcache_bin_info. */ 5731f0a49e8SJason Evans tcache_bin_info = (tcache_bin_info_t *)base_alloc(tsdn, nhbins * 574a4bd5210SJason Evans sizeof(tcache_bin_info_t)); 575a4bd5210SJason Evans if (tcache_bin_info == NULL) 576a4bd5210SJason Evans return (true); 577a4bd5210SJason Evans stack_nelms = 0; 578a4bd5210SJason Evans for (i = 0; i < NBINS; i++) { 579d0e79aa3SJason Evans if ((arena_bin_info[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MIN) { 580d0e79aa3SJason Evans tcache_bin_info[i].ncached_max = 581d0e79aa3SJason Evans TCACHE_NSLOTS_SMALL_MIN; 582d0e79aa3SJason Evans } else if ((arena_bin_info[i].nregs << 1) <= 583d0e79aa3SJason Evans TCACHE_NSLOTS_SMALL_MAX) { 584a4bd5210SJason Evans tcache_bin_info[i].ncached_max = 585a4bd5210SJason Evans (arena_bin_info[i].nregs << 1); 586a4bd5210SJason Evans } else { 587a4bd5210SJason Evans tcache_bin_info[i].ncached_max = 588a4bd5210SJason Evans TCACHE_NSLOTS_SMALL_MAX; 589a4bd5210SJason Evans } 590a4bd5210SJason Evans stack_nelms += tcache_bin_info[i].ncached_max; 591a4bd5210SJason Evans } 592a4bd5210SJason Evans for (; i < nhbins; i++) { 593a4bd5210SJason Evans tcache_bin_info[i].ncached_max = TCACHE_NSLOTS_LARGE; 594a4bd5210SJason Evans stack_nelms += tcache_bin_info[i].ncached_max; 595a4bd5210SJason Evans } 596a4bd5210SJason Evans 597a4bd5210SJason Evans return (false); 598a4bd5210SJason Evans } 599*8244f2aaSJason Evans 600*8244f2aaSJason Evans void 601*8244f2aaSJason Evans tcache_prefork(tsdn_t *tsdn) { 602*8244f2aaSJason Evans if (!config_prof && opt_tcache) { 603*8244f2aaSJason Evans malloc_mutex_prefork(tsdn, &tcaches_mtx); 604*8244f2aaSJason Evans } 605*8244f2aaSJason Evans } 606*8244f2aaSJason Evans 607*8244f2aaSJason Evans void 608*8244f2aaSJason Evans tcache_postfork_parent(tsdn_t *tsdn) { 609*8244f2aaSJason Evans if (!config_prof && opt_tcache) { 610*8244f2aaSJason Evans malloc_mutex_postfork_parent(tsdn, &tcaches_mtx); 611*8244f2aaSJason Evans } 612*8244f2aaSJason Evans } 613*8244f2aaSJason Evans 614*8244f2aaSJason Evans void 615*8244f2aaSJason Evans tcache_postfork_child(tsdn_t *tsdn) { 616*8244f2aaSJason Evans if (!config_prof && opt_tcache) { 617*8244f2aaSJason Evans malloc_mutex_postfork_child(tsdn, &tcaches_mtx); 618*8244f2aaSJason Evans } 619*8244f2aaSJason Evans } 620