Halide  17.0.2
Halide compiler and libraries
memory_resources.h
Go to the documentation of this file.
1 #ifndef HALIDE_RUNTIME_MEMORY_RESOURCES_H
2 #define HALIDE_RUNTIME_MEMORY_RESOURCES_H
3 
4 #include "../HalideRuntime.h"
5 
6 namespace Halide {
7 namespace Runtime {
8 namespace Internal {
9 
10 // --
11 
12 // Hint for allocation usage indicating whether or not the resource
13 // is in use, available, or dedicated (and can't be split or shared)
14 enum class AllocationStatus {
16  InUse,
17  Available,
18  Purgeable,
19  Dedicated
20 };
21 
22 // Hint for allocation requests indicating intended usage
23 // required between host and device address space mappings
24 enum class MemoryVisibility {
25  InvalidVisibility, //< invalid enum value
26  HostOnly, //< host local
27  DeviceOnly, //< device local
28  DeviceToHost, //< transfer from device to host
29  HostToDevice, //< transfer from host to device
30  DefaultVisibility, //< default visibility (use any valid visibility -- unable to determine prior to usage)
31 };
32 
33 // Hint for allocation requests indicating intended update
34 // frequency for modifying the contents of the allocation
35 enum class MemoryUsage {
36  InvalidUsage, //< invalid enum value
37  StaticStorage, //< intended for static storage, whereby the contents will be set once and remain unchanged
38  DynamicStorage, //< intended for dyanmic storage, whereby the contents will be set frequently and change constantly
39  UniformStorage, //< intended for fast & small fixed read-only uniform storage (intended for passing shader parameters), whereby the contents will be set once and remain unchanged
40  TransferSrc, //< intended for staging storage updates, whereby the contents will be used as the source of a transfer
41  TransferDst, //< intended for staging storage updates, whereby the contents will be used as the destination of a transfer
42  TransferSrcDst, //< intended for staging storage updates, whereby the contents will be used either as a source or destination of a transfer
43  DefaultUsage //< default usage (use any valid usage -- unable to determine prior to usage)
44 };
45 
46 // Hint for allocation requests indicating ideal caching support (if available)
47 enum class MemoryCaching {
48  InvalidCaching, //< invalid enum value
49  Cached, //< cached
50  Uncached, //< uncached
51  CachedCoherent, //< cached and coherent
52  UncachedCoherent, //< uncached but still coherent
53  DefaultCaching //< default caching (use any valid caching behaviour -- unable to determine prior to usage)
54 };
55 
60  size_t alignment = 0; //< required alignment of allocations (zero for no constraint)
61  size_t nearest_multiple = 0; //< require the allocation size to round up to the nearest multiple (zero means no rounding)
62 };
63 
64 // Client-facing struct for exchanging memory block allocation requests
65 struct MemoryBlock {
66  void *handle = nullptr; //< client data storing native handle (managed by alloc_block_region/free_block_region)
67  size_t size = 0; //< allocated size (in bytes)
68  bool dedicated = false; //< flag indicating whether allocation is one dedicated resource (or split/shared into other resources)
69  MemoryProperties properties; //< properties for the allocated block
70 };
71 
72 // Client-facing struct for specifying a range of a memory region (eg for crops)
73 struct MemoryRange {
74  size_t head_offset = 0; //< byte offset from start of region
75  size_t tail_offset = 0; //< byte offset from end of region
76 };
77 
78 // Client-facing struct for exchanging memory region allocation requests
79 struct MemoryRegion {
80  void *handle = nullptr; //< client data storing native handle (managed by alloc_block_region/free_block_region) or a pointer to region owning allocation
81  size_t offset = 0; //< offset from base address in block (in bytes)
82  size_t size = 0; //< allocated size (in bytes)
83  MemoryRange range; //< optional range (e.g. for handling crops, etc)
84  bool dedicated = false; //< flag indicating whether allocation is one dedicated resource (or split/shared into other resources)
85  bool is_owner = true; //< flag indicating whether allocation is owned by this region, in which case handle is a native handle. Otherwise handle points to owning region of alloction.
86  MemoryProperties properties; //< properties for the allocated region
87 };
88 
89 // Client-facing struct for issuing memory allocation requests
90 struct MemoryRequest {
91  size_t offset = 0; //< offset from base address in block (in bytes)
92  size_t size = 0; //< allocated size (in bytes)
93  size_t alignment = 0; //< alignment constraint for address
94  bool dedicated = false; //< flag indicating whether allocation is one dedicated resource (or split/shared into other resources)
95  MemoryProperties properties; //< properties for the allocated region
96 };
97 
98 class RegionAllocator;
99 struct BlockRegion;
100 
101 // Internal struct for block resource state
102 // -- Note: first field must MemoryBlock
104  MemoryBlock memory; //< memory info for the allocated block
105  RegionAllocator *allocator = nullptr; //< designated allocator for the block
106  BlockRegion *regions = nullptr; //< head of linked list of memory regions
107  size_t reserved = 0; //< number of bytes already reserved to regions
108 };
109 
110 // Internal struct for block region state
111 // -- Note: first field must MemoryRegion
112 struct BlockRegion {
113  MemoryRegion memory; //< memory info for the allocated region
114  uint32_t usage_count = 0; //< number of active clients using region
115  AllocationStatus status = AllocationStatus::InvalidStatus; //< allocation status indicator
116  BlockRegion *next_ptr = nullptr; //< pointer to next block region in linked list
117  BlockRegion *prev_ptr = nullptr; //< pointer to prev block region in linked list
118  BlockResource *block_ptr = nullptr; //< pointer to parent block resource
119 };
120 
121 // Returns true if given byte alignment is a power of two
123  return (x & (x - 1)) == 0;
124 }
125 
126 // Returns an aligned byte offset to adjust the given offset based on alignment constraints
127 // -- Alignment must be power of two!
128 ALWAYS_INLINE size_t aligned_offset(size_t offset, size_t alignment) {
130  return (alignment == 0) ? (offset) : (offset + (alignment - 1)) & ~(alignment - 1);
131 }
132 
133 // Returns a suitable alignment such that requested alignment is a suitable
134 // integer multiple of the required alignment
135 ALWAYS_INLINE size_t conform_alignment(size_t requested, size_t required) {
136  size_t alignment = max(requested, required);
137  return ((required > 0) && (alignment > required)) ? (required * ((alignment / required) + 1)) : alignment;
138 }
139 
140 // Returns a padded size to accommodate an adjusted offset due to alignment constraints
141 // -- Alignment must be power of two!
142 ALWAYS_INLINE size_t aligned_size(size_t offset, size_t size, size_t alignment) {
143  size_t actual_offset = aligned_offset(offset, alignment);
144  size_t padding = actual_offset - offset;
145  size_t actual_size = padding + size;
146  return actual_size;
147 }
148 
149 // Returns a padded size to accommodate an adjusted offset due to alignment constraints rounded up to the nearest multiple
150 // -- Alignment must be power of two!
151 ALWAYS_INLINE size_t conform_size(size_t offset, size_t size, size_t alignment, size_t nearest_multiple) {
152  size_t adjusted_size = aligned_size(offset, size, alignment);
153  adjusted_size = (alignment > adjusted_size) ? alignment : adjusted_size;
154  if (nearest_multiple > 0) {
155  size_t rounded_size = (((adjusted_size + nearest_multiple - 1) / nearest_multiple) * nearest_multiple);
156  return rounded_size;
157  } else {
158  return adjusted_size;
159  }
160 }
161 
162 // Clamps the given value to be within the [min_value, max_value] range
163 ALWAYS_INLINE size_t clamped_size(size_t value, size_t min_value, size_t max_value) {
164  size_t result = (value < min_value) ? min_value : value;
165  return (result > max_value) ? max_value : result;
166 }
167 
168 // Offset the untyped pointer by the given number of bytes
169 ALWAYS_INLINE const void *offset_address(const void *address, size_t byte_offset) {
170  const uintptr_t base = reinterpret_cast<uintptr_t>(address);
171  return reinterpret_cast<const void *>(base + byte_offset);
172 }
173 
174 // Offset the untyped pointer by the given number of bytes
175 ALWAYS_INLINE void *offset_address(void *address, size_t byte_offset) {
176  const uintptr_t base = reinterpret_cast<uintptr_t>(address);
177  return reinterpret_cast<void *>(base + byte_offset);
178 }
179 
180 // --
181 
182 typedef void *(*AllocateSystemFn)(void *, size_t);
183 typedef void (*DeallocateSystemFn)(void *, void *);
184 
185 ALWAYS_INLINE void *native_system_malloc(void *user_context, size_t bytes) {
186  return malloc(bytes);
187 }
188 
189 ALWAYS_INLINE void native_system_free(void *user_context, void *ptr) {
190  free(ptr);
191 }
192 
196 };
197 
201 };
202 
203 typedef int (*AllocateBlockFn)(void *, MemoryBlock *);
204 typedef int (*DeallocateBlockFn)(void *, MemoryBlock *);
205 typedef int (*ConformBlockRequestFn)(void *, MemoryRequest *);
206 
211 };
212 
213 typedef int (*AllocateRegionFn)(void *, MemoryRegion *);
214 typedef int (*DeallocateRegionFn)(void *, MemoryRegion *);
215 typedef int (*ConformBlockRegionFn)(void *, MemoryRequest *);
216 
221 };
222 
223 // --
224 
225 } // namespace Internal
226 } // namespace Runtime
227 } // namespace Halide
228 
229 // --
230 
231 extern "C" {
232 
234  switch (value) {
235  case MemoryVisibility::InvalidVisibility: {
236  return "InvalidVisibility";
237  }
238  case MemoryVisibility::DefaultVisibility: {
239  return "DefaultVisibility";
240  }
241  case MemoryVisibility::HostOnly: {
242  return "HostOnly";
243  }
244  case MemoryVisibility::DeviceOnly: {
245  return "DeviceOnly";
246  }
247  case MemoryVisibility::HostToDevice: {
248  return "HostToDevice";
249  }
250  case MemoryVisibility::DeviceToHost: {
251  return "DeviceToHost";
252  }
253  default: {
254  return "<unknown memory visibility value>";
255  }
256  };
257  return "<unknown memory visibility value>";
258 }
259 
261  switch (value) {
262  case MemoryUsage::InvalidUsage: {
263  return "InvalidUsage";
264  }
265  case MemoryUsage::DefaultUsage: {
266  return "DefaultUsage";
267  }
268  case MemoryUsage::StaticStorage: {
269  return "StaticStorage";
270  }
271  case MemoryUsage::DynamicStorage: {
272  return "DynamicStorage";
273  }
274  case MemoryUsage::UniformStorage: {
275  return "UniformStorage";
276  }
277  case MemoryUsage::TransferSrc: {
278  return "TransferSrc";
279  }
280  case MemoryUsage::TransferDst: {
281  return "TransferDst";
282  }
283  case MemoryUsage::TransferSrcDst: {
284  return "TransferSrcDst";
285  }
286  default: {
287  return "<unknown memory usage value>";
288  }
289  };
290  return "<unknown memory usage value>";
291 }
292 
294  switch (value) {
295  case MemoryCaching::InvalidCaching: {
296  return "InvalidCaching";
297  }
298  case MemoryCaching::DefaultCaching: {
299  return "DefaultCaching";
300  }
301  case MemoryCaching::Cached: {
302  return "Cached";
303  }
304  case MemoryCaching::Uncached: {
305  return "Uncached";
306  }
307  case MemoryCaching::CachedCoherent: {
308  return "CachedCoherent";
309  }
310  case MemoryCaching::UncachedCoherent: {
311  return "UncachedCoherent";
312  }
313  default: {
314  return "<unknown memory visibility value>";
315  }
316  };
317  return "<unknown memory visibility value>";
318 }
319 
320 } // extern "C"
321 
322 // --
323 
324 #endif // HALIDE_RUNTIME_MEMORY_RESOURCES_H
Expr max(const FuncRef &a, const FuncRef &b)
Explicit overloads of min and max for FuncRef.
Definition: Func.h:606
int(* AllocateBlockFn)(void *, MemoryBlock *)
#define WEAK
int(* DeallocateRegionFn)(void *, MemoryRegion *)
void * halide_malloc(void *user_context, size_t x)
Halide calls these functions to allocate and free memory.
int(* AllocateRegionFn)(void *, MemoryRegion *)
This file defines the class FunctionDAG, which is our representation of a Halide pipeline, and contains methods to using Halide&#39;s bounds tools to query properties of it.
ALWAYS_INLINE size_t conform_alignment(size_t requested, size_t required)
WEAK const char * halide_memory_usage_name(MemoryUsage value)
#define halide_abort_if_false(user_context, cond)
void halide_free(void *user_context, void *ptr)
Halide calls these functions to allocate and free memory.
void *(* AllocateSystemFn)(void *, size_t)
int(* DeallocateBlockFn)(void *, MemoryBlock *)
int(* ConformBlockRequestFn)(void *, MemoryRequest *)
ALWAYS_INLINE size_t aligned_offset(size_t offset, size_t alignment)
unsigned __INT32_TYPE__ uint32_t
void(* DeallocateSystemFn)(void *, void *)
Not visible externally, similar to &#39;static&#39; linkage in C.
ALWAYS_INLINE void native_system_free(void *user_context, void *ptr)
ALWAYS_INLINE size_t clamped_size(size_t value, size_t min_value, size_t max_value)
#define ALWAYS_INLINE
ALWAYS_INLINE void * native_system_malloc(void *user_context, size_t bytes)
ALWAYS_INLINE const void * offset_address(const void *address, size_t byte_offset)
Allocator class interface for sub-allocating a contiguous memory block into smaller regions of memory...
void * malloc(size_t)
__SIZE_TYPE__ size_t
ALWAYS_INLINE size_t conform_size(size_t offset, size_t size, size_t alignment, size_t nearest_multiple)
int(* ConformBlockRegionFn)(void *, MemoryRequest *)
ALWAYS_INLINE bool is_power_of_two_alignment(size_t x)
WEAK const char * halide_memory_visibility_name(MemoryVisibility value)
__UINTPTR_TYPE__ uintptr_t
ALWAYS_INLINE size_t aligned_size(size_t offset, size_t size, size_t alignment)
WEAK const char * halide_memory_caching_name(MemoryCaching value)
void free(void *)