Deepity
Ultra-fast, SIMD-accelerated Predictive Coding
Loading...
Searching...
No Matches
MemoryArena.h
Go to the documentation of this file.
1#pragma once
2#include <cstddef>
3#include <cstdlib>
4#include <new>
5#include <stdexcept>
6#if defined(_WIN32)
7#include <malloc.h>
8#include <windows.h>
9#else
10#include <sys/mman.h>
11#endif
12
29
30namespace Deep
31{
32 namespace detail
33 {
34 // std::aligned_alloc isn't reliably available on Windows/MinGW: the
35 // standard requires memory from it be freed with plain free(), but
36 // Windows' native aligned allocator needs the opposite pairing
37 // (_aligned_malloc / _aligned_free), so many MinGW toolchains simply
38 // omit std::aligned_alloc rather than violate the standard's contract.
39
53 inline void *AlignedAllocPortable(size_t alignment, size_t size)
54 {
55#if defined(_WIN32)
56 return _aligned_malloc(size, alignment); // note: (size, alignment) -- reversed vs aligned_alloc
57#else
58 return std::aligned_alloc(alignment, size);
59#endif
60 }
61
66 inline void AlignedFreePortable(void *ptr)
67 {
68#if defined(_WIN32)
69 _aligned_free(ptr);
70#else
71 std::free(ptr);
72#endif
73 }
74
97 inline void *HugePageAllocPortable(size_t size, bool &succeeded)
98 {
99 succeeded = false;
100#if defined(_WIN32)
101 void *ptr = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
102 if (ptr)
103 succeeded = true;
104 return ptr;
105#else
106 void *ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE,
107 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
108 if (ptr == MAP_FAILED)
109 return nullptr;
110
111 // Best-effort hint -- if this fails, we still have valid,
112 // page-aligned (though not huge-page-backed) memory from the
113 // mmap itself, so this isn't treated as a hard failure.
114 madvise(ptr, size, MADV_HUGEPAGE);
115 succeeded = true;
116 return ptr;
117#endif
118 }
119
126 inline void HugePageFreePortable(void *ptr, size_t size)
127 {
128#if defined(_WIN32)
129 VirtualFree(ptr, 0, MEM_RELEASE);
130#else
131 munmap(ptr, size);
132#endif
133 }
134 } // namespace detail
135
141 {
142 private:
144 float *base_ptr;
147 size_t capacity_bytes;
151 size_t offset_bytes;
157 bool used_huge_pages;
158
159 public:
175 explicit MemoryArena(size_t total_floats, bool use_huge_pages = false)
176 : used_huge_pages(false)
177 {
178 // Guarantee total capacity is a multiple of 64 bytes
179 capacity_bytes = (total_floats * sizeof(float) + 63) & ~63;
180
181 if (use_huge_pages)
182 {
183 bool succeeded = false;
184 base_ptr = static_cast<float *>(detail::HugePageAllocPortable(capacity_bytes, succeeded));
185 used_huge_pages = succeeded;
186 }
187
188 if (!used_huge_pages)
189 {
190 // Either huge pages weren't requested, or the request
191 // wasn't honored (mmap/VirtualAlloc failed, or the
192 // system lacks the required configuration/privilege) --
193 // fall back to the standard aligned allocator rather
194 // than treating this as a hard failure.
195 base_ptr = static_cast<float *>(detail::AlignedAllocPortable(64, capacity_bytes));
196 }
197
198 if (!base_ptr)
199 {
200 throw std::bad_alloc();
201 }
202 offset_bytes = 0;
203 }
204
209 {
210 if (base_ptr)
211 {
212 if (used_huge_pages)
213 detail::HugePageFreePortable(base_ptr, capacity_bytes);
214 else
216 }
217 }
218
219 // Delete copy/move constructors to prevent double-free corruption
220
223 MemoryArena(const MemoryArena &) = delete;
227
239 float *AllocateFloats(size_t num_floats)
240 {
241 // Calculate how many bytes we need, padded to the nearest 64-byte boundary
242 size_t allocation_size = (num_floats * sizeof(float) + 63) & ~63;
243
244 if (offset_bytes + allocation_size > capacity_bytes)
245 {
246 throw std::runtime_error("Fatal: MemoryArena capacity exceeded during allocation.");
247 }
248
249 float *chunk = reinterpret_cast<float *>(
250 reinterpret_cast<char *>(base_ptr) + offset_bytes);
251
252 offset_bytes += allocation_size;
253 return chunk;
254 }
255
259 size_t GetUsedBytes() const { return offset_bytes; }
262 size_t GetCapacityBytes() const { return capacity_bytes; }
270 bool UsedHugePages() const { return used_huge_pages; }
271 };
272} // namespace Deep
size_t GetUsedBytes() const
Returns how many bytes have been allocated from the arena so far.
Definition MemoryArena.h:259
MemoryArena(const MemoryArena &)=delete
Deleted: MemoryArena owns a single aligned allocation, so copying would risk a double-free.
~MemoryArena()
Frees the underlying allocation, via whichever deallocator matches the allocator actually used (see u...
Definition MemoryArena.h:208
MemoryArena(size_t total_floats, bool use_huge_pages=false)
Allocates a single 64-byte-aligned buffer large enough to hold total_floats floats,...
Definition MemoryArena.h:175
bool UsedHugePages() const
Returns whether this arena's memory is actually huge-page-backed. Since huge pages are requested on a...
Definition MemoryArena.h:270
MemoryArena & operator=(const MemoryArena &)=delete
Deleted: MemoryArena owns a single aligned allocation, so copy-assignment would risk a double-free.
size_t GetCapacityBytes() const
Returns the arena's total capacity.
Definition MemoryArena.h:262
float * AllocateFloats(size_t num_floats)
Allocates a 64-byte aligned chunk of floats from the arena.
Definition MemoryArena.h:239
Definition MemoryArena.h:33
void * HugePageAllocPortable(size_t size, bool &succeeded)
Attempts to allocate size bytes backed by huge pages. This is genuinely opt-in and best-effort: on Li...
Definition MemoryArena.h:97
void HugePageFreePortable(void *ptr, size_t size)
Frees a block previously returned by HugePageAllocPortable() with succeeded=true.
Definition MemoryArena.h:126
void AlignedFreePortable(void *ptr)
Frees a block previously returned by AlignedAllocPortable().
Definition MemoryArena.h:66
void * AlignedAllocPortable(size_t alignment, size_t size)
Allocates a block of memory aligned to alignment bytes, using whichever aligned-allocation API is act...
Definition MemoryArena.h:53
This file exists to provide both Tensor.h and IComputeBackend.h with device types without introducing...
Definition Backend.cpp:10