Hugepage-Aware Memory Allocator TEMERAIRE
Hugepage-Aware Memory Allocator TEMERAIRE
A.H. Hunter Chris Kennelly Paul Turner Darryl Gove Tipp Moseley
Jane Street Capital∗ Google Google Google Google
Parthasarathy Ranganathan
Google
of hardware. A busy-looping spinlock has extremely high IPC, but does little system page size. The default configuration is to use an 8 KiB TCM ALLOC
useful work under contention. “page”, which is two (small) virtual memory pages on x86.
requested from OS The pageheap is also responsible for returning no-longer-
2 MiB needed memory to the OS when possible. Rather than do-
ing this on the free() path, a dedicated release-memory
method is invoked periodically, aiming to maintain a con-
figurable, steady rate of release in MB/s. This is a heuristic.
TCM ALLOC wants to simultaneously use the least memory
25 KiB possible in steady-state, avoiding expensive system alloca-
tions that could be elided by using previously provisioned
memory. We discuss handling this peak-to-trough allocation
pattern in more detail in Section 4.3.
Ideally, TCM ALLOC would return all memory that user
code will not need soon. Memory demand varies unpre-
200 KiB dictably, making it challenging to return memory that will
go unused while simultaneously retaining memory to avoid
Figure 2: Organization of memory in TCM ALLOC. System- syscalls and page faults.. Better decisions about memory re-
mapped memory is broken into (multi-)page spans, which are turn policies have high value and are discussed in section 7.
sub-divided into objects of an assigned, fixed sizeclass, here TCM ALLOC will first attempt to serve allocations from a
25 KiB. “local” cache, like most modern allocators [9,12,20,39]. Orig-
inally these were the eponymous per-Thread Caches, storing
a list of free objects for each sizeclass. To reduce stranded
memory and improve re-use for highly threaded applications,
minimize space overhead and fragmentation? TCM ALLOC now uses a per-hyperthread local cache. When
2. How do we scalably support concurrent allocations? the local cache has no objects of the appropriate sizeclass to
serve a request (or has too many after an attempt to free()),
Sufficiently large allocations are fulfilled with a span con- requests route to a single central cache for that sizeclass. This
taining only the allocated object. Other spans contain multiple has two components–a small fast, mutex-protected transfer
smaller objects of the same size (a sizeclass). The “small” ob- cache (containing flat arrays of objects from that sizeclass)
ject size boundary is 256 KiB. Within this “small” threshold, and a large, mutex-protected central freelist, containing every
allocation requests are rounded up to one of 100 sizeclasses. span assigned to that sizeclass; objects can be fetched from,
TCM ALLOC stores objects in a series of caches, illustrated in or returned to these spans. When all objects from a span have
been returned to a span held in the central freelist, that span
mmap is returned to the pageheap.
(large) malloc()
OS pageheap In our WSC, most allocations are small (50% of allocated
(large) free() space is objects ≤ 8192 bytes), as depicted in Figure 4. These
release s
s p are then aggregated into spans. The pageheap primarily al-
8 bytes span . . . ans 256KiB locates 1- or 2-page spans, as depicted in Figure 5. 80% of
central central central spans are smaller than a hugepage.
... The design of “stacked” caches make the system usefully
transfer transfer transfer modular, and there are several concomitant advantages:
0.8 0.8
0.6 0.6
Proportion
Proportion
0.4 0.4
0.2 0.2
0.0 0.0
102 104 106 108 1010 104 105 106 107 108 109 1010
Allocated Size (bytes) Span Size (bytes)
Figure 4: CDF of allocation sizes from WSC applications, Figure 5: CDF of TCM ALLOC span sizes from WSC appli-
weighted by bytes. cations, weighted by bytes.
unbacked hugepages
Behind all components is the HugeAllocator, which deals
HugeCache with virtual memory and the OS. It provides other compo-
backed hugepages nents with unbacked memory that they can back and pass on.
We also maintain a cache of backed, fully-empty hugepages,
called the HugeCache.
HugeFiller HugeRegion We keep a list of partially filled single hugepages (the
HugeFiller) that can be densely filled by subsequent small
sometimes
allocations. Where binpacking the allocations along hugepage
small requests large requests medium requests boundaries would be inefficient, we implement a specialized
(< 1 MiB) (≥ 1 GiB) (1 MiB - 1 GiB) allocator (the HugeRegion).
T EMERAIRE directs allocation decisions to its subcompo-
nents based on request size with the algorithm in Figure 7.
Figure 6: T EMERAIRE’s components. Arrows represent the Each subcomponent is optimized for different allocation sizes.
flow of requests to interior components. Allocations for an exact multiple of hugepage size, or those
sufficiently large that slack is immaterial, we forward directly
to the HugeCache.
Intermediate sized allocations (between 1MiB and 1GiB)
4.1 The overall algorithm are typically also allocated from the HugeCache, with a final
We will briefly sketch the overall approach and each com- step of donation for slack. For example, a 4.5 MiB allocation
ponent’s role, then describe each component in detail. Our from the HugeCache produces 1.5 MiB of slack, an unaccept-
goal is to minimize generated slack, and if we do generate ably high overhead ratio. T EMERAIRE donates that slack to
slack, to reuse it for other allocations (as with any page-level the HugeFiller by pretending that the last hugepage of the
fragmentation.) request has a single “leading” allocation on it (Figure 8).
4 As each operation holds an often-contended mutex, we do maintain
When such a large span is deallocated, the allocator also
reasonable efficiency: most operations are O(1), with care taken to optimize
marks the fictitious leading allocation as free. If the slack is un-
constant factors. used, it is returned to the tail hugepage along with the rest. Oth-
5 Indeed, jemalloc is doing so, based on T EMERAIRE . erwise the tail hugepage is left behind in the HugeFiller and
while (true) {
allocation slack Delete(New(512KB))
}
4.2 HugeAllocator
Figure 8: The slack from a large allocation spanning 3 huge- HugeAllocator tracks mapped virtual memory. All OS map-
pages is “donated” to the HugeFiller. The larger allocation’s pings are made here. It stores hugepage-aligned unbacked
tail is treated as a fictitious allocation. ranges (i.e. those with no associated physical memory.) Vir-
tual memory is nearly free, so we aim for simplicity and rea-
sonable speed. Our implementation tracks unused ranges with
only the first N − 1 hugepages are returned to the HugeCache. a treap [40]. We augment subtrees with their largest contained
range, which lets us quickly select an approximate best-fit.
For certain allocation patterns, intermediate-size alloca-
tions produce more slack than we can fill with smaller al-
locations in strict 2MiB bins. For example, many 1.1MiB 4.3 HugeCache
allocations will produce 0.9MiB of slack per hugepage (see
The HugeCache tracks backed ranges of memory at full huge-
Figure 12). When we detect this pattern, the HugeRegion
page granularity. A consequence of the HugeFiller filling
allocator places allocations across hugepage boundaries to
and draining whole hugepages is that we need to decide when
minimize this overhead.
to return empty hugepages to the OS. We will regret returning
Small requests (<= 1MiB) are always served from the memory we will need again, and equally regret not returning
HugeFiller. For allocations between 1MiB and a hugepage, memory that will languish in the cache. Returning memory
we evaluate several options: eagerly means we make syscalls to return the memory and
take page faults to reuse it. Releasing memory only at the rate
1. We try the HugeFiller: if we have available space there requested by TCM ALLOC’s periodic release thread means
we use it and are happy to fill a mostly-empty page. memory is held unused.
Consider the artificial program in Figure 9 with no addi-
2. If the HugeFiller can’t serve these requests, we next tional heap allocations. On each iteration of the loop, ‘New‘
consider HugeRegion; if we have regions allocated requires a new hugepage and places it with the HugeFiller.
which can serve the request, we do so. If no region exists ‘Delete‘ removes the allocation and the hugepage is now com-
(or they’re all too full) we consider allocating one, but pletely free. Returning eagerly would require a syscall every
only, as discussed below, if we’ve measured high ratios iteration for this simple, but pathological program.
of slack to small allocations. We track periodicity in the demand over a 2-second slid-
ing window and calculate the minimum and maximum seen
3. Otherwise, we allocate a full hugepage from the (demandmin , demandmax ). Whenever memory is returned to
HugeCache. This generates slack, but we anticipate that the HugeCache, we return hugepages to the OS if the cache
it will be filled by future allocations. would be larger than demandmax − demandmin . We also tried
other algorithms, but this one is simple and suffices to capture
We make a design choice in T EMERAIRE to care about the empirical dynamics we’ve seen. The cache is allowed
external fragmentation up to the level of a hugepage, but to grow as long as our windowed demand has seen a need
essentially not at all past it (but see Section 4.5 for an excep- for the new size. In oscillating usage, this will (incorrectly)
tion.) For example, a system with a single 1 GiB free range free memory once, then (correctly) keep it from then on. Fig-
and one with 512 discontiguous free hugepages is handled ure 10 shows our cache size for a Tensorflow workload which
equally well by T EMERAIRE. In either case, the allocator rapidly oscillates usage by a large fraction; we track the actu-
will (typically) return all of the unused space to the OS; a ally needed memory tightly.
fresh allocation of 1 GiB will require faulting in memory in
either case. In the fragmented scenario, we will need to do
4.4 HugeFiller
so on fresh virtual memory. Waste of virtual address range
unoccupied by live allocations and not consuming physical The HugeFiller satisfies smaller allocations that each fit
memory is not a concern, since with 64-bit address spaces, within a single hugepage. This satisfies the majority of allo-
virtual memory is practically free. cations (78% of the pageheap is backed by the HugeFiller
500 • the longest free range (L), the number of contiguous
total usage pages not already allocated,
400 • the total number of allocations (A),
memory (MiB)
while (true) { Figure 12: Slack (“s”) can accumulate when many allocations
// Reserve 51 hugepages + donate tail of last (“a”) are placed on single hugepages. No single slack region
L = New(100 MiB + 1 page); is large enough to accommodate a subsequent allocation of
// Make a small allocation size “a.”
S = New(1);
HugeRegion, only about 0.1%. (This motivates the large size ing both CPU and memory savings. We present evaluations
of each region.) of T EMERAIRE on several key services, measuring 10% of
Most programs don’t need regions at all. We do not allocate cycles and 15% of RAM usage in our WSC. In section 6.4
any region until we’ve accumulated large quantities of slack we discuss workload diversity; in this evaluation we examine
that are larger than the total of the program’s small allocations. data across all workloads using our experimental framework
Fleetwide, only 8.8% of programs trigger usage of regions, and fleetwide-profiler telemetry. We’ve argued for prioritizing
but the feature is still important: 53.2% of allocations in those workload efficiency over the attributable cost of malloc; we
binaries are served from regions. One such workload is a therefore examine IPC metrics (as a proxy for user through-
key-value store that loads long-lived data in large chunks put) and where possible, we obtained application-level perfor-
into memory and makes a small number of short-lived small mance metrics to gauge workload productivity (e.g., requests-
allocations for serving requests. Without regions, the request- per-second per core) on our servers. We present longitudinal
related allocations are unable to fill the slack generated by the data from the rollout of T EMERAIRE to all TCM ALLOC users
larger allocations. This technique prevents this slack-heavy in our fleet.
uncommon allocation pattern from bloating memory use. Overall, T EMERAIRE proved a significant win for CPU and
memory.
4.6 Memory Release
As discussed above, Release(N) is invoked periodically by
5.1 Application Case Studies
support threads at a steady trickle. We worked with performance-sensitive applications to enable
To implement our interface’s Release(N) methods, T EMERAIRE in their production systems, and measure the
T EMERAIRE typically just frees hugepage ranges from effect. We summarize the results in Table 1. Where possible,
HugeCache and possibly shrinks its limit as described above. we measured each application’s user-level performance met-
Releasing more than the hinted N pages is not a problem; the rics (throughput-per-CPU and latency). These applications
support threads use the actual released amount as feedback, use roughly 10% of cycles and 15% of RAM in our WSC.
and adjust future calls to target the correct overall rate. Four of these applications (search1; search2; search3;
If the HugeCache cannot release N pages of memory, the and loadbalancer) had previously turned off the periodic
HugeFiller will subrelease just the free (small) pages on the memory release feature of TCM ALLOC. This allowed them
emptiest hugepage. to have good hugepage coverage, even with the legacy page-
Returning small pages from partially filled hugepages heap’s hugepage-oblivious implementation, at the expense of
(“subreleasing” them) is the last resort for reducing memory memory. We did not change that setting with T EMERAIRE.
footprints as the process is largely irreversible6 . By returning These applications maintained their high levels of CPU per-
some but not all small pages on a hugepage, we cause the OS formance while reducing their total memory footprint.
to replace the single page table entry spanning the hugepage With the exception of Redis, all of these applications are
with small entries for the remaining pages. This one-way op- multithreaded. With the exception of search3, these work-
eration, through increased TLB misses, slows down accesses loads run on a single NUMA domain with local data.
to the remaining memory. The Linux kernel will use small
pagetable entries for the still-used pages, even if we re-use • Tensorflow [1] is a commonly used machine learning ap-
the released address space later. We make these return deci- plication. It had previously used a high periodic release
sions in the HugeFiller, where we manage partially filled rate to minimize memory pressure, albeit at the expense
hugepages. of hugepages and page faults.
The HugeFiller treats the subreleased hugepages sepa-
rately: we do not allocate from them unless no other hugepage • search1, search2, ads1, ads2, ads4, ads5 receive
is usable. Allocations placed on this memory will not benefit RPCs and make subsequent RPCs of their own other
from hugepages, so this helps performance and allows these services.
partially released hugepages to become completely empty.
• search3, ads3, ads6 are leaf RPC servers, performing
read-mostly retrieval tasks.
5 Evaluation of T EMERAIRE
• Spanner [17] is a node in a distributed database. It also
We evaluated T EMERAIRE on Google’s WSC workloads. includes an in-memory cache of data read from disk
The evaluation was concerned with several metrics, includ- which adapts to the memory provisioned for the process
6 While the THP machinery may reassemble hugepages, it is non- and unused elsewhere by the program.
deterministic and dependent on system utilization. There is a negative feed-
back loop here where high-utilization scenarios actually compete with and • loadbalancer receives updates over RPC and periodi-
impede THP progress that might benefit them the most. cally publishes summary statistics.
Mean RSS RAM IPC dTLB Load Walk (%) malloc (% of cycles) Page Fault (% of cycles)
Application Throughput
Latency (GiB) change Before After Before After Before After Before After
Tensorflow [1] +26%
search1 [6, 18]† 8.4 -8.7% 1.33 ± 0.04 1.43 ± 0.02 9.5 ± 0.6 9.0 ± 0.6 5.9 ± 0.09 5.9 ± 0.12 0.005 ± 0.003 0.131 ± 0.071
search2† 3.7 -20% 1.28 ± 0.01 1.29 ± 0.01 10.3 ± 0.2 10.2 ± 0.1 4.37 ± 0.05 4.38 ± 0.02 0.003 ± 0.003 0.032 ± 0.002
search3 † 234 -7% 1.64 ± 0.02 1.67 ± 0.02 8.9 ± 0.1 8.9 ± 0.3 3.2 ± 0.02 3.3 ± 0.04 0.001 ± 0.000 0.005 ± 0.001
ads1 +2.5% -14% 4.8 -6.9% 0.77 ± 0.02 0.84 ± 0.01 38.1 ± 1.3 15.9 ± 0.3 2.3 ± 0.04 2.7 ± 0.05 0.012 ± 0.003 0.011 ± 0.002
ads2 +3.4% -1.7% 5.6 -6.5% 1.12 ± 0.01 1.22 ± 0.01 27.4 ± 0.4 10.3 ± 0.2 2.7 ± 0.03 3.5 ± 0.08 0.022 ± 0.001 0.047 ± 0.001
ads3 +0.5% -0.2% 50.6 -0.8% 1.36 ± 0.01 1.43 ± 0.01 27.1 ± 0.5 11.6 ± 0.2 2.9 ± 0.04 3.2 ± 0.03 0.067 ± 0.002 0.03 ± 0.003
ads4 +6.6% -1.1% 2.5 -1.7% 0.87 ± 0.01 0.93 ± 0.01 28.5 ± 0.9 11.1 ± 0.3 4.2 ± 0.05 4.9 ± 0.04 0.022 ± 0.001 0.008 ± 0.001
ads5 +1.8% -0.7% 10.0 -1.1% 1.16 ± 0.02 1.16 ± 0.02 21.9 ± 1.2 16.7 ± 2.4 3.6 ± 0.08 3.8 ± 0.15 0.018 ± 0.002 0.033 ± 0.007
ads6 +15% -10% 53.5 -2.3% 1.40 ± 0.02 1.59 ± 0.03 33.6 ± 2.4 17.8 ± 0.4 13.5 ± 0.48 9.9 ± 0.07 0.037 ± 0.012 0.048 ± 0.067
Spanner [17] +6.3% 7.0 1.55 ± 0.30 1.70 ± 0.14 31.0 ± 4.3 15.7 ± 1.8 3.1 ± 0.88 3.0 ± 0.24 0.025 ± 0.08 0.024 ± 0.01
loadbalancer† 1.4 -40% 1.38 ± 0.12 1.39 ± 0.28 19.6 ± 1.2 9.5 ± 4.5 11.5 ± 0.60 10.7 ± 0.46 0.094 ± 0.06 0.057 ± 0.062
Average (all WSC apps) +5.2% -7.9% 1.26 1.33 23.3 12.4 5.2 5.0 0.058 0.112
Redis† +0.75%
Redis +0.44%
Table 1: Application experiments from enabling T EMERAIRE. Throughput is normalized for CPU. †: Applications’ periodic
memory release turned off. dTLB load walk (%) is the fraction of cycles spent page walking, not accessing the L2 TLB. malloc
(% of cycles) is the relative amount of time in allocation and deallocation functions. 90%th confidence intervals reported.
% hugepage coverage
a baseline. These experiments were run on servers with 60
Intel Skylake Xeon processors. Redis and TCM ALLOC
were compiled with LLVM built from Git commit
‘cd442157cf‘ using ‘-O3‘. In each configuration, we ran 44.3
40
2000 trials of ‘redis-benchmark‘, with each trial making
1000000 requests to push 5 elements and read those 5
elements. 23
20
For the 8 applications with periodic release, we observed a
11.8
mean CPU improvement of 7.7% and a mean RAM reduction
of 2.4%. Two of these workloads did not see memory reduc- 0
tions. T EMERAIRE’s HugeCache design handles Tensorflow’s periodic release on periodic release off
allocation pattern well, but cannot affect its bursty demand.
Spanner maximizes its caches up to a certain memory limit, control T EMERAIRE
so reducing TCM ALLOC’s overhead meant more application
data could be cached within the same footprint.
Figure 13: Percentage of heap memory backed by hugepages
during fleet experiment and 90%th confidence interval. (Error
5.2 Fleet experiment bars in "release on" condition are too small to cleanly render.)
We randomly selected 1% of the machines distributed through-
out our WSCs as an experiment group and a separate 1% as
a control group (see section 6.4). We enabled T EMERAIRE
on all applications running on the experiment machines. The We observed a strong improvement even in the case that pe-
applications running on control machines continued to use riodic release was disabled. Since these binaries do not break
the stock pageheap in TCM ALLOC. up hugepages in either configuration, the benefit is derived
Our fleetwide profiler lets us correlate performance metrics from increased system-wide availability of hugepages (due
against the groupings above. We collected data on memory to reduced fragmentation in other applications). T EMERAIRE
usage, hugepage coverage, overall IPC, and TLB misses. At improves this situation in two ways: since we aggressively re-
the time of the experiment, application-level performance lease empty hugepages (where the traditional pageheap does
metrics (throughput-per-CPU, latency) were not collected. In not), we consume fewer hugepages that we do not need, allow-
our analysis, we distinguish between applications that period- ing other applications to more successfully request them, and
ically release memory to the OS and those that turn off this other co-located applications are no longer breaking up huge-
feature to preserve hugepages with TCM ALLOC’s prior non- pages at the same rate. Even if we map large aligned regions
hugepage-aware pageheap. Figure 13 shows that T EMERAIRE of memory and do not interfere with transparent hugepages,
improved hugepage coverage, increasing the percentage of the kernel cannot always back these with hugepages [26, 33].
heap memory backed by hugepages from 11.8% to 23% for Fragmentation in physical memory can limit the number of
applications periodically releasing memory and from 44.3% available hugepages on the system.
to 67.3% for applications not periodically releasing memory. We next examine the effect this hugepage coverage had
Periodic Walk Cycles (%) MPKI
Release Control Exp. Control Exp.
On 12.5 11.9 (-4.5%) 1.20 1.14 (-5.4%) -1.3%
10
on TLB misses. Again, we break down between apps that
enable and disable periodic memory release. We measure the
percentage of total cycles spent in a dTLB load stall7 . 5
We see reductions of 4.5-5% of page walk miss cycles
(Table 2). We see in the experiment data that apps not re- 10 20 30 40 50 60
leasing memory (which have better hugepage coverage) have time (days)
higher dTLB stall costs, which is slightly surprising. Our dis-
load (old) store (old)
cussions with teams managing these applications is that they
turn off memory release because they need to guarantee per- load (T EMERAIRE) store (T EMERAIRE)
formance: on average, they have more challenging memory
access patterns and consequently greater concerns about mi- Figure 14: Stacked line graph showing effect of T EMERAIRE
croarchitectural variance. By disabling this release under the rollout on TLB miss cycles. We see an overall downward
prior implementation, they observed better application perfor- trend from 21.6% to 20.3% as T EMERAIRE became a larger
mance and fewer TLB stalls. With T EMERAIRE, we see our fraction of observed usage in our WSC.
improved hugepage coverage leads to materially lower dTLB
costs for both classes of applications.
For our last CPU consideration, we measured the over-
to 20.3% (6% reduction) and a reduction in pageheap over-
all impact on IPC8 . Fleetwide overall IPC in the control
head from 14.3% to 10.6% (26% reduction). Figure 14 shows
group was 0.796647919 ± 4e−9; in the experiment group,
the effect on TLB misses over time: at each point we show
0.806301729 ± 5e−9 instructions-per-cycle. This 1.2% im-
the total percentage of cycles attributable to TLB stalls (load
provement is small in relative terms but is a large absolute
and store), broken down by pageheap implementation. As
savings (especially when considered in the context of the
T EMERAIRE rolled out fleetwide, it caused a noticeable down-
higher individual application benefits discussed earlier).
ward trend.
For memory usage, we looked at pageheap overhead: the Figure 15 shows a similar plot of pageheap overhead. We
ratio of backed memory in the pageheap to the total heap see another significant improvement. Hugepage optimization
memory in use by the application. The experiment group has a natural tradeoff between space and time here; saving the
decreased this from 15.0% to 11.2%, again, a significant im- maximum memory possible requires breaking up hugepages,
provement. The production experiments comprise thousands which will cost CPU cycles. But T EMERAIRE outperforms
of applications running continuously on many thousands of the previous design in both space and time. We highlight
machines, conferring high confidence in a fleetwide benefit. several conclusions from our data:
Application productivity outpaced IPC. As noted above
5.3 Full rollout trajectories and by Alameldeen et al. [3], simple hardware metrics don’t
always accurately reflect application-level benefits. By all
With data gained from individual applications and the indication, T EMERAIRE improved application metrics (RPS,
1% experiment, we changed the default9 behavior to use latencies, etc.) by more than IPC.
T EMERAIRE. This rolled out to 100% of our workloads grad- Gains were not driven by reduction in the cost of malloc.
ually [10, 38]. Gains came from accelerating user code, which was some-
Over this deployment, we observed a reduction in cycles times drastic–in both directions. One application (ads2) saw
stalled on TLB misses (L2 TLB and page walks) from 21.6% an increase of malloc cycles from 2.7% to 3.5%, an apparent
regression, but they reaped improvements of 3.42% RPS, 1.7%
7 More precisely cycles spent page walking, not accessing the L2 TLB.
8 Our source of IPC data is not segmented by periodic background memory
latency, and 6.5% peak memory usage.
release status.
There is still considerable headroom, and small percent-
9 This doesn’t imply, quite, that every binary uses it. We allow opt outs ages matter. Even though T EMERAIRE has been successful,
for various operational needs. hugepage coverage is still only 67% when using T EMERAIRE
6.1 “Empirical” distribution sampling
15
Our production fleet implements a fleet wide profiler [35].
%age memory overhead
-3.7% Among the data collected by this profiler are fleet-wide sam-
ples of malloc tagged with request size and other useful prop-
10 erties. We collect a sample of currently-live data in our heap
and calls to malloc. From these samples we can infer the
empirical distribution of size both for live objects and mal-
loc calls. Our empirical driver generates calls to malloc and
5 free as a Poisson process10 that replicates these distributions,
while also targeting an arbitrary (average) heap size. That
target size can be changed over simulated time, reproducing
factors such as diurnal cycles, transient usage, or high startup
costs. We have made this driver and its inputs available on
20 40 60 80 Github (see Section 9).
time (days) Despite the name “empirical driver,” this remains a highly
unrealistic workload: every allocation (of a given size) is
old T EMERAIRE
equally likely to be freed at any timestep, and there is no cor-
relation between the sizes of consecutive allocation. Neither
Figure 15: Stacked line graph showing effect of T EMERAIRE does it reproduce per-thread or per-CPU dynamics. Never-
rollout on pageheap overhead. Total memory overhead goes theless, the empirical driver is a fast, efficient way to place
from 14.3% to 10.6%, as T EMERAIRE became a larger frac- malloc under an extremely challenging load that successfully
tion of observed usage in our WSC by growing from a handful replicates many macro characteristics of real work.
of applications (section 5.1) to nearly all applications.
[2] Yehuda Afek, Dave Dice, and Adam Morrison. Cache [14] Fay Chang, Jeffrey Dean, Sanjay Ghemawat, Wilson C.
Index-Aware Memory Allocation. SIGPLAN Not., Hsieh, Deborah A. Wallach, Mike Burrows, Tushar
46(11):55–64, June 2011. Chandra, Andrew Fikes, and Robert E. Gruber. Bigtable:
A Distributed Storage System for Structured Data. In
[3] A. R. Alameldeen and D. A. Wood. IPC Considered 7th USENIX Symposium on Operating Systems Design
Harmful for Multiprocessor Workloads. IEEE Micro, and Implementation (OSDI), pages 205–218, 2006.
26(4):8–17, 2006.
[15] Dehao Chen, David Xinliang Li, and Tipp Moseley. Aut-
[4] Andrea Arcangeli. Transparent hugepage support. 2010. ofdo: Automatic Feedback-Directed Optimization for
Warehouse-Scale Applications. In CGO 2016 Proceed-
[5] Aravinda Prasad Ashish Panwar and K. Gopinath. Mak- ings of the 2016 International Symposium on Code Gen-
ing Huge Pages Actually Useful. In Proceedings of the eration and Optimization, pages 12–23, New York, NY,
Twenty-Third International Conference on Architectural USA, 2016.
Support for Programming Languages and Operating
Systems (ASPLOS ’18), 2018. [16] William D. Clinger and Lars T. Hansen. Generational
Garbage Collection and the Radioactive Decay Model.
[6] Luiz Andre Barroso, Jeffrey Dean, and Urs Hölzle. Web
SIGPLAN Not., 32(5):97–108, May 1997.
search for a planet: The google cluster architecture.
IEEE Micro, 23:22–28, 2003. [17] James C. Corbett, Jeffrey Dean, Michael Epstein,
Andrew Fikes, Christopher Frost, JJ Furman, Sanjay
[7] Arkaprava Basu, Jayneel Gandhi, Jichuan Chang,
Ghemawat, Andrey Gubarev, Christopher Heiser, Pe-
Mark D. Hill, and Michael M. Swift. Efficient Virtual
ter Hochschild, Wilson Hsieh, Sebastian Kanthak, Eu-
Memory for Big Memory Servers. In Proceedings of
gene Kogan, Hongyi Li, Alexander Lloyd, Sergey Mel-
the 40th Annual International Symposium on Computer
nik, David Mwaura, David Nagle, Sean Quinlan, Rajesh
Architecture, ISCA ’13, page 237–248, New York, NY,
Rao, Lindsay Rolig, Yasushi Saito, Michal Szymaniak,
USA, 2013. Association for Computing Machinery.
Christopher Taylor, Ruth Wang, and Dale Woodford.
[8] Jon Bentley. Tiny Experiments for Algorithms and Life. Spanner: Google’s Globally-Distributed Database. In
In Experimental Algorithms, pages 182–182, Berlin, Hei- 10th USENIX Symposium on Operating Systems Design
delberg, 2006. Springer Berlin Heidelberg. and Implementation (OSDI 12), Hollywood, CA, 2012.
[9] Emery D. Berger, Kathryn S. McKinley, Robert D. Blu- [18] Jeffrey Dean. Challenges in building large-scale infor-
mofe, and Paul R. Wilson. Hoard: A Scalable Memory mation retrieval systems: invited talk. In WSDM ’09:
Allocator for Multithreaded Applications. SIGPLAN Proceedings of the Second ACM International Confer-
Not., 35(11):117–128, November 2000. ence on Web Search and Data Mining, pages 1–1, New
York, NY, USA, 2009.
[10] Jennifer Petoff Betsy Beyer, Chris Jones and
Niall Richard Murphy. Site Reliability Engineering: [19] Dave Dice, Tim Harris, Alex Kogan, and Yossi Lev. The
How Google Runs Production Systems. O’Reilly Media, Influence of Malloc Placement on TSX Hardware Trans-
Inc, 2016. actional Memory. CoRR, abs/1504.04640, 2015.
[11] Stephen M. Blackburn, Perry Cheng, and Kathryn S. [20] Jason Evans. A scalable concurrent malloc (3) imple-
McKinley. Myths and Realities: The Performance Im- mentation for FreeBSD. In Proceedings of the BSDCan
pact of Garbage Collection. In Proceedings of the Joint Conference, 2006.
[21] T. B. Ferreira, R. Matias, A. Macedo, and L. B. Araujo. [30] Martin Maas, David G. Andersen, Michael Isard, Mo-
An Experimental Study on Memory Allocators in Mul- hammad Mahdi Javanmard, Kathryn S. McKinley, and
ticore and Multithreaded Applications. In 2011 12th Colin Raffel. Learning-based Memory Allocation for
International Conference on Parallel and Distributed C++ Server Workloads. In 25th ACM International
Computing, Applications and Technologies, pages 92– Conference on Architectural Support for Programming
98, 2011. Languages and Operating Systems (ASPLOS), 2020.
[22] M. Jägemar. Mallocpool: Improving Memory Perfor- [31] Martin Maas, Chris Kennelly, Khanh Nguyen, Darryl
mance Through Contiguously TLB Mapped Memory. In Gove, Kathryn S. McKinley, and Paul Turner. Adaptive
2018 IEEE 23rd International Conference on Emerging huge-page subrelease for non-moving memory alloca-
Technologies and Factory Automation (ETFA), volume 1, tors in warehouse-scale computers. In Proceedings
pages 1127–1130, 2018. of the 2021 ACM SIGPLAN International Symposium
on Memory Management, ISMM 2021, New York, NY,
[23] Svilen Kanev, Juan Darago, Kim Hazelwood, USA, 2021. Association for Computing Machinery.
Parthasarathy Ranganathan, Tipp Moseley, Gu-Yeon
[32] Ashish Panwar, Sorav Bansal, and K. Gopinath. Hawk-
Wei, and David Brooks. Profiling a warehouse-scale
Eye: Efficient Fine-Grained OS Support for Huge Pages.
computer. In ISCA ’15 Proceedings of the 42nd Annual
In Proceedings of the Twenty-Fourth International Con-
International Symposium on Computer Architecture,
ference on Architectural Support for Programming Lan-
pages 158–169, 2014.
guages and Operating Systems, ASPLOS ’19, page
347–360, New York, NY, USA, 2019. Association for
[24] Svilen Kanev, Sam Likun Xi, Gu-Yeon Wei, and David
Computing Machinery.
Brooks. Mallacc: Accelerating Memory Allocation.
SIGARCH Comput. Archit. News, 45(1):33–45, April [33] Binh Pham, Viswanathan Vaidyanathan, Aamer Jaleel,
2017. and Abhishek Bhattacharjee. CoLT: Coalesced Large-
Reach TLBs. In Proceedings of the 2012 45th Annual
[25] Bradley C. Kuszmaul. Supermalloc: A Super Fast Mul- IEEE/ACM International Symposium on Microarchi-
tithreaded Malloc for 64-Bit Machines. SIGPLAN Not., tecture, MICRO-45, page 258–269, USA, 2012. IEEE
50(11):41–55, June 2015. Computer Society.
[26] Youngjin Kwon, Hangchen Yu, Simon Peter, Christo- [34] Bobby Powers, David Tench, Emery D. Berger, and An-
pher J. Rossbach, and Emmett Witchel. Coordinated drew McGregor. Mesh: Compacting Memory Manage-
and Efficient Huge Page Management with Ingens. In ment for C/C++ Applications. In Proceedings of the
Proceedings of the 12th USENIX Conference on Operat- 40th ACM SIGPLAN Conference on Programming Lan-
ing Systems Design and Implementation, OSDI’16, page guage Design and Implementation, PLDI 2019, page
705–721, USA, 2016. USENIX Association. 333–346, New York, NY, USA, 2019. Association for
Computing Machinery.
[27] Andres Lagar-Cavilla, Junwhan Ahn, Suleiman Souhlal,
Neha Agarwal, Radoslaw Burny, Shakeel Butt, Jichuan [35] Gang Ren, Eric Tune, Tipp Moseley, Yixin Shi, Silvius
Chang, Ashwin Chaugule, Nan Deng, Junaid Shahid, Rus, and Robert Hundt. Google-Wide Profiling: A Con-
Greg Thelen, Kamil Adam Yurtsever, Yu Zhao, and tinuous Profiling Infrastructure for Data Centers. IEEE
Parthasarathy Ranganathan. Software-Defined Far Mem- Micro, pages 65–79, 2010.
ory in Warehouse-Scale Computers. In Proceedings of
[36] John Robson. Worst Case Fragmentation of First Fit
the Twenty-Fourth International Conference on Archi-
and Best Fit Storage Allocation Strategies. Comput. J.,
tectural Support for Programming Languages and Oper-
20:242–244, 08 1977.
ating Systems, ASPLOS ’19, page 317–330, New York,
NY, USA, 2019. Association for Computing Machinery. [37] Joe Savage and Timothy M. Jones. HALO: Post-Link
Heap-Layout Optimisation. In Proceedings of the 18th
[28] Jaekyu Lee, Hyesoon Kim, and Richard Vuduc. When ACM/IEEE International Symposium on Code Genera-
Prefetching Works, When It Doesn’t, and Why. ACM tion and Optimization, CGO 2020, page 94–106, New
Transactions on Architecture and Code Optimization - York, NY, USA, 2020. Association for Computing Ma-
TACO, 9:1–29, 03 2012. chinery.
[29] Daan Leijen, Ben Zorn, and Leonardo de Moura. Mi- [38] T. Savor, M. Douglas, M. Gentili, L. Williams, K. Beck,
malloc: Free List Sharding in Action. Technical Report and M. Stumm. Continuous Deployment at Facebook
MSR-TR-2019-18, Microsoft, June 2019. and OANDA. In 2016 IEEE/ACM 38th International
Conference on Software Engineering Companion (ICSE-
C), pages 21–30, 2016.
[39] Scott Schneider, Christos D. Antonopoulos, and Dim-
itrios S. Nikolopoulos. Scalable Locality-Conscious
Multithreaded Memory Allocation. In Proceedings of
the 5th International Symposium on Memory Manage-
ment, ISMM ’06, page 84–94, New York, NY, USA,
2006. Association for Computing Machinery.
[40] Raimund Seidel and Cecilia R Aragon. Randomized
search trees. Algorithmica, 16(4-5):464–497, 1996.
TEMERAIRE addresses memory allocation slack by donating slack from large allocations to the HugeFiller. For instance, when a 4.5 MiB allocation generates 1.5 MiB of slack, this slack is treated as a "leading" allocation on the last hugepage. This slack can be returned if unused, or if the allocation is deallocated, the allocator marks it as free . For intermediate-sized allocations, TEMERAIRE evaluates using HugeFiller, HugeRegion, or HugeCache to minimize slack. The HugeRegion allocator minimizes slack by allocating across hugepage boundaries. This approach is particularly useful in uncommon allocation patterns where slack could otherwise bloat memory use .
Slack management strategies in TEMERAIRE play a pivotal role by optimizing the use of available memory and reducing overhead from fragmentation. For smaller allocations, slack is managed through donations to the HugeFiller, thus efficiently utilizing the "leading" allocation concept. By doing so, it minimizes wasted space and allows smaller allocations to be effectively filled and consolidated, contributing to high allocation density. These strategies prevent the unnecessary expansion of memory usage, maintaining efficiency and cost-effectiveness in handling memory allocations across different sizes .
TEMERAIRE innovatively handles large intermediate-size allocations by routing these through the HugeCache and dealing effectively with slack. For allocations such as 1.1 MiB, it anticipates slack and strategically uses HugeFiller or allocates a new region if slack ratios justify it. By this approach, it reduces inefficient memory use, minimizes slack, and prevents unnecessary bloat in memory usage. The slack handling through HugeFiller donation and HugeRegion allocation ensures that intermediate allocations are effectively utilized, improving system efficiency and performance .
TEMERAIRE's memory release decisions, such as the adaptive return of hugepages, impact system performance by aligning memory release with actual demand. Support threads use these release decisions to trickle-free memory at a sustainable rate, thereby avoiding excessive system overheads while keeping memory usage within optimal limits. By occasionally subreleasing small pages from partially filled hugepages, TEMERAIRE reduces footprints but risks increased TLB misses. However, these strategies improve memory availability and system efficiency by finely balancing memory retention and release, ensuring that performance is consistently high .
TEMERAIRE differs from traditional allocators by prioritizing allocation density within hugepages and adopting a dynamic, rather than static, release strategy. Unlike traditional fixed allocation strategies, TEMERAIRE's decision points allow it to retain or release empty hugepages based on current and predicted demand, optimizing system calls and memory utilization. Traditional allocators might rely more on a background process, while TEMERAIRE uses adaptive strategies such as selectively subreleasing pages only when necessary, thus reducing TLB misses and enhancing performance. This design promotes flexible and efficient memory use tailored to modern application demands .
The decision to retain or release hugepages significantly impacts memory and system performance. Retaining hugepages until they're mandatorily released can increase memory cost but minimize system call overhead. Releasing them too early reduces memory usage immediately but can incur costs due to potential page faults and increased system calls if the pages need to be reused. TEMERAIRE optimizes performance by adaptively timing these release decisions, aiming for a balance that returns memory to the OS efficiently without incurring unnecessary overheads .
The HugeAllocator enhances memory management by tracking mapped virtual memory and storing hugepage-aligned unbacked ranges, crucial for managing large-scale application demands. In TEMERAIRE, it ensures efficient handling of all OS mappings, enabling a streamlined approach to large memory requests, preventing excessive space loss by aligning allocations across the longest free ranges. This reduces virtual memory wastage and allows effective memory allocation even when dealing with hugepages, contributing to overall efficient memory management at scale .
The internal design principles of TEMERAIRE, such as emphasizing allocation density and strategic retention of hugepages, lead to better allocation decisions. By considering allocation unpredictability and varying memory demands, TEMERAIRE uses principles like smart placement over speed and flexible pageheap management to reduce fragmentation and overhead. The system minimizes costly mistakes by delaying allocations for better placement decisions, adapting dynamically to the system's demands. These principles enhance memory allocation efficiency by maximizing memory usage and adapting to changes swiftly in high-demand systems .
TEMERAIRE addresses external fragmentation by focusing on managing slack within a hugepage boundary rather than beyond it. For large and intermediate-sized allocations, it uses the HugeFiller to try using available space and consider the HugeRegion only when slack ratios are high. By donating slack efficiently, it minimizes the wastage of address spaces. TEMERAIRE does not concern itself with unoccupied virtual address ranges, leveraging 64-bit address spaces, hence treating scenarios of fragmented memory equally whether it's contiguously free or scattered .
Unpredictable memory demand means allocations can vary rapidly and extensively over time, with some allocations never being released. TEMERAIRE manages these challenges by maintaining strategies for allocations that might be "immortal" or "instantaneous". It focuses on densely packing allocations into hugepages, allowing it to adaptively decide when to release or retain hugepages, optimizing memory use and system calls. It implements subcomponents that specialize in different allocation types to enhance decision-making while reducing fragmentation .