Changeset cc7bbe6 for doc/theses/mubeen_zulfiqar_MMath/background.tex
- Timestamp:
- Feb 23, 2022, 11:24:34 AM (4 years ago)
- Branches:
- ADT, ast-experimental, enum, master, pthread-emulation, qualifiedEnum
- Children:
- 08ed947
- Parents:
- f5a51db (diff), 3a038fa (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/theses/mubeen_zulfiqar_MMath/background.tex
rf5a51db rcc7bbe6 1 1 \chapter{Background} 2 3 4 A program dynamically allocates and deallocates the storage for a variable, referred to as an \newterm{object}, through calls such as @malloc@ and @free@ in C, and @new@ and @delete@ in \CC. 5 Space for each allocated object comes from the dynamic-allocation zone. 6 A \newterm{memory allocator} contains a complex data-structure and code that manages the layout of objects in the dynamic-allocation zone. 7 The management goals are to make allocation/deallocation operations as fast as possible while densely packing objects to make efficient use of memory. 8 Objects in C/\CC cannot be moved to aid the packing process, only adjacent free storage can be \newterm{coalesced} into larger free areas. 9 The allocator grows or shrinks the dynamic-allocation zone to obtain storage for objects and reduce memory usage via operating-system calls, such as @mmap@ or @sbrk@ in UNIX. 10 11 12 \section{Allocator Components} 13 \label{s:AllocatorComponents} 14 15 \VRef[Figure]{f:AllocatorComponents} shows the two important data components for a memory allocator, management and storage, collectively called the \newterm{heap}. 16 The \newterm{management data} is a data structure located at a known memory address and contains all information necessary to manage the storage data. 17 The management data starts with fixed-sized information in the static-data memory that flows into the dynamic-allocation memory. 18 The \newterm{storage data} is composed of allocated and freed objects, and \newterm{reserved memory}. 19 Allocated objects (white) are variable sized, and allocated and maintained by the program; 20 \ie only the program knows the location of allocated storage, not the memory allocator. 21 \begin{figure}[h] 22 \centering 23 \input{AllocatorComponents} 24 \caption{Allocator Components (Heap)} 25 \label{f:AllocatorComponents} 26 \end{figure} 27 Freed objects (light grey) are memory deallocated by the program, which are linked into one or more lists facilitating easy location for new allocations. 28 Often the free list is chained internally so it does not consume additional storage, \ie the link fields are placed at known locations in the unused memory blocks. 29 Reserved memory (dark grey) is one or more blocks of memory obtained from the operating system but not yet allocated to the program; 30 if there are multiple reserved blocks, they are also chained together, usually internally. 31 32 Allocated and freed objects typically have additional management data embedded within them. 33 \VRef[Figure]{f:AllocatedObject} shows an allocated object with a header, trailer, and alignment padding and spacing around the object. 34 The header contains information about the object, \eg size, type, etc. 35 The trailer may be used to simplify an allocation implementation, \eg coalescing, and/or for security purposes to mark the end of an object. 36 An object may be preceded by padding to ensure proper alignment. 37 Some algorithms quantize allocation requests into distinct sizes resulting in additional spacing after objects less than the quantized value. 38 When padding and spacing are necessary, neither can be used to satisfy a future allocation request while the current allocation exists. 39 A free object also contains management data, \eg size, chaining, etc. 40 The amount of management data for a free node defines the minimum allocation size, \eg if 16 bytes are needed for a free-list node, any allocation request less than 16 bytes must be rounded up, otherwise the free list cannot use internal chaining. 41 The information in an allocated or freed object is overwritten when it transitions from allocated to freed and vice-versa by new management information and possibly data. 42 43 \begin{figure} 44 \centering 45 \input{AllocatedObject} 46 \caption{Allocated Object} 47 \label{f:AllocatedObject} 48 \end{figure} 49 50 51 \section{Single-Threaded Memory-Allocator} 52 \label{s:SingleThreadedMemoryAllocator} 53 54 A single-threaded memory-allocator does not run any threads itself, but is used by a single-threaded program. 55 Because the memory allocator is only executed by a single thread, concurrency issues do not exist. 56 The primary issues in designing a single-threaded memory-allocator are fragmentation and locality. 57 58 59 \subsection{Fragmentation} 60 \label{s:Fragmentation} 61 62 Fragmentation is memory requested from the operating system but not used by the program; 63 hence, allocated objects are not fragmentation. 64 \VRef[Figure]{f:InternalExternalFragmentation}) shows fragmentation is divided into two forms: internal or external. 65 66 \begin{figure} 67 \centering 68 \input{IntExtFragmentation} 69 \caption{Internal and External Fragmentation} 70 \label{f:InternalExternalFragmentation} 71 \end{figure} 72 73 \newterm{Internal fragmentation} is memory space that is allocated to the program, but is not intended to be accessed by the program, such as headers, trailers, padding, and spacing around an allocated object. 74 This memory is typically used by the allocator for management purposes or required by the architecture for correctness, \eg alignment. 75 Internal fragmentation is problematic when management space is a significant proportion of an allocated object. 76 For example, if internal fragmentation is as large as the object being managed, then the memory usage for that object is doubled. 77 An allocator should strive to keep internal management information to a minimum. 78 79 \newterm{External fragmentation} is all memory space reserved from the operating system but not allocated to the program~\cite{Wilson95,Lim98,Siebert00}, which includes freed objects, all external management data, and reserved memory. 80 This memory is problematic in two ways: heap blowup and highly fragmented memory. 81 \newterm{Heap blowup} occurs when memory freed by the program is not reused for future allocations leading to potentially unbounded external fragmentation growth~\cite{Berger00}. 82 Heap blowup can occur due to allocator policies that are too restrictive in reusing freed memory and/or no coalescing of free storage. 83 Memory can become \newterm{highly fragmented} after multiple allocations and deallocations of objects. 84 \VRef[Figure]{f:MemoryFragmentation} shows an example of how a small block of memory fragments as objects are allocated and deallocated over time. 85 Blocks of free memory become smaller and non-contiguous making them less useful in serving allocation requests. 86 Memory is highly fragmented when the sizes of most free blocks are unusable. 87 For example, \VRef[Figure]{f:Contiguous} and \VRef[Figure]{f:HighlyFragmented} have the same quantity of external fragmentation, but \VRef[Figure]{f:HighlyFragmented} is highly fragmented. 88 If there is a request to allocate a large object, \VRef[Figure]{f:Contiguous} is more likely to be able to satisfy it with existing free memory, while \VRef[Figure]{f:HighlyFragmented} likely has to request more memory from the operating system. 89 90 \begin{figure} 91 \centering 92 \input{MemoryFragmentation} 93 \caption{Memory Fragmentation} 94 \label{f:MemoryFragmentation} 95 \vspace{10pt} 96 \subfigure[Contiguous]{ 97 \input{ContigFragmentation} 98 \label{f:Contiguous} 99 } % subfigure 100 \subfigure[Highly Fragmented]{ 101 \input{NonContigFragmentation} 102 \label{f:HighlyFragmented} 103 } % subfigure 104 \caption{Fragmentation Quality} 105 \label{f:FragmentationQuality} 106 \end{figure} 107 108 For a single-threaded memory allocator, three basic approaches for controlling fragmentation have been identified~\cite{Johnstone99}. 109 The first approach is a \newterm{sequential-fit algorithm} with one list of free objects that is searched for a block large enough to fit a requested object size. 110 Different search policies determine the free object selected, \eg the first free object large enough or closest to the requested size. 111 Any storage larger than the request can become spacing after the object or be split into a smaller free object. 112 The cost of the search depends on the shape and quality of the free list, \eg a linear versus a binary-tree free-list, a sorted versus unsorted free-list. 113 114 The second approach is a \newterm{segregated} or \newterm{binning algorithm} with a set of lists for different sized freed objects. 115 When an object is allocated, the requested size is rounded up to the nearest bin-size, possibly with spacing after the object. 116 A binning algorithm is fast at finding free memory of the appropriate size and allocating it, since the first free object on the free list is used. 117 The fewer bin-sizes, the fewer lists need to be searched and maintained; 118 however, the bin sizes are less likely to closely fit the requested object size, leading to more internal fragmentation. 119 The more bin-sizes, the longer the search and the less likely free objects are to be reused, leading to more external fragmentation and potentially heap blowup. 120 A variation of the binning algorithm allows objects to be allocated to the requested size, but when an object is freed, it is placed on the free list of the next smallest or equal bin-size. 121 For example, with bin sizes of 8 and 16 bytes, a request for 12 bytes allocates only 12 bytes, but when the object is freed, it is placed on the 8-byte bin-list. 122 For subsequent requests, the bin free-lists contain objects of different sizes, ranging from one bin-size to the next (8-16 in this example), and a sequential-fit algorithm may be used to find an object large enough for the requested size on the associated bin list. 123 124 The third approach is \newterm{splitting} and \newterm{coalescing algorithms}. 125 When an object is allocated, if there are no free objects of the requested size, a larger free object may be split into two smaller objects to satisfy the allocation request without obtaining more memory from the operating system. 126 For example, in the buddy system, a block of free memory is split into two equal chunks, one of those chunks is again split into two equal chunks, and so on until a block just large enough to fit the requested object is created. 127 When an object is deallocated it is coalesced with the objects immediately before and after it in memory, if they are free, turning them into one larger object. 128 Coalescing can be done eagerly at each deallocation or lazily when an allocation cannot be fulfilled. 129 In all cases, coalescing increases allocation latency, hence some allocations can cause unbounded delays during coalescing. 130 While coalescing does not reduce external fragmentation, the coalesced blocks improve fragmentation quality so future allocations are less likely to cause heap blowup. 131 Splitting and coalescing can be used with other algorithms to avoid highly fragmented memory. 132 133 134 \subsection{Locality} 135 \label{s:Locality} 136 137 The principle of locality recognizes that programs tend to reference a small set of data, called a working set, for a certain period of time, where a working set is composed of temporal and spatial accesses~\cite{Denning05}. 138 Temporal clustering implies a group of objects are accessed repeatedly within a short time period, while spatial clustering implies a group of objects physically close together (nearby addresses) are accessed repeatedly within a short time period. 139 Temporal locality commonly occurs during an iterative computation with a fix set of disjoint variables, while spatial locality commonly occurs when traversing an array. 140 141 Hardware takes advantage of temporal and spatial locality through multiple levels of caching (\ie memory hierarchy). 142 When an object is accessed, the memory physically located around the object is also cached with the expectation that the current and nearby objects will be referenced within a short period of time. 143 For example, entire cache lines are transferred between memory and cache and entire virtual-memory pages are transferred between disk and memory. 144 A program exhibiting good locality has better performance due to fewer cache misses and page faults\footnote{With the advent of large RAM memory, paging is becoming less of an issue in modern programming.}. 145 146 Temporal locality is largely controlled by how a program accesses its variables~\cite{Feng05}. 147 Nevertheless, a memory allocator can have some indirect influence on temporal locality and largely dictates spatial locality. 148 For temporal locality, an allocator can return storage for new allocations that was just freed as these memory locations are still \emph{warm} in the memory hierarchy. 149 For spatial locality, an allocator can place objects used together close together in memory, so the working set of the program fits into the fewest possible cache lines and pages. 150 However, usage patterns are different for every program as is the underlying hardware memory architecture; 151 hence, no general-purpose memory-allocator can provide ideal locality for every program on every computer. 152 153 There are a number of ways a memory allocator can degrade locality by increasing the working set. 154 For example, a memory allocator may access multiple free objects before finding one to satisfy an allocation request (\eg sequential-fit algorithm). 155 If there are a (large) number of objects accessed in very different areas of memory, the allocator may perturb the program's memory hierarchy causing multiple cache or page misses~\cite{Grunwald93}. 156 Another way locality can be degraded is by spatially separating related data. 157 For example, in a binning allocator, objects of different sizes are allocated from different bins that may be located in different pages of memory. 158 159 160 \section{Multi-Threaded Memory-Allocator} 161 \label{s:MultiThreadedMemoryAllocator} 162 163 A multi-threaded memory-allocator does not run any threads itself, but is used by a multi-threaded program. 164 In addition to single-threaded design issues of locality and fragmentation, a multi-threaded allocator may be simultaneously accessed by multiple threads, and hence, must deal with concurrency issues such as ymutual exclusion, false sharing, and additional forms of heap blowup. 165 166 167 \subsection{Mutual Exclusion} 168 \label{s:MutualExclusion} 169 170 \newterm{Mutual exclusion} provides sequential access to the shared management data of the heap. 171 There are two performance issues for mutual exclusion. 172 First is the overhead necessary to perform (at least) a hardware atomic operation every time a shared resource is accessed. 173 Second is when multiple threads contend for a shared resource simultaneously, and hence, some threads must wait until the resource is released. 174 Contention can be reduced in a number of ways: 175 using multiple fine-grained locks versus a single lock, spreading the contention across a number of locks; 176 using trylock and generating new storage if the lock is busy, yielding a classic space versus time tradeoff; 177 using one of the many lock-free approaches for reducing contention on basic data-structure operations~\cite{Oyama99}. 178 However, all of these approaches have degenerate cases where contention occurs. 179 180 181 \subsection{False Sharing} 182 \label{s:FalseSharing} 183 184 False sharing is a dynamic phenomenon leading to cache thrashing. 185 When two or more threads on separate CPUs simultaneously change different objects sharing a cache line, the change invalidates the other thread's associated cache, even though these threads may be uninterested in the other modified object. 186 False sharing can occur in three different ways: program induced, allocator-induced active, and allocator-induced passive; 187 a memory allocator can only affect the latter two. 188 189 \paragraph{\newterm{Program-induced false-sharing}} occurs when one thread passes an object sharing a cache line to another thread, and both threads modify the respective objects. 190 \VRef[Figure]{f:ProgramInducedFalseSharing} shows when Task$_1$ passes Object$_2$ to Task$_2$, a false-sharing situation forms when Task$_1$ modifies Object$_1$ and Task$_2$ modifies Object$_2$. 191 Changes to Object$_1$ invalidate CPU$_2$'s cache line, and changes to Object$_2$ invalidate CPU$_1$'s cache line. 192 193 \begin{figure} 194 \centering 195 \subfigure[Program-Induced False-Sharing]{ 196 \input{ProgramFalseSharing} 197 \label{f:ProgramInducedFalseSharing} 198 } \\ 199 \vspace{5pt} 200 \subfigure[Allocator-Induced Active False-Sharing]{ 201 \input{AllocInducedActiveFalseSharing} 202 \label{f:AllocatorInducedActiveFalseSharing} 203 } \\ 204 \vspace{5pt} 205 \subfigure[Allocator-Induced Passive False-Sharing]{ 206 \input{AllocInducedPassiveFalseSharing} 207 \label{f:AllocatorInducedPassiveFalseSharing} 208 } % subfigure 209 \caption{False Sharing} 210 \label{f:FalseSharing} 211 \end{figure} 212 213 \paragraph{\newterm{Allocator-induced active false-sharing}} occurs when objects are allocated within the same cache line but to different threads. 214 For example, in \VRef[Figure]{f:AllocatorInducedActiveFalseSharing}, each task allocates an object and loads a cache-line of memory into its associated cache. 215 Again, changes to Object$_1$ invalidate CPU$_2$'s cache line, and changes to Object$_2$ invalidate CPU$_1$'s cache line. 216 217 \paragraph{\newterm{Allocator-induced passive false-sharing}} is another form of allocator-induced false-sharing caused by program-induced false-sharing. 218 When an object in a program-induced false-sharing situation is deallocated, a future allocation of that object may cause passive false-sharing. 219 For example, in \VRef[Figure]{f:AllocatorInducedPassiveFalseSharing}, Task$_1$ passes Object$_2$ to Task$_2$, and Task$_2$ subsequently deallocates Object$_2$. 220 Allocator-induced passive false-sharing occurs when Object$_2$ is reallocated to Task$_2$ while Task$_1$ is still using Object$_1$. 221 222 223 \subsection{Heap Blowup} 224 \label{s:HeapBlowup} 225 226 In a multi-threaded program, heap blowup can occur when memory freed by one thread is inaccessible to other threads due to the allocation strategy. 227 Specific examples are presented in later sections. 228 229 230 \section{Multi-Threaded Memory-Allocator Features} 231 \label{s:MultiThreadedMemoryAllocatorFeatures} 232 233 By analyzing a suite of existing allocators (see \VRef{s:ExistingAllocators}), the following salient features were identified: 234 \begin{list}{\arabic{enumi}.}{\usecounter{enumi}\topsep=0.5ex\parsep=0pt\itemsep=0pt} 235 \item multiple heaps 236 \begin{list}{\alph{enumii})}{\usecounter{enumii}\topsep=0.5ex\parsep=0pt\itemsep=0pt} 237 \item with or without a global heap 238 \item with or without ownership 239 \end{list} 240 \item object containers 241 \begin{list}{\alph{enumii})}{\usecounter{enumii}\topsep=0.5ex\parsep=0pt\itemsep=0pt} 242 \item with or without ownership 243 \item fixed or variable sized 244 \item global or local free-lists 245 \end{list} 246 \item hybrid private/public heap 247 \item allocation buffer 248 \item lock-free operations 249 \end{list} 250 The first feature, multiple heaps, pertains to different kinds of heaps. 251 The second feature, object containers, pertains to the organization of objects within the storage area. 252 The remaining features apply to different parts of the allocator design or implementation. 253 254 255 \section{Multiple Heaps} 256 \label{s:MultipleHeaps} 257 258 A single-threaded allocator has at most one thread and heap, while a multi-threaded allocator has potentially multiple threads and heaps. 259 The multiple threads cause complexity, and multiple heaps are a mechanism for dealing with the complexity. 260 The spectrum ranges from multiple threads using a single heap, denoted as T:1 (see \VRef[Figure]{f:SingleHeap}), to multiple threads sharing multiple heaps, denoted as T:H (see \VRef[Figure]{f:SharedHeaps}), to one thread per heap, denoted as 1:1 (see \VRef[Figure]{f:PerThreadHeap}), which is almost back to a single-threaded allocator. 261 262 In the T:1 model, all threads allocate and deallocate objects from one heap. 263 Memory is obtained from the freed objects or reserved memory in the heap, or from the operating system (OS); 264 the heap may also return freed memory to the operating system. 265 The arrows indicate the direction memory conceptually moves for each kind of operation: allocation moves memory along the path from the heap/operating-system to the user application, while deallocation moves memory along the path from the application back to the heap/operating-system. 266 To safely handle concurrency, a single heap uses locking to provide mutual exclusion. 267 Whether using a single lock for all heap operations or fine-grained locking for different operations, a single heap may be a significant source of contention for programs with a large amount of memory allocation. 268 269 \begin{figure} 270 \centering 271 \subfigure[T:1]{ 272 % \input{SingleHeap.pstex_t} 273 \input{SingleHeap} 274 \label{f:SingleHeap} 275 } % subfigure 276 \vrule 277 \subfigure[T:H]{ 278 % \input{MultipleHeaps.pstex_t} 279 \input{SharedHeaps} 280 \label{f:SharedHeaps} 281 } % subfigure 282 \vrule 283 \subfigure[1:1]{ 284 % \input{MultipleHeapsGlobal.pstex_t} 285 \input{PerThreadHeap} 286 \label{f:PerThreadHeap} 287 } % subfigure 288 \caption{Multiple Heaps, Thread:Heap Relationship} 289 \end{figure} 290 291 In the T:H model, each thread allocates storage from several heaps depending on certain criteria, with the goal of reducing contention by spreading allocations/deallocations across the heaps. 292 The decision on when to create a new heap and which heap a thread allocates from depends on the allocator design. 293 The performance goal is to reduce the ratio of heaps to threads. 294 In general, locking is required, since more than one thread may concurrently access a heap during its lifetime, but contention is reduced because fewer threads access a specific heap. 295 Two examples of this approach are: 296 \begin{description} 297 \item[heap pool:] 298 Multiple heaps are managed in a pool, starting with a single or a fixed number of heaps that increase\-/decrease depending on contention\-/space issues. 299 At creation, a thread is associated with a heap from the pool. 300 When the thread attempts an allocation and its associated heap is locked (contention), it scans for an unlocked heap in the pool. 301 If an unlocked heap is found, the thread changes its association and uses that heap. 302 If all heaps are locked, the thread may create a new heap, use it, and then place the new heap into the pool; 303 or the thread can block waiting for a heap to become available. 304 While the heap-pool approach often minimizes the number of extant heaps, the worse case can result in more heaps than threads; 305 \eg if the number of threads is large at startup with many allocations creating a large number of heaps and then the number of threads reduces. 306 \item[kernel threads:] 307 Each kernel thread (CPU) executing an application has its own heap. 308 A thread allocates/deallocates from/to the heap of the kernel thread on which it is executing. 309 Special precautions must be taken to handle or prevent the case where a thread is preempted during allocation/deallocation and restarts execution on a different kernel thread~\cite{Dice02}. 310 \end{description} 311 312 In the 1:1 model (thread heaps), each thread has its own heap, which eliminates contention and locking because no thread accesses another thread's heap. 313 An additional benefit of thread heaps is improved locality due to better memory layout. 314 As each thread only allocates from its heap, all objects for a thread are more consolidated in the storage area for that heap, better utilizing each CPUs cache and accessing fewer pages. 315 In contrast, the T:H model spreads each thread's objects over a larger area in different heaps. 316 Thread heaps can also eliminate allocator-induced active false-sharing, if memory is acquired so it does not overlap at crucial boundaries with memory for another thread's heap. 317 For example, assume page boundaries coincide with cache line boundaries, then if a thread heap always acquires pages of memory, no two threads share a page or cache line unless pointers are passed among them. 318 Hence, allocator-induced active false-sharing in \VRef[Figure]{f:AllocatorInducedActiveFalseSharing} cannot occur because the memory for thread heaps never overlaps. 319 320 Threads using multiple heaps need to determine the specific heap to access for an allocation/deallocation, \ie association of thread to heap. 321 A number of techniques are used to establish this association. 322 The simplest approach is for each thread to have a pointer to its associated heap (or to administrative information that points to the heap), and this pointer changes if the association changes. 323 For threading systems with thread-local/specific storage, the heap pointer/data is created using this mechanism; 324 otherwise, the heap routines must use approaches like hashing the thread's stack-pointer or thread-id to find its associated heap. 325 326 The storage management for multiple heaps is more complex than for a single heap (see \VRef[Figure]{f:AllocatorComponents}). 327 \VRef[Figure]{f:MultipleHeapStorage} illustrates the general storage layout for multiple heaps. 328 Allocated and free objects are labelled by the thread or heap they are associated with. 329 (Links between free objects are removed for simplicity.) 330 The management information in the static zone must be able to locate all heaps in the dynamic zone. 331 The management information for the heaps must reside in the dynamic-allocation zone if there are a variable number. 332 Each heap in the dynamic zone is composed of a list of a free objects and a pointer to its reserved memory. 333 An alternative implementation is for all heaps to share one reserved memory, which requires a separate lock for the reserved storage to ensure mutual exclusion when acquiring new memory. 334 Because multiple threads can allocate/free/reallocate adjacent storage, all forms of false sharing may occur. 335 Other storage-management options are to use @mmap@ to set aside (large) areas of virtual memory for each heap and suballocate each heap's storage within that area. 336 337 \begin{figure} 338 \centering 339 \input{MultipleHeapsStorage} 340 \caption{Multiple-Heap Storage} 341 \label{f:MultipleHeapStorage} 342 \end{figure} 343 344 Multiple heaps increase external fragmentation as the ratio of heaps to threads increases, which can lead to heap blowup. 345 The external fragmentation experienced by a program with a single heap is now multiplied by the number of heaps, since each heap manages its own free storage and allocates its own reserved memory. 346 Additionally, objects freed by one heap cannot be reused by other threads, except indirectly by returning free memory to the operating system, which can be expensive. 347 (Depending on how the operating system provides dynamic storage to an application, returning storage may be difficult or impossible, \eg the contiguous @sbrk@ area in Unix.) 348 In the worst case, a program in which objects are allocated from one heap but deallocated to another heap means these freed objects are never reused. 349 350 Adding a \newterm{global heap} (G) attempts to reduce the cost of obtaining/returning memory among heaps (sharing) by buffering storage within the application address-space. 351 Now, each heap obtains and returns storage to/from the global heap rather than the operating system. 352 Storage is obtained from the global heap only when a heap allocation cannot be fulfilled, and returned to the global heap when a heap's free memory exceeds some threshold. 353 Similarly, the global heap buffers this memory, obtaining and returning storage to/from the operating system as necessary. 354 The global heap does not have its own thread and makes no internal allocation requests; 355 instead, it uses the application thread, which called one of the multiple heaps and then the global heap, to perform operations. 356 Hence, the worst-case cost of a memory operation includes all these steps. 357 With respect to heap blowup, the global heap provides an indirect mechanism to move free memory among heaps, which usually has a much lower cost than interacting with the operating system to achieve the same goal and is independent of the mechanism used by the operating system to present dynamic memory to an address space. 358 359 However, since any thread may indirectly perform a memory operation on the global heap, it is a shared resource that requires locking. 360 A single lock can be used to protect the global heap or fine-grained locking can be used to reduce contention. 361 In general, the cost is minimal since the majority of memory operations are completed without the use of the global heap. 362 363 For thread heaps, when a kernel/user-thread terminates, there are two options for handling its heap. 364 First is to free all objects in the heap to the global heap and destroy the thread heap. 365 Second is to place the thread heap on a list of available heaps and reuse it for a new kernel/user thread in the future. 366 Destroying the thread heap immediately may reduce external fragmentation sooner, since all free objects are freed to the global heap and may be reused by other threads. 367 Alternatively, reusing thread heaps may improve performance if the inheriting thread makes similar allocation requests as the thread that previously held the thread heap. 368 369 As multiple heaps are a key feature for a multi-threaded allocator, all further discussion assumes multiple heaps with a global heap to eliminate direct interaction with the operating system. 370 371 372 \subsection{Ownership} 373 \label{s:Ownership} 374 375 \newterm{Ownership} defines which heap an object is returned-to on deallocation. 376 If a thread returns an object to the heap it was originally allocated from, the heap has ownership of its objects. 377 Alternatively, a thread can return an object to the heap it is currently allocating from, which can be any heap accessible during a thread's lifetime. 378 \VRef[Figure]{f:HeapsOwnership} shows an example of multiple heaps (minus the global heap) with and without ownership. 379 Again, the arrows indicate the direction memory conceptually moves for each kind of operation. 380 For the 1:1 thread:heap relationship, a thread only allocates from its own heap, and without ownership, a thread only frees objects to its own heap, which means the heap is private to its owner thread and does not require any locking, called a \newterm{private heap}. 381 For the T:1/T:H models with or without ownership or the 1:1 model with ownership, a thread may free objects to different heaps, which makes each heap publicly accessible to all threads, called a \newterm{public heap}. 382 383 \begin{figure} 384 \centering 385 \subfigure[Ownership]{ 386 \input{MultipleHeapsOwnership} 387 } % subfigure 388 \hspace{0.25in} 389 \subfigure[No Ownership]{ 390 \input{MultipleHeapsNoOwnership} 391 } % subfigure 392 \caption{Heap Ownership} 393 \label{f:HeapsOwnership} 394 \end{figure} 395 396 \VRef[Figure]{f:MultipleHeapStorageOwnership} shows the effect of ownership on storage layout. 397 (For simplicity assume the heaps all use the same size of reserves storage.) 398 In contrast to \VRef[Figure]{f:MultipleHeapStorage}, each reserved area used by a heap only contains free storage for that particular heap because threads must return free objects back to the owner heap. 399 Again, because multiple threads can allocate/free/reallocate adjacent storage in the same heap, all forms of false sharing may occur. 400 The exception is for the 1:1 model if reserved memory does not overlap a cache-line because all allocated storage within a used area is associated with a single thread. 401 In this case, there is no allocator-induced active false-sharing (see \VRef[Figure]{f:AllocatorInducedActiveFalseSharing}) because two adjacent allocated objects used by different threads cannot share a cache-line. 402 As well, there is no allocator-induced passive false-sharing (see \VRef[Figure]{f:AllocatorInducedActiveFalseSharing}) because two adjacent allocated objects used by different threads cannot occur because free objects are returned to the owner heap. 403 % Passive false-sharing may still occur, if delayed ownership is used (see below). 404 405 \begin{figure} 406 \centering 407 \input{MultipleHeapsOwnershipStorage.pstex_t} 408 \caption{Multiple-Heap Storage with Ownership} 409 \label{f:MultipleHeapStorageOwnership} 410 \end{figure} 411 412 The main advantage of ownership is preventing heap blowup by returning storage for reuse by the owner heap. 413 Ownership prevents the classical problem where one thread performs allocations from one heap, passes the object to another thread, and the receiving thread deallocates the object to another heap, hence draining the initial heap of storage. 414 As well, allocator-induced passive false-sharing is eliminated because returning an object to its owner heap means it can never be allocated to another thread. 415 For example, in \VRef[Figure]{f:AllocatorInducedPassiveFalseSharing}, the deallocation by Task$_2$ returns Object$_2$ back to Task$_1$'s heap; 416 hence a subsequent allocation by Task$_2$ cannot return this storage. 417 The disadvantage of ownership is deallocating to another task's heap so heaps are no longer private and require locks to provide safe concurrent access. 418 419 Object ownership can be immediate or delayed, meaning objects may be returned to the owner heap immediately at deallocation or after some delay. 420 A thread may delay the return by storing objects it does not own on a separate free list. 421 Delaying can improve performance by batching objects for return to their owner heap and possibly reallocating these objects if storage runs out on the current heap. 422 However, reallocation can result in passive false-sharing. 423 For example, in \VRef[Figure]{f:AllocatorInducedPassiveFalseSharing}, Object$_2$ may be deallocated to Task$_2$'s heap initially. 424 If Task$_2$ reallocates Object$_2$ before it is returned to its owner heap, then passive false-sharing may occur. 425 426 427 \section{Object Containers} 428 \label{s:ObjectContainers} 429 430 One approach for managing objects places headers/trailers around individual objects, meaning memory adjacent to the object is reserved for object-management information, as shown in \VRef[Figure]{f:ObjectHeaders}. 431 However, this approach leads to poor cache usage, since only a portion of the cache line is holding useful information from the program's perspective. 432 Spatial locality is also negatively affected. 433 While the header and object are together in memory, they are generally not accessed together; 434 \eg the object is accessed by the program when it is allocated, while the header is accessed by the allocator when the object is free. 435 This difference in usage patterns can lead to poor cache locality~\cite{Feng05}. 436 Additionally, placing headers on individual objects can lead to redundant management information. 437 For example, if a header stores only the object size, then all objects with the same size have identical headers. 438 439 \begin{figure} 440 \centering 441 \subfigure[Object Headers]{ 442 \input{ObjectHeaders} 443 \label{f:ObjectHeaders} 444 } % subfigure 445 \\ 446 \subfigure[Object Container]{ 447 \input{Container} 448 \label{f:ObjectContainer} 449 } % subfigure 450 \caption{Header Placement} 451 \label{f:HeaderPlacement} 452 \end{figure} 453 454 An alternative approach for managing objects factors common header/trailer information to a separate location in memory and organizes associated free storage into blocks called \newterm{object containers} (\newterm{superblocks} in~\cite{Berger00}), as in \VRef[Figure]{f:ObjectContainer}. 455 The header for the container holds information necessary for all objects in the container; 456 a trailer may also be used at the end of the container. 457 Similar to the approach described for thread heaps in \VRef{s:MultipleHeaps}, if container boundaries do not overlap with memory of another container at crucial boundaries and all objects in a container are allocated to the same thread, allocator-induced active false-sharing is avoided. 458 459 The difficulty with object containers lies in finding the object header/trailer given only the object address, since that is normally the only information passed to the deallocation operation. 460 One way to do this is to start containers on aligned addresses in memory, then truncate the lower bits of the object address to obtain the header address (or round up and subtract the trailer size to obtain the trailer address). 461 For example, if an object at address 0xFC28\,EF08 is freed and containers are aligned on 64\,KB (0x0001\,0000) addresses, then the container header is at 0xFC28\,0000. 462 463 Normally, a container has homogeneous objects of fixed size, with fixed information in the header that applies to all container objects (\eg object size and ownership). 464 This approach greatly reduces internal fragmentation since far fewer headers are required, and potentially increases spatial locality as a cache line or page holds more objects since the objects are closer together due to the lack of headers. 465 However, although similar objects are close spatially within the same container, different sized objects are further apart in separate containers. 466 Depending on the program, this may or may not improve locality. 467 If the program uses several objects from a small number of containers in its working set, then locality is improved since fewer cache lines and pages are required. 468 If the program uses many containers, there is poor locality, as both caching and paging increase. 469 Another drawback is that external fragmentation may be increased since containers reserve space for objects that may never be allocated by the program, \ie there are often multiple containers for each size only partially full. 470 However, external fragmentation can be reduced by using small containers. 471 472 Containers with heterogeneous objects implies different headers describing them, which complicates the problem of locating a specific header solely by an address. 473 A couple of solutions can be used to implement containers with heterogeneous objects. 474 However, the problem with allowing objects of different sizes is that the number of objects, and therefore headers, in a single container is unpredictable. 475 One solution allocates headers at one end of the container, while allocating objects from the other end of the container; 476 when the headers meet the objects, the container is full. 477 Freed objects cannot be split or coalesced since this causes the number of headers to change. 478 The difficulty in this strategy remains in finding the header for a specific object; 479 in general, a search is necessary to find the object's header among the container headers. 480 A second solution combines the use of container headers and individual object headers. 481 Each object header stores the object's heterogeneous information, such as its size, while the container header stores the homogeneous information, such as the owner when using ownership. 482 This approach allows containers to hold different types of objects, but does not completely separate headers from objects. 483 The benefit of the container in this case is to reduce some redundant information that is factored into the container header. 484 485 In summary, object containers trade off internal fragmentation for external fragmentation by isolating common administration information to remove/reduce internal fragmentation, but at the cost of external fragmentation as some portion of a container may not be used and this portion is unusable for other kinds of allocations. 486 A consequence of this tradeoff is its effect on spatial locality, which can produce positive or negative results depending on program access-patterns. 487 488 489 \subsection{Container Ownership} 490 \label{s:ContainerOwnership} 491 492 Without ownership, objects in a container are deallocated to the heap currently associated with the thread that frees the object. 493 Thus, different objects in a container may be on different heap free-lists (see \VRef[Figure]{f:ContainerNoOwnershipFreelist}). 494 With ownership, all objects in a container belong to the same heap (see \VRef[Figure]{f:ContainerOwnershipFreelist}), so ownership of an object is determined by the container owner. 495 If multiple threads can allocate/free/reallocate adjacent storage in the same heap, all forms of false sharing may occur. 496 Only with the 1:1 model and ownership is active and passive false-sharing avoided (see \VRef{s:Ownership}). 497 Passive false-sharing may still occur, if delayed ownership is used. 498 499 \begin{figure} 500 \centering 501 \subfigure[No Ownership]{ 502 \input{ContainerNoOwnershipFreelist} 503 \label{f:ContainerNoOwnershipFreelist} 504 } % subfigure 505 \vrule 506 \subfigure[Ownership]{ 507 \input{ContainerOwnershipFreelist} 508 \label{f:ContainerOwnershipFreelist} 509 } % subfigure 510 \caption{Free-list Structure with Container Ownership} 511 \end{figure} 512 513 A fragmented heap has multiple containers that may be partially or completely free. 514 A completely free container can become reserved storage and be reset to allocate objects of a new size. 515 When a heap reaches a threshold of free objects, it moves some free storage to the global heap for reuse to prevent heap blowup. 516 Without ownership, when a heap frees objects to the global heap, individual objects must be passed, and placed on the global-heap's free-list. 517 Containers cannot be freed to the global heap unless completely free because 518 519 When a container changes ownership, the ownership of all objects within it change as well. 520 Moving a container involves moving all objects on the heap's free-list in that container to the new owner. 521 This approach can reduce contention for the global heap, since each request for objects from the global heap returns a container rather than individual objects. 522 523 Additional restrictions may be applied to the movement of containers to prevent active false-sharing. 524 For example, in \VRef[Figure]{f:ContainerFalseSharing1}, a container being used by Task$_1$ changes ownership, through the global heap. 525 In \VRef[Figure]{f:ContainerFalseSharing2}, when Task$_2$ allocates an object from the newly acquired container it is actively false-sharing even though no objects are passed among threads. 526 Note, once the object is freed by Task$_1$, no more false sharing can occur until the container changes ownership again. 527 To prevent this form of false sharing, container movement may be restricted to when all objects in the container are free. 528 One implementation approach that increases the freedom to return a free container to the operating system involves allocating containers using a call like @mmap@, which allows memory at an arbitrary address to be returned versus only storage at the end of the contiguous @sbrk@ area. 529 530 \begin{figure} 531 \centering 532 \subfigure[]{ 533 \input{ContainerFalseSharing1} 534 \label{f:ContainerFalseSharing1} 535 } % subfigure 536 \subfigure[]{ 537 \input{ContainerFalseSharing2} 538 \label{f:ContainerFalseSharing2} 539 } % subfigure 540 \caption{Active False-Sharing using Containers} 541 \label{f:ActiveFalseSharingContainers} 542 \end{figure} 543 544 Using containers with ownership increases external fragmentation since a new container for a requested object size must be allocated separately for each thread requesting it. 545 In \VRef[Figure]{f:ExternalFragmentationContainerOwnership}, using object ownership allocates 80\% more space than without ownership. 546 547 \begin{figure} 548 \centering 549 \subfigure[No Ownership]{ 550 \input{ContainerNoOwnership} 551 } % subfigure 552 \\ 553 \subfigure[Ownership]{ 554 \input{ContainerOwnership} 555 } % subfigure 556 \caption{External Fragmentation with Container Ownership} 557 \label{f:ExternalFragmentationContainerOwnership} 558 \end{figure} 559 560 561 \subsection{Container Size} 562 \label{s:ContainerSize} 563 564 One way to control the external fragmentation caused by allocating a large container for a small number of requested objects is to vary the size of the container. 565 As described earlier, container boundaries need to be aligned on addresses that are a power of two to allow easy location of the header (by truncating lower bits). 566 Aligning containers in this manner also determines the size of the container. 567 However, the size of the container has different implications for the allocator. 568 569 The larger the container, the fewer containers are needed, and hence, the fewer headers need to be maintained in memory, improving both internal fragmentation and potentially performance. 570 However, with more objects in a container, there may be more objects that are unallocated, increasing external fragmentation. 571 With smaller containers, not only are there more containers, but a second new problem arises where objects are larger than the container. 572 In general, large objects, \eg greater than 64\,KB, are allocated directly from the operating system and are returned immediately to the operating system to reduce long-term external fragmentation. 573 If the container size is small, \eg 1\,KB, then a 1.5\,KB object is treated as a large object, which is likely to be inappropriate. 574 Ideally, it is best to use smaller containers for smaller objects, and larger containers for medium objects, which leads to the issue of locating the container header. 575 576 In order to find the container header when using different sized containers, a super container is used (see~\VRef[Figure]{f:SuperContainers}). 577 The super container spans several containers, contains a header with information for finding each container header, and starts on an aligned address. 578 Super-container headers are found using the same method used to find container headers by dropping the lower bits of an object address. 579 The containers within a super container may be different sizes or all the same size. 580 If the containers in the super container are different sizes, then the super-container header must be searched to determine the specific container for an object given its address. 581 If all containers in the super container are the same size, \eg 16KB, then a specific container header can be found by a simple calculation. 582 The free space at the end of a super container is used to allocate new containers. 583 584 \begin{figure} 585 \centering 586 \input{SuperContainers} 587 % \includegraphics{diagrams/supercontainer.eps} 588 \caption{Super Containers} 589 \label{f:SuperContainers} 590 \end{figure} 591 592 Minimal internal and external fragmentation is achieved by having as few containers as possible, each being as full as possible. 593 It is also possible to achieve additional benefit by using larger containers for popular small sizes, as it reduces the number of containers with associated headers. 594 However, this approach assumes it is possible for an allocator to determine in advance which sizes are popular. 595 Keeping statistics on requested sizes allows the allocator to make a dynamic decision about which sizes are popular. 596 For example, after receiving a number of allocation requests for a particular size, that size is considered a popular request size and larger containers are allocated for that size. 597 If the decision is incorrect, larger containers than necessary are allocated that remain mostly unused. 598 A programmer may be able to inform the allocator about popular object sizes, using a mechanism like @mallopt@, in order to select an appropriate container size for each object size. 599 600 601 \subsection{Container Free-Lists} 602 \label{s:containersfreelists} 603 604 The container header allows an alternate approach for managing the heap's free-list. 605 Rather than maintain a global free-list throughout the heap (see~\VRef[Figure]{f:GlobalFreeListAmongContainers}), the containers are linked through their headers and only the local free objects within a container are linked together (see~\VRef[Figure]{f:LocalFreeListWithinContainers}). 606 Note, maintaining free lists within a container assumes all free objects in the container are associated with the same heap; 607 thus, this approach only applies to containers with ownership. 608 609 This alternate free-list approach can greatly reduce the complexity of moving all freed objects belonging to a container to another heap. 610 To move a container using a global free-list, as in \VRef[Figure]{f:GlobalFreeListAmongContainers}, the free list is first searched to find all objects within the container. 611 Each object is then removed from the free list and linked together to form a local free-list for the move to the new heap. 612 With local free-lists in containers, as in \VRef[Figure]{f:LocalFreeListWithinContainers}, the container is simply removed from one heap's free list and placed on the new heap's free list. 613 Thus, when using local free-lists, the operation of moving containers is reduced from $O(N)$ to $O(1)$. 614 The cost is adding information to a header, which increases the header size, and therefore internal fragmentation. 615 616 \begin{figure} 617 \centering 618 \subfigure[Global Free-List Among Containers]{ 619 \input{FreeListAmongContainers} 620 \label{f:GlobalFreeListAmongContainers} 621 } % subfigure 622 \hspace{0.25in} 623 \subfigure[Local Free-List Within Containers]{ 624 \input{FreeListWithinContainers} 625 \label{f:LocalFreeListWithinContainers} 626 } % subfigure 627 \caption{Container Free-List Structure} 628 \label{f:ContainerFreeListStructure} 629 \end{figure} 630 631 When all objects in the container are the same size, a single free-list is sufficient. 632 However, when objects in the container are different size, the header needs a free list for each size class when using a binning allocation algorithm, which can be a significant increase in the container-header size. 633 The alternative is to use a different allocation algorithm with a single free-list, such as a sequential-fit allocation-algorithm. 634 635 636 \subsection{Hybrid Private/Public Heap} 637 \label{s:HybridPrivatePublicHeap} 638 639 Section~\Vref{s:Ownership} discusses advantages and disadvantages of public heaps (T:H model and with ownership) and private heaps (thread heaps with ownership). 640 For thread heaps with ownership, it is possible to combine these approaches into a hybrid approach with both private and public heaps (see~\VRef[Figure]{f:HybridPrivatePublicHeap}). 641 The main goal of the hybrid approach is to eliminate locking on thread-local allocation/deallocation, while providing ownership to prevent heap blowup. 642 In the hybrid approach, a task first allocates from its private heap and second from its public heap if no free memory exists in the private heap. 643 Similarly, a task first deallocates an object its private heap, and second to the public heap. 644 Both private and public heaps can allocate/deallocate to/from the global heap if there is no free memory or excess free memory, although an implementation may choose to funnel all interaction with the global heap through one of the heaps. 645 Note, deallocation from the private to the public (dashed line) is unlikely because there is no obvious advantages unless the public heap provides the only interface to the global heap. 646 Finally, when a task frees an object it does not own, the object is either freed immediately to its owner's public heap or put in the freeing task's private heap for delayed ownership, which allows the freeing task to temporarily reuse an object before returning it to its owner or batch objects for an owner heap into a single return. 647 648 \begin{figure} 649 \centering 650 \input{PrivatePublicHeaps.pstex_t} 651 \caption{Hybrid Private/Public Heap for Per-thread Heaps} 652 \label{f:HybridPrivatePublicHeap} 653 % \vspace{10pt} 654 % \input{RemoteFreeList.pstex_t} 655 % \caption{Remote Free-List} 656 % \label{f:RemoteFreeList} 657 \end{figure} 658 659 As mentioned, an implementation may have only one heap deal with the global heap, so the other heap can be simplified. 660 For example, if only the private heap interacts with the global heap, the public heap can be reduced to a lock-protected free-list of objects deallocated by other threads due to ownership, called a \newterm{remote free-list}. 661 To avoid heap blowup, the private heap allocates from the remote free-list when it reaches some threshold or it has no free storage. 662 Since the remote free-list is occasionally cleared during an allocation, this adds to that cost. 663 Clearing the remote free-list is $O(1)$ if the list can simply be added to the end of the private-heap's free-list, or $O(N)$ if some action must be performed for each freed object. 664 665 If only the public heap interacts with other threads and the global heap, the private heap can handle thread-local allocations and deallocations without locking. 666 In this scenario, the private heap must deallocate storage after reaching a certain threshold to the public heap (and then eventually to the global heap from the public heap) or heap blowup can occur. 667 If the public heap does the major management, the private heap can be simplified to provide high-performance thread-local allocations and deallocations. 668 669 The main disadvantage of each thread having both a private and public heap is the complexity of managing two heaps and their interactions in an allocator. 670 Interestingly, heap implementations often focus on either a private or public heap, giving the impression a single versus a hybrid approach is being used. 671 In many case, the hybrid approach is actually being used, but the simpler heap is just folded into the complex heap, even though the operations logically belong in separate heaps. 672 For example, a remote free-list is actually a simple public-heap, but may be implemented as an integral component of the complex private-heap in an allocator, masking the presence of a hybrid approach. 673 674 675 \section{Allocation Buffer} 676 \label{s:AllocationBuffer} 677 678 An allocation buffer is reserved memory (see~\VRef{s:AllocatorComponents}) not yet allocated to the program, and is used for allocating objects when the free list is empty. 679 That is, rather than requesting new storage for a single object, an entire buffer is requested from which multiple objects are allocated later. 680 Both any heap may use an allocation buffer, resulting in allocation from the buffer before requesting objects (containers) from the global heap or operating system, respectively. 681 The allocation buffer reduces contention and the number of global/operating-system calls. 682 For coalescing, a buffer is split into smaller objects by allocations, and recomposed into larger buffer areas during deallocations. 683 684 Allocation buffers are useful initially when there are no freed objects in a heap because many allocations usually occur when a thread starts. 685 Furthermore, to prevent heap blowup, objects should be reused before allocating a new allocation buffer. 686 Thus, allocation buffers are often allocated more frequently at program/thread start, and then their use often diminishes. 687 688 Using an allocation buffer with a thread heap avoids active false-sharing, since all objects in the allocation buffer are allocated to the same thread. 689 For example, if all objects sharing a cache line come from the same allocation buffer, then these objects are allocated to the same thread, avoiding active false-sharing. 690 Active false-sharing may still occur if objects are freed to the global heap and reused by another heap. 691 692 Allocation buffers may increase external fragmentation, since some memory in the allocation buffer may never be allocated. 693 A smaller allocation buffer reduces the amount of external fragmentation, but increases the number of calls to the global heap or operating system. 694 The allocation buffer also slightly increases internal fragmentation, since a pointer is necessary to locate the next free object in the buffer. 695 696 The unused part of a container, neither allocated or freed, is an allocation buffer. 697 For example, when a container is created, rather than placing all objects within the container on the free list, the objects form an allocation buffer and are allocated from the buffer as allocation requests are made. 698 This lazy method of constructing objects is beneficial in terms of paging and caching. 699 For example, although an entire container, possibly spanning several pages, is allocated from the operating system, only a small part of the container is used in the working set of the allocator, reducing the number of pages and cache lines that are brought into higher levels of cache. 700 701 702 \section{Lock-Free Operations} 703 \label{s:LockFreeOperations} 704 705 A lock-free algorithm guarantees safe concurrent-access to a data structure, so that at least one thread can make progress in the system, but an individual task has no bound to execution, and hence, may starve~\cite[pp.~745--746]{Herlihy93}. 706 % A wait-free algorithm puts a finite bound on the number of steps any thread takes to complete an operation, so an individual task cannot starve 707 Lock-free operations can be used in an allocator to reduce or eliminate the use of locks. 708 Locks are a problem for high contention or if the thread holding the lock is preempted and other threads attempt to use that lock. 709 With respect to the heap, these situations are unlikely unless all threads makes extremely high use of dynamic-memory allocation, which can be an indication of poor design. 710 Nevertheless, lock-free algorithms can reduce the number of context switches, since a thread does not yield/block while waiting for a lock; 711 on the other hand, a thread may busy-wait for an unbounded period. 712 Finally, lock-free implementations have greater complexity and hardware dependency. 713 Lock-free algorithms can be applied most easily to simple free-lists, \eg remote free-list, to allow lock-free insertion and removal from the head of a stack. 714 Implementing lock-free operations for more complex data-structures (queue~\cite{Valois94}/deque~\cite{Sundell08}) is more complex. 715 Michael~\cite{Michael04} and Gidenstam \etal \cite{Gidenstam05} have created lock-free variations of the Hoard allocator. 716 2 717 3 718 \noindent … … 23 738 ==================== 24 739 25 \section{Background}26 27 740 % FIXME: cite wasik 28 741 \cite{wasik.thesis} 29 742 30 \s ubsection{Memory Allocation}31 With dynamic allocation being an important feature of C, there are many stand alone memory allocators that have been designed for different purposes. For this thesis, we chose 7 of the most popular and widely used memory allocators.743 \section{Existing Memory Allocators} 744 With dynamic allocation being an important feature of C, there are many stand-alone memory allocators that have been designed for different purposes. For this thesis, we chose 7 of the most popular and widely used memory allocators. 32 745 33 746 \paragraph{dlmalloc} … … 35 748 36 749 \paragraph{hoard} 37 Hoard (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and using a heap layer framework. It has per-thre d heaps that have thread-local free-lists, and a gloabl shared heap. (FIX ME: cite wasik)750 Hoard (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and using a heap layer framework. It has per-thread heaps that have thread-local free-lists, and a global shared heap. (FIX ME: cite wasik) 38 751 39 752 \paragraph{jemalloc} … … 44 757 45 758 \paragraph{rpmalloc} 46 rpmalloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses per-thread heap. Each heap has multiple size-classes and each size-c alss contains memory regions of the relevant size.759 rpmalloc (FIX ME: cite allocator) is a thread-safe allocator that is multi-threaded and uses per-thread heap. Each heap has multiple size-classes and each size-class contains memory regions of the relevant size. 47 760 48 761 \paragraph{tbb malloc} … … 51 764 \paragraph{tc malloc} 52 765 tcmalloc (FIX ME: cite allocator) is a thread-safe allocator. It uses per-thread cache to store free objects that prevents contention on shared resources in multi-threaded application. A central free-list is used to refill per-thread cache when it gets empty. 53 54 \subsection{Benchmarks}55 There are multiple benchmarks that are built individually and evaluate different aspects of a memory allocator. But, there is not standard set of benchamrks that can be used to evaluate multiple aspects of memory allocators.56 57 \paragraph{threadtest}58 (FIX ME: cite benchmark and hoard) Each thread repeatedly allocates and then deallocates 100,000 objects. Runtime of the benchmark evaluates its efficiency.59 60 \paragraph{shbench}61 (FIX ME: cite benchmark and hoard) Each thread allocates and randomly frees a number of random-sized objects. It is a stress test that also uses runtime to determine efficiency of the allocator.62 63 \paragraph{larson}64 (FIX ME: cite benchmark and hoard) Larson simulates a server environment. Multiple threads are created where each thread allocator and free a number of objects within a size range. Some objects are passed from threads to the child threads to free. It caluculates memory operations per second as an indicator of memory allocator's performance.
Note:
See TracChangeset
for help on using the changeset viewer.