| // Written in the D programming language. | |
| /** | |
| $(H2 Assembling Your Own Allocator) | |
| This package also implements | |
| untyped composable memory allocators. They are $(I untyped) because they deal | |
| exclusively in `void[]` and have no notion of what type the memory allocated | |
| would be destined for. They are $(I composable) because the included allocators | |
| are building blocks that can be assembled in complex nontrivial allocators. | |
| $(P Unlike the allocators for the C and C++ programming languages, which manage | |
| the allocated size internally, these allocators require that the client | |
| maintains (or knows $(I a priori)) the allocation size for each piece of memory | |
| allocated. Put simply, the client must pass the allocated size upon | |
| deallocation. Storing the size in the _allocator has significant negative | |
| performance implications, and is virtually always redundant because client code | |
| needs knowledge of the allocated size in order to avoid buffer overruns. (See | |
| more discussion in a $(HTTP open- | |
| std.org/JTC1/SC22/WG21/docs/papers/2013/n3536.html, proposal) for sized | |
| deallocation in C++.) For this reason, allocators herein traffic in `void[]` | |
| as opposed to `void*`.) | |
| $(P In order to be usable as an _allocator, a type should implement the | |
| following methods with their respective semantics. Only `alignment` and $(D | |
| allocate) are required. If any of the other methods is missing, the _allocator | |
| is assumed to not have that capability (for example some allocators do not offer | |
| manual deallocation of memory). Allocators should NOT implement | |
| unsupported methods to always fail. For example, an allocator that lacks the | |
| capability to implement `alignedAllocate` should not define it at all (as | |
| opposed to defining it to always return `null` or throw an exception). The | |
| missing implementation statically informs other components about the | |
| allocator's capabilities and allows them to make design decisions accordingly.) | |
| $(BOOKTABLE , | |
| $(TR $(TH Method name) $(TH Semantics)) | |
| $(TR $(TDC uint alignment;, $(POST $(RES) > 0)) $(TD Returns the minimum | |
| alignment of all data returned by the allocator. An allocator may implement $(D | |
| alignment) as a statically-known `enum` value only. Applications that need | |
| dynamically-chosen alignment values should use the `alignedAllocate` and $(D | |
| alignedReallocate) APIs.)) | |
| $(TR $(TDC size_t goodAllocSize(size_t n);, $(POST $(RES) >= n)) $(TD Allocators | |
| customarily allocate memory in discretely-sized chunks. Therefore, a request for | |
| `n` bytes may result in a larger allocation. The extra memory allocated goes | |
| unused and adds to the so-called $(HTTPS en.wikipedia.org/wiki/Fragmentation_(computing)#Internal_fragmentation,internal fragmentation). | |
| The function `goodAllocSize(n)` returns the actual number of bytes that would | |
| be allocated upon a request for `n` bytes. This module defines a default | |
| implementation that returns `n` rounded up to a multiple of the allocator's | |
| alignment.)) | |
| $(TR $(TDC void[] allocate(size_t s);, $(POST $(RES) is null || $(RES).length == | |
| s)) $(TD If $(D s == 0), the call may return any empty slice (including $(D | |
| null)). Otherwise, the call allocates `s` bytes of memory and returns the | |
| allocated block, or `null` if the request could not be satisfied.)) | |
| $(TR $(TDC void[] alignedAllocate(size_t s, uint a);, $(POST $(RES) is null || | |
| $(RES).length == s)) $(TD Similar to `allocate`, with the additional | |
| guarantee that the memory returned is aligned to at least `a` bytes. `a` | |
| must be a power of 2.)) | |
| $(TR $(TDC void[] allocateAll();) $(TD Offers all of allocator's memory to the | |
| caller, so it's usually defined by fixed-size allocators. If the allocator is | |
| currently NOT managing any memory, then `allocateAll()` shall allocate and | |
| return all memory available to the allocator, and subsequent calls to all | |
| allocation primitives should not succeed (e.g. `allocate` shall return $(D | |
| null) etc). Otherwise, `allocateAll` only works on a best-effort basis, and | |
| the allocator is allowed to return `null` even if does have available memory. | |
| Memory allocated with `allocateAll` is not otherwise special (e.g. can be | |
| reallocated or deallocated with the usual primitives, if defined).)) | |
| $(TR $(TDC bool expand(ref void[] b, size_t delta);, $(POST !$(RES) || b.length | |
| == $(I old)(b).length + delta)) $(TD Expands `b` by `delta` bytes. If $(D | |
| delta == 0), succeeds without changing `b`. If $(D b is null), returns | |
| `false` (the null pointer cannot be expanded in place). Otherwise, $(D | |
| b) must be a buffer previously allocated with the same allocator. If expansion | |
| was successful, `expand` changes `b`'s length to $(D b.length + delta) and | |
| returns `true`. Upon failure, the call effects no change upon the allocator | |
| object, leaves `b` unchanged, and returns `false`.)) | |
| $(TR $(TDC bool reallocate(ref void[] b, size_t s);, $(POST !$(RES) || b.length | |
| == s)) $(TD Reallocates `b` to size `s`, possibly moving memory around. | |
| `b` must be `null` or a buffer allocated with the same allocator. If | |
| reallocation was successful, `reallocate` changes `b` appropriately and | |
| returns `true`. Upon failure, the call effects no change upon the allocator | |
| object, leaves `b` unchanged, and returns `false`. An allocator should | |
| implement `reallocate` if it can derive some advantage from doing so; | |
| otherwise, this module defines a `reallocate` free function implemented in | |
| terms of `expand`, `allocate`, and `deallocate`.)) | |
| $(TR $(TDC bool alignedReallocate(ref void[] b,$(BR) size_t s, uint a);, $(POST | |
| !$(RES) || b.length == s)) $(TD Similar to `reallocate`, but guarantees the | |
| reallocated memory is aligned at `a` bytes. The buffer must have been | |
| originated with a call to `alignedAllocate`. `a` must be a power of 2 | |
| greater than `(void*).sizeof`. An allocator should implement $(D | |
| alignedReallocate) if it can derive some advantage from doing so; otherwise, | |
| this module defines a `alignedReallocate` free function implemented in terms | |
| of `expand`, `alignedAllocate`, and `deallocate`.)) | |
| $(TR $(TDC Ternary owns(void[] b);) $(TD Returns `Ternary.yes` if `b` has been | |
| allocated with this allocator. An allocator should define this method only if it | |
| can decide on ownership precisely and fast (in constant time, logarithmic time, | |
| or linear time with a low multiplication factor). Traditional allocators such as | |
| the C heap do not define such functionality. If $(D b is null), the allocator | |
| shall return `Ternary.no`, i.e. no allocator owns the `null` slice.)) | |
| $(TR $(TDC Ternary resolveInternalPointer(void* p, ref void[] result);) $(TD If | |
| `p` is a pointer somewhere inside a block allocated with this allocator, | |
| `result` holds a pointer to the beginning of the allocated block and returns | |
| `Ternary.yes`. Otherwise, `result` holds `null` and returns `Ternary.no`. | |
| If the pointer points immediately after an allocated block, the result is | |
| implementation defined.)) | |
| $(TR $(TDC bool deallocate(void[] b);) $(TD If $(D b is null), does | |
| nothing and returns `true`. Otherwise, deallocates memory previously allocated | |
| with this allocator and returns `true` if successful, `false` otherwise. An | |
| implementation that would not support deallocation (i.e. would always return | |
| `false` should not define this primitive at all.))) | |
| $(TR $(TDC bool deallocateAll();, $(POST empty)) $(TD Deallocates all memory | |
| allocated with this allocator. If an allocator implements this method, it must | |
| specify whether its destructor calls it, too.)) | |
| $(TR $(TDC Ternary empty();) $(TD Returns `Ternary.yes` if and only if the | |
| allocator holds no memory (i.e. no allocation has occurred, or all allocations | |
| have been deallocated).)) | |
| $(TR $(TDC static Allocator instance;, $(POST instance $(I is a valid) | |
| Allocator $(I object))) $(TD Some allocators are $(I monostate), i.e. have only | |
| an instance and hold only global state. (Notable examples are C's own | |
| `malloc`-based allocator and D's garbage-collected heap.) Such allocators must | |
| define a static `instance` instance that serves as the symbolic placeholder | |
| for the global instance of the allocator. An allocator should not hold state | |
| and define `instance` simultaneously. Depending on whether the allocator is | |
| thread-safe or not, this instance may be `shared`.)) | |
| ) | |
| $(H2 Sample Assembly) | |
| The example below features an _allocator modeled after $(HTTP jemalloc.net/, | |
| jemalloc), which uses a battery of free-list allocators spaced so as to keep | |
| internal fragmentation to a minimum. The `FList` definitions specify no | |
| bounds for the freelist because the `Segregator` does all size selection in | |
| advance. | |
| Sizes through 3584 bytes are handled via freelists of staggered sizes. Sizes | |
| from 3585 bytes through 4072 KB are handled by a `BitmappedBlock` with a | |
| block size of 4 KB. Sizes above that are passed direct to the `GCAllocator`. | |
| $(RUNNABLE_EXAMPLE | |
| ---- | |
| import std.experimental.allocator; | |
| import std.algorithm.comparison : max; | |
| alias FList = FreeList!(GCAllocator, 0, unbounded); | |
| alias A = Segregator!( | |
| 8, FreeList!(GCAllocator, 0, 8), | |
| 128, Bucketizer!(FList, 1, 128, 16), | |
| 256, Bucketizer!(FList, 129, 256, 32), | |
| 512, Bucketizer!(FList, 257, 512, 64), | |
| 1024, Bucketizer!(FList, 513, 1024, 128), | |
| 2048, Bucketizer!(FList, 1025, 2048, 256), | |
| 3584, Bucketizer!(FList, 2049, 3584, 512), | |
| 4072 * 1024, AllocatorList!(n => Region!GCAllocator(max(n, 1024 * 4096))), | |
| GCAllocator | |
| ); | |
| A tuMalloc; | |
| auto b = tuMalloc.allocate(500); | |
| assert(b.length == 500); | |
| auto c = tuMalloc.allocate(113); | |
| assert(c.length == 113); | |
| assert(tuMalloc.expand(c, 14)); | |
| tuMalloc.deallocate(b); | |
| tuMalloc.deallocate(c); | |
| ---- | |
| ) | |
| $(H2 Allocating memory for sharing across threads) | |
| One allocation pattern used in multithreaded applications is to share memory | |
| across threads, and to deallocate blocks in a different thread than the one that | |
| allocated it. | |
| All allocators in this module accept and return `void[]` (as opposed to | |
| $(D shared void[])). This is because at the time of allocation, deallocation, or | |
| reallocation, the memory is effectively not `shared` (if it were, it would | |
| reveal a bug at the application level). | |
| The issue remains of calling `a.deallocate(b)` from a different thread than | |
| the one that allocated `b`. It follows that both threads must have access to | |
| the same instance `a` of the respective allocator type. By definition of D, | |
| this is possible only if `a` has the `shared` qualifier. It follows that | |
| the allocator type must implement `allocate` and `deallocate` as $(D | |
| shared) methods. That way, the allocator commits to allowing usable `shared` | |
| instances. | |
| Conversely, allocating memory with one non-`shared` allocator, passing it | |
| across threads (by casting the obtained buffer to `shared`), and later | |
| deallocating it in a different thread (either with a different allocator object | |
| or with the same allocator object after casting it to `shared`) is illegal. | |
| $(H2 Building Blocks) | |
| $(P The table below gives a synopsis of predefined allocator building blocks, | |
| with their respective modules. Either `import` the needed modules individually, | |
| or `import` `std.experimental.building_blocks`, which imports them all | |
| `public`ly. The building blocks can be assembled in unbounded ways and also | |
| combined with your own. For a collection of typical and useful preassembled | |
| allocators and for inspiration in defining more such assemblies, refer to | |
| $(MREF std,experimental,allocator,showcase).) | |
| $(BOOKTABLE, | |
| $(TR $(TH Allocator$(BR)) $(TH Description)) | |
| $(TR $(TDC2 NullAllocator, null_allocator) $(TD Very good at doing absolutely nothing. A good | |
| starting point for defining other allocators or for studying the API.)) | |
| $(TR $(TDC3 GCAllocator, gc_allocator) $(TD The system-provided garbage-collector allocator. | |
| This should be the default fallback allocator tapping into system memory. It | |
| offers manual `free` and dutifully collects litter.)) | |
| $(TR $(TDC3 Mallocator, mallocator) $(TD The C heap _allocator, a.k.a. $(D | |
| malloc)/`realloc`/`free`. Use sparingly and only for code that is unlikely | |
| to leak.)) | |
| $(TR $(TDC3 AlignedMallocator, mallocator) $(TD Interface to OS-specific _allocators that | |
| support specifying alignment: | |
| $(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, `posix_memalign`) | |
| on Posix and $(HTTP msdn.microsoft.com/en-us/library/fs9stz4e(v=vs.80).aspx, | |
| `__aligned_xxx`) on Windows.)) | |
| $(TR $(TDC2 AlignedBlockList, aligned_block_list) $(TD A wrapper around a list of allocators | |
| which allow for very fast deallocations.)) | |
| $(TR $(TDC2 AffixAllocator, affix_allocator) $(TD Allocator that allows and manages allocating | |
| extra prefix and/or a suffix bytes for each block allocated.)) | |
| $(TR $(TDC2 BitmappedBlock, bitmapped_block) $(TD Organizes one contiguous chunk of memory in | |
| equal-size blocks and tracks allocation status at the cost of one bit per | |
| block.)) | |
| $(TR $(TDC2 FallbackAllocator, fallback_allocator) $(TD Allocator that combines two other | |
| allocators - primary and fallback. Allocation requests are first tried with primary, and | |
| upon failure are passed to the fallback. Useful for small and fast allocators | |
| fronting general-purpose ones.)) | |
| $(TR $(TDC2 FreeList, free_list) $(TD Allocator that implements a $(HTTP | |
| wikipedia.org/wiki/Free_list, free list) on top of any other allocator. The | |
| preferred size, tolerance, and maximum elements are configurable at compile- and | |
| run time.)) | |
| $(TR $(TDC2 SharedFreeList, free_list) $(TD Same features as `FreeList`, but packaged as | |
| a `shared` structure that is accessible to several threads.)) | |
| $(TR $(TDC2 FreeTree, free_tree) $(TD Allocator similar to `FreeList` that uses a | |
| binary search tree to adaptively store not one, but many free lists.)) | |
| $(TR $(TDC2 Region, region) $(TD Region allocator organizes a chunk of memory as a | |
| simple bump-the-pointer allocator.)) | |
| $(TR $(TDC2 InSituRegion, region) $(TD Region holding its own allocation, most often on | |
| the stack. Has statically-determined size.)) | |
| $(TR $(TDC2 SbrkRegion, region) $(TD Region using $(D $(LINK2 https://en.wikipedia.org/wiki/Sbrk, | |
| sbrk)) for allocating memory.)) | |
| $(TR $(TDC3 MmapAllocator, mmap_allocator) $(TD Allocator using | |
| $(D $(LINK2 https://en.wikipedia.org/wiki/Mmap, mmap)) directly.)) | |
| $(TR $(TDC2 StatsCollector, stats_collector) $(TD Collect statistics about any other | |
| allocator.)) | |
| $(TR $(TDC2 Quantizer, quantizer) $(TD Allocates in coarse-grained quantas, thus | |
| improving performance of reallocations by often reallocating in place. The drawback is higher memory consumption because of allocated and unused memory.)) | |
| $(TR $(TDC2 AllocatorList, allocator_list) $(TD Given an allocator factory, lazily creates as | |
| many allocators as needed to satisfy allocation requests. The allocators are | |
| stored in a linked list. Requests for allocation are satisfied by searching the | |
| list in a linear manner.)) | |
| $(TR $(TDC2 Segregator, segregator) $(TD Segregates allocation requests by size | |
| and dispatches them to distinct allocators.)) | |
| $(TR $(TDC2 Bucketizer, bucketizer) $(TD Divides allocation sizes in discrete buckets and | |
| uses an array of allocators, one per bucket, to satisfy requests.)) | |
| $(TR $(TDC2 AscendingPageAllocator, ascending_page_allocator) $(TD A memory safe allocator | |
| where sizes are rounded to a multiple of the page size and allocations are satisfied at increasing addresses.)) | |
| $(COMMENT $(TR $(TDC2 InternalPointersTree) $(TD Adds support for resolving internal | |
| pointers on top of another allocator.))) | |
| ) | |
| Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/package.d) | |
| Macros: | |
| MYREF2 = $(REF_SHORT $1, std,experimental,allocator,building_blocks,$2) | |
| MYREF3 = $(REF_SHORT $1, std,experimental,allocator,$2) | |
| TDC = $(TDNW `$1`$+) | |
| TDC2 = $(TDNW $(D $(MYREF2 $1,$+))$(BR)$(SMALL | |
| `std.experimental.allocator.building_blocks.$2`)) | |
| TDC3 = $(TDNW $(D $(MYREF3 $1,$+))$(BR)$(SMALL | |
| `std.experimental.allocator.$2`)) | |
| RES = $(I result) | |
| POST = $(BR)$(SMALL $(I Post:) $(BLUE `$0`)) | |
| */ | |
| module std.experimental.allocator.building_blocks; | |
| public import | |
| std.experimental.allocator.building_blocks.affix_allocator, | |
| std.experimental.allocator.building_blocks.aligned_block_list, | |
| std.experimental.allocator.building_blocks.allocator_list, | |
| std.experimental.allocator.building_blocks.ascending_page_allocator, | |
| std.experimental.allocator.building_blocks.bucketizer, | |
| std.experimental.allocator.building_blocks.fallback_allocator, | |
| std.experimental.allocator.building_blocks.free_list, | |
| std.experimental.allocator.building_blocks.free_tree, | |
| std.experimental.allocator.gc_allocator, | |
| std.experimental.allocator.building_blocks.bitmapped_block, | |
| std.experimental.allocator.building_blocks.kernighan_ritchie, | |
| std.experimental.allocator.mallocator, | |
| std.experimental.allocator.mmap_allocator, | |
| std.experimental.allocator.building_blocks.null_allocator, | |
| std.experimental.allocator.building_blocks.quantizer, | |
| std.experimental.allocator.building_blocks.region, | |
| std.experimental.allocator.building_blocks.segregator, | |
| std.experimental.allocator.building_blocks.stats_collector; |