| /** |
| $(H2 Assembling Your Own Allocator) |
| |
| In addition to defining the interfaces above, this package also implements |
| untyped composable memory allocators. They are $(I untyped) because they deal |
| exclusively in $(D 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 $(D void[]) |
| as opposed to $(D void*).) |
| |
| $(P In order to be usable as an _allocator, a type should implement the |
| following methods with their respective semantics. Only $(D 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 $(D enum) value only. Applications that need |
| dynamically-chosen alignment values should use the $(D 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 |
| $(D n) bytes may result in a larger allocation. The extra memory allocated goes |
| unused and adds to the so-called $(HTTP goo.gl/YoKffF,internal fragmentation). |
| The function $(D goodAllocSize(n)) returns the actual number of bytes that would |
| be allocated upon a request for $(D n) bytes. This module defines a default |
| implementation that returns $(D 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 $(D s) bytes of memory and returns the |
| allocated block, or $(D 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 $(D allocateAll()) shall allocate and |
| return all memory available to the allocator, and subsequent calls to all |
| allocation primitives should not succeed (e.g. $(D allocate) shall return $(D |
| null) etc). Otherwise, $(D allocateAll) only works on a best-effort basis, and |
| the allocator is allowed to return $(D null) even if does have available memory. |
| Memory allocated with $(D 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 $(D b) by $(D delta) bytes. If $(D |
| delta == 0), succeeds without changing $(D 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, $(D expand) changes $(D b)'s length to $(D b.length + delta) and |
| returns $(D true). Upon failure, the call effects no change upon the allocator |
| object, leaves $(D b) unchanged, and returns $(D false).)) |
| |
| $(TR $(TDC bool reallocate(ref void[] b, size_t s);, $(POST !$(RES) || b.length |
| == s)) $(TD Reallocates $(D b) to size $(D s), possibly moving memory around. |
| $(D b) must be $(D null) or a buffer allocated with the same allocator. If |
| reallocation was successful, $(D reallocate) changes $(D b) appropriately and |
| returns $(D true). Upon failure, the call effects no change upon the allocator |
| object, leaves $(D b) unchanged, and returns $(D false). An allocator should |
| implement $(D reallocate) if it can derive some advantage from doing so; |
| otherwise, this module defines a $(D reallocate) free function implemented in |
| terms of $(D expand), $(D allocate), and $(D deallocate).)) |
| |
| $(TR $(TDC bool alignedReallocate(ref void[] b,$(BR) size_t s, uint a);, $(POST |
| !$(RES) || b.length == s)) $(TD Similar to $(D reallocate), but guarantees the |
| reallocated memory is aligned at $(D a) bytes. The buffer must have been |
| originated with a call to $(D alignedAllocate). $(D a) must be a power of 2 |
| greater than $(D (void*).sizeof). An allocator should implement $(D |
| alignedReallocate) if it can derive some advantage from doing so; otherwise, |
| this module defines a $(D alignedReallocate) free function implemented in terms |
| of $(D expand), $(D alignedAllocate), and $(D 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 $(D 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 $(D shared).)) |
| ) |
| |
| $(H2 Sample Assembly) |
| |
| The example below features an _allocator modeled after $(HTTP goo.gl/m7329l, |
| jemalloc), which uses a battery of free-list allocators spaced so as to keep |
| internal fragmentation to a minimum. The $(D FList) definitions specify no |
| bounds for the freelist because the $(D 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 $(D BitmappedBlock) with a |
| block size of 4 KB. Sizes above that are passed direct to the $(D GCAllocator). |
| |
| ---- |
| 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!( |
| () => BitmappedBlock!(GCAllocator, 4096)(4072 * 1024)), |
| 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 $(D void[]) (as opposed to |
| $(D shared void[])). This is because at the time of allocation, deallocation, or |
| reallocation, the memory is effectively not $(D shared) (if it were, it would |
| reveal a bug at the application level). |
| |
| The issue remains of calling $(D a.deallocate(b)) from a different thread than |
| the one that allocated $(D b). It follows that both threads must have access to |
| the same instance $(D a) of the respective allocator type. By definition of D, |
| this is possible only if $(D a) has the $(D shared) qualifier. It follows that |
| the allocator type must implement $(D allocate) and $(D deallocate) as $(D |
| shared) methods. That way, the allocator commits to allowing usable $(D shared) |
| instances. |
| |
| Conversely, allocating memory with one non-$(D shared) allocator, passing it |
| across threads (by casting the obtained buffer to $(D 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 $(D 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 $(D free) and dutifully collects litter.)) |
| |
| $(TR $(TDC3 Mallocator, mallocator) $(TD The C heap _allocator, a.k.a. $(D |
| malloc)/$(D realloc)/$(D 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, $(D posix_memalign)) |
| on Posix and $(HTTP msdn.microsoft.com/en-us/library/fs9stz4e(v=vs.80).aspx, |
| $(D __aligned_xxx)) on Windows.)) |
| |
| $(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 $(D FreeList), but packaged as |
| a $(D shared) structure that is accessible to several threads.)) |
| |
| $(TR $(TDC2 FreeTree, free_tree) $(TD Allocator similar to $(D 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.)) |
| |
| $(COMMENT $(TR $(TDC2 InternalPointersTree) $(TD Adds support for resolving internal |
| pointers on top of another allocator.))) |
| ) |
| |
| Macros: |
| MYREF2 = $(REF_SHORT $1, std,experimental,allocator,building_blocks,$2) |
| MYREF3 = $(REF_SHORT $1, std,experimental,allocator,$2) |
| TDC = $(TDNW $(D $1)$+) |
| TDC2 = $(TDNW $(D $(MYREF2 $1,$+))$(BR)$(SMALL |
| $(D std.experimental.allocator.building_blocks.$2))) |
| TDC3 = $(TDNW $(D $(MYREF3 $1,$+))$(BR)$(SMALL |
| $(D std.experimental.allocator.$2))) |
| RES = $(I result) |
| POST = $(BR)$(SMALL $(I Post:) $(BLUE $(D $0))) |
| */ |
| |
| module std.experimental.allocator.building_blocks; |
| |
| public import |
| std.experimental.allocator.building_blocks.affix_allocator, |
| std.experimental.allocator.building_blocks.allocator_list, |
| 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; |