1 #ifndef HALIDE_RUNTIME_REGION_ALLOCATOR_H
2 #define HALIDE_RUNTIME_REGION_ALLOCATOR_H
4 #include "../HalideRuntime.h"
5 #include "../printer.h"
52 bool collect(
void *user_context);
53 int release(
void *user_context);
54 int destroy(
void *user_context);
76 bool can_split(
const BlockRegion *region,
size_t size)
const;
82 BlockRegion *create_block_region(
void *user_context,
const MemoryProperties &properties,
size_t offset,
size_t size,
bool dedicated);
85 int destroy_block_region(
void *user_context,
BlockRegion *region);
88 int alloc_block_region(
void *user_context,
BlockRegion *region);
91 int release_block_region(
void *user_context,
BlockRegion *region);
94 int free_block_region(
void *user_context,
BlockRegion *region);
97 bool is_last_block_region(
void *user_context,
const BlockRegion *region)
const;
103 bool is_block_region_suitable_for_request(
void *user_context,
const BlockRegion *region,
const MemoryRequest &request)
const;
106 size_t region_count(
void *user_context)
const;
118 if (result ==
nullptr) {
129 instance->
destroy(user_context);
135 int RegionAllocator::initialize(
void *user_context,
BlockResource *mb,
const MemoryAllocators &ma) {
141 block->
regions = create_block_region(
154 if (remaining < actual_size) {
155 #ifdef DEBUG_RUNTIME_INTERNAL
156 debug(user_context) <<
"RegionAllocator: Unable to reserve more memory from block "
157 <<
"-- requested size (" << (
int32_t)(request.
size) <<
" bytes) "
158 <<
"greater than available (" << (
int32_t)(remaining) <<
" bytes)!\n";
163 BlockRegion *block_region = find_block_region(user_context, request);
164 if (block_region ==
nullptr) {
165 #ifdef DEBUG_RUNTIME_INTERNAL
166 debug(user_context) <<
"RegionAllocator: Failed to locate region for requested size ("
172 if (can_split(block_region, request.
size)) {
173 #ifdef DEBUG_RUNTIME_INTERNAL
174 debug(user_context) <<
"RegionAllocator: Splitting region of size ( " << (
int32_t)(block_region->
memory.
size) <<
") "
175 <<
"to accomodate requested size (" << (
int32_t)(request.
size) <<
" bytes)!\n";
177 split_block_region(user_context, block_region, request.
size, request.
alignment);
180 alloc_block_region(user_context, block_region);
191 return release_block_region(user_context, block_region);
201 release_block_region(user_context, block_region);
202 free_block_region(user_context, block_region);
203 if (can_coalesce(block_region)) {
204 block_region = coalesce_block_regions(user_context, block_region);
219 if (block_region ==
nullptr) {
222 if (block_region->
block_ptr ==
nullptr) {
228 bool RegionAllocator::is_last_block_region(
void *user_context,
const BlockRegion *region)
const {
229 return ((region ==
nullptr) || (region == region->
next_ptr) || (region->
next_ptr ==
nullptr));
232 bool RegionAllocator::is_block_region_suitable_for_request(
void *user_context,
const BlockRegion *region,
const MemoryRequest &request)
const {
233 if (!is_available(region)) {
234 #ifdef DEBUG_RUNTIME_INTERNAL
235 debug(user_context) <<
"RegionAllocator: skipping block region ... not available! "
236 <<
" block_region=" << (
void *)region <<
"\n";
242 if (!is_compatible_block_region(region, request.properties)) {
243 #ifdef DEBUG_RUNTIME_INTERNAL
244 debug(user_context) <<
"RegionAllocator: skipping block region ... incompatible properties! "
245 <<
" block_region=" << (
void *)region <<
"\n";
254 if (actual_size > region->memory.size) {
255 #ifdef DEBUG_RUNTIME_INTERNAL
256 debug(user_context) <<
"RegionAllocator: skipping block region ... not enough space for adjusted size! "
257 <<
" block_region=" << (
void *)region <<
"\n";
264 #ifdef DEBUG_RUNTIME_INTERNAL
265 debug(user_context) <<
"RegionAllocator: found suitable block region! "
266 <<
" block_region=" << (
void *)region <<
"\n";
274 BlockRegion *RegionAllocator::find_block_region(
void *user_context,
const MemoryRequest &request) {
275 BlockRegion *block_region = block->
regions;
276 while (block_region !=
nullptr) {
277 if (is_block_region_suitable_for_request(user_context, block_region, request)) {
278 #ifdef DEBUG_RUNTIME_INTERNAL
279 debug(user_context) <<
"RegionAllocator: found suitable region ...\n"
280 <<
" user_context=" << (
void *)(user_context) <<
"\n"
281 <<
" block_resource=" << (
void *)block <<
"\n"
284 <<
" requested_size=" << (
uint32_t)request.size <<
"\n"
285 <<
" requested_is_dedicated=" << (request.dedicated ?
"true" :
"false") <<
"\n"
293 if (is_last_block_region(user_context, block_region)) {
294 block_region =
nullptr;
297 block_region = block_region->next_ptr;
300 if (block_region ==
nullptr) {
301 #ifdef DEBUG_RUNTIME_INTERNAL
302 debug(user_context) <<
"RegionAllocator: couldn't find suitable region!\n"
303 <<
" user_context=" << (
void *)(user_context) <<
"\n"
304 <<
" requested_size=" << (
uint32_t)request.size <<
"\n"
305 <<
" requested_is_dedicated=" << (request.dedicated ?
"true" :
"false") <<
"\n"
315 bool RegionAllocator::is_available(
const BlockRegion *block_region)
const {
316 if (block_region ==
nullptr) {
319 if (block_region->usage_count > 0) {
328 bool RegionAllocator::can_coalesce(
const BlockRegion *block_region)
const {
329 if (!is_available(block_region)) {
332 if (is_available(block_region->prev_ptr)) {
335 if (is_available(block_region->next_ptr)) {
341 BlockRegion *RegionAllocator::coalesce_block_regions(
void *user_context, BlockRegion *block_region) {
343 if ((block_region->usage_count == 0) && (block_region->memory.handle !=
nullptr)) {
344 #ifdef DEBUG_RUNTIME_INTERNAL
345 debug(user_context) <<
"Freeing region ("
346 <<
"block_ptr=" << (
void *)block_region->block_ptr <<
" "
347 <<
"block_region=" << (
void *)block_region <<
" "
348 <<
"memory_size=" << (
uint32_t)(block_region->memory.size) <<
" "
353 MemoryRegion *memory_region = &(block_region->memory);
355 block_region->memory.handle =
nullptr;
358 BlockRegion *prev_region = block_region->prev_ptr;
359 if (is_available(prev_region) && (prev_region != block_region)) {
361 #ifdef DEBUG_RUNTIME_INTERNAL
362 debug(user_context) <<
"RegionAllocator: Coalescing "
363 <<
"previous region (offset=" << (
int32_t)prev_region->memory.offset <<
" size=" << (
int32_t)(prev_region->memory.size) <<
" bytes) "
364 <<
"into current region (offset=" << (
int32_t)block_region->memory.offset <<
" size=" << (
int32_t)(block_region->memory.size) <<
" bytes)\n!";
367 prev_region->next_ptr = block_region->next_ptr;
368 if (block_region->next_ptr) {
369 block_region->next_ptr->prev_ptr = prev_region;
371 prev_region->memory.size += block_region->memory.size;
372 destroy_block_region(user_context, block_region);
373 block_region = prev_region;
376 BlockRegion *next_region = block_region->next_ptr;
377 if (is_available(next_region) && (next_region != block_region)) {
379 #ifdef DEBUG_RUNTIME_INTERNAL
380 debug(user_context) <<
"RegionAllocator: Coalescing "
381 <<
"next region (offset=" << (
int32_t)next_region->memory.offset <<
" size=" << (
int32_t)(next_region->memory.size) <<
" bytes) "
382 <<
"into current region (offset=" << (
int32_t)block_region->memory.offset <<
" size=" << (
int32_t)(block_region->memory.size) <<
" bytes)!\n";
385 if (next_region->next_ptr) {
386 next_region->next_ptr->prev_ptr = block_region;
388 block_region->next_ptr = next_region->next_ptr;
389 block_region->memory.size += next_region->memory.size;
390 destroy_block_region(user_context, next_region);
396 bool RegionAllocator::can_split(
const BlockRegion *block_region,
size_t size)
const {
397 return (block_region && (block_region->memory.size > size) && (block_region->usage_count == 0));
400 BlockRegion *RegionAllocator::split_block_region(
void *user_context, BlockRegion *block_region,
size_t size,
size_t alignment) {
402 if ((block_region->usage_count == 0) && (block_region->memory.handle !=
nullptr)) {
403 #ifdef DEBUG_RUNTIME_INTERNAL
404 debug(user_context) <<
"RegionAllocator: Split deallocate region ("
405 <<
"block_ptr=" << (
void *)block_region->block_ptr <<
" "
406 <<
"block_region=" << (
void *)block_region <<
" "
407 <<
"memory_size=" << (
uint32_t)(block_region->memory.size) <<
" "
408 <<
"block_reserved=" << (
uint32_t)block_region->block_ptr->reserved <<
" "
412 MemoryRegion *memory_region = &(block_region->memory);
414 block_region->memory.handle =
nullptr;
419 size_t split_offset =
aligned_offset(block_region->memory.offset + size, actual_alignment);
420 size_t empty_size = block_region->memory.size - split_size;
422 #ifdef DEBUG_RUNTIME_INTERNAL
423 debug(user_context) <<
"RegionAllocator: Conforming size and alignment \n"
424 <<
" requested_size=" << (
uint32_t)size <<
"\n"
425 <<
" split_size=" << (
uint32_t)split_size <<
"\n"
426 <<
" requested_alignment=" << (
uint32_t)alignment <<
" "
428 <<
" actual_alignment=" << (
uint32_t)actual_alignment <<
")\n";
431 #ifdef DEBUG_RUNTIME_INTERNAL
432 debug(user_context) <<
"RegionAllocator: Splitting "
433 <<
"current region (offset=" << (
int32_t)block_region->memory.offset <<
" size=" << (
int32_t)(block_region->memory.size) <<
" bytes) "
434 <<
"to create empty region (offset=" << (
int32_t)split_offset <<
" size=" << (
int32_t)(empty_size) <<
" bytes)!\n";
437 BlockRegion *next_region = block_region->next_ptr;
438 BlockRegion *empty_region = create_block_region(user_context,
439 block_region->memory.properties,
440 split_offset, empty_size,
441 block_region->memory.dedicated);
444 empty_region->next_ptr = next_region;
446 next_region->prev_ptr = empty_region;
448 empty_region->prev_ptr = block_region;
449 block_region->next_ptr = empty_region;
450 block_region->memory.size -= empty_size;
454 BlockRegion *RegionAllocator::create_block_region(
void *user_context,
const MemoryProperties &properties,
size_t offset,
size_t size,
bool dedicated) {
455 #ifdef DEBUG_RUNTIME_INTERNAL
456 debug(user_context) <<
"RegionAllocator: Creating block region ("
457 <<
"user_context=" << (
void *)(user_context) <<
" "
458 <<
"offset=" << (
uint32_t)offset <<
" "
460 <<
"alignment=" << (
uint32_t)properties.alignment <<
" "
461 <<
"dedicated=" << (dedicated ?
"true" :
"false") <<
" "
467 BlockRegion *block_region =
static_cast<BlockRegion *
>(arena->
reserve(user_context,
true));
468 if (block_region ==
nullptr) {
469 error(user_context) <<
"RegionAllocator: Failed to allocate new block region!\n";
473 #ifdef DEBUG_RUNTIME_INTERNAL
474 debug(user_context) <<
"RegionAllocator: Added block region ("
475 <<
"user_context=" << (
void *)(user_context) <<
" "
476 <<
"block_region=" << (
void *)(block_region) <<
") ...\n";
483 block_region->memory.handle =
nullptr;
484 block_region->memory.offset = actual_offset;
485 block_region->memory.size = actual_size;
486 block_region->memory.properties = properties;
487 block_region->memory.dedicated = dedicated;
489 block_region->block_ptr = block;
490 block_region->usage_count = 0;
492 #ifdef DEBUG_RUNTIME_INTERNAL
493 debug(user_context) <<
"Creating region ("
494 <<
"block_ptr=" << (
void *)block_region->block_ptr <<
" "
495 <<
"block_region=" << (
void *)block_region <<
" "
496 <<
"memory_size=" << (
uint32_t)(block_region->memory.size) <<
" "
503 int RegionAllocator::release_block_region(
void *user_context, BlockRegion *block_region) {
504 #ifdef DEBUG_RUNTIME_INTERNAL
505 debug(user_context) <<
"RegionAllocator: Releasing block region ("
506 <<
"user_context=" << (
void *)(user_context) <<
" "
507 <<
"block_region=" << (
void *)(block_region) <<
") ...\n";
509 if (block_region ==
nullptr) {
513 if (block_region->usage_count > 0) {
519 #ifdef DEBUG_RUNTIME_INTERNAL
520 debug(user_context) <<
"Releasing region ("
521 <<
"block_ptr=" << (
void *)block_region->block_ptr <<
" "
522 <<
"block_region=" << (
void *)block_region <<
" "
523 <<
"memory_size=" << (
uint32_t)(block_region->memory.size) <<
" "
524 <<
"block_reserved=" << (
uint32_t)(block->
reserved - block_region->memory.size) <<
" "
528 block->
reserved -= block_region->memory.size;
534 int RegionAllocator::destroy_block_region(
void *user_context, BlockRegion *block_region) {
535 #ifdef DEBUG_RUNTIME_INTERNAL
536 debug(user_context) <<
"RegionAllocator: Destroying block region ("
537 <<
"user_context=" << (
void *)(user_context) <<
" "
538 <<
"block_region=" << (
void *)(block_region) <<
") ...\n";
541 block_region->usage_count = 0;
542 release_block_region(user_context, block_region);
543 free_block_region(user_context, block_region);
544 arena->
reclaim(user_context, block_region);
548 int RegionAllocator::alloc_block_region(
void *user_context, BlockRegion *block_region) {
549 #ifdef DEBUG_RUNTIME_INTERNAL
550 debug(user_context) <<
"RegionAllocator: Allocating region (user_context=" << (
void *)(user_context)
551 <<
" size=" << (
int32_t)(block_region->memory.size)
552 <<
" offset=" << (
int32_t)block_region->memory.offset <<
")!\n";
557 MemoryRegion *memory_region = &(block_region->memory);
558 if (memory_region->handle ==
nullptr) {
559 error_code = allocators.
region.
allocate(user_context, memory_region);
560 memory_region->is_owner =
true;
562 #ifdef DEBUG_RUNTIME_INTERNAL
563 debug(user_context) <<
"Allocating region ("
564 <<
"block_ptr=" << (
void *)block_region->block_ptr <<
" "
565 <<
"block_region=" << (
void *)block_region <<
" "
566 <<
"memory_offset=" << (
uint32_t)(block_region->memory.offset) <<
" "
567 <<
"memory_size=" << (
uint32_t)(block_region->memory.size) <<
" "
574 #ifdef DEBUG_RUNTIME_INTERNAL
575 debug(user_context) <<
"Re-using region ("
576 <<
"block_ptr=" << (
void *)block_region->block_ptr <<
" "
577 <<
"block_region=" << (
void *)block_region <<
" "
578 <<
"memory_offset=" << (
uint32_t)(block_region->memory.offset) <<
" "
579 <<
"memory_size=" << (
uint32_t)(block_region->memory.size) <<
" "
585 block->
reserved += block_region->memory.size;
589 int RegionAllocator::free_block_region(
void *user_context, BlockRegion *block_region) {
590 #ifdef DEBUG_RUNTIME_INTERNAL
591 debug(user_context) <<
"RegionAllocator: Freeing block region ("
592 <<
"user_context=" << (
void *)(user_context) <<
" "
593 <<
"block_region=" << (
void *)(block_region) <<
" "
594 <<
"status=" << (
uint32_t)block_region->status <<
" "
595 <<
"usage_count=" << (
uint32_t)block_region->usage_count <<
") ...\n";
597 if ((block_region->usage_count == 0) && (block_region->memory.handle !=
nullptr)) {
598 #ifdef DEBUG_RUNTIME_INTERNAL
599 debug(user_context) <<
"Freeing region ("
600 <<
"block_ptr=" << (
void *)block_region->block_ptr <<
" "
601 <<
"block_region=" << (
void *)block_region <<
" "
602 <<
"memory_size=" << (
uint32_t)(block_region->memory.size) <<
" "
607 MemoryRegion *memory_region = &(block_region->memory);
609 block_region->memory.size = 0;
610 block_region->memory.offset = 0;
611 block_region->memory.handle =
nullptr;
613 block_region->usage_count = 0;
619 #ifdef DEBUG_RUNTIME_INTERNAL
620 debug(user_context) <<
"RegionAllocator: Releasing all regions ("
621 <<
"user_context=" << (
void *)(user_context) <<
") ...\n";
625 while (block_region !=
nullptr) {
626 release_block_region(user_context, block_region);
627 if (is_last_block_region(user_context, block_region)) {
630 block_region = block_region->
next_ptr;
636 #ifdef DEBUG_RUNTIME_INTERNAL
637 debug(user_context) <<
"RegionAllocator: Collecting free block regions ("
638 <<
"user_context=" << (
void *)(user_context) <<
") ...\n";
642 debug(user_context) <<
" collecting unused regions ("
643 <<
"block_ptr=" << (
void *)block <<
" "
648 bool has_collected =
false;
650 while (block_region !=
nullptr) {
651 if (can_coalesce(block_region)) {
652 #ifdef DEBUG_RUNTIME_INTERNAL
654 debug(user_context) <<
" collecting region ("
655 <<
"block_ptr=" << (
void *)block_region->
block_ptr <<
" "
656 <<
"block_region=" << (
void *)block_region <<
" "
661 block_region = coalesce_block_regions(user_context, block_region);
662 has_collected =
true;
664 if (is_last_block_region(user_context, block_region)) {
667 block_region = block_region->
next_ptr;
671 #ifdef DEBUG_RUNTIME_INTERNAL
672 debug(user_context) <<
" collected unused regions ("
673 <<
"block_ptr=" << (
void *)block <<
" "
674 <<
"region_count=" << (
uint32_t)count <<
" "
679 return has_collected;
683 #ifdef DEBUG_RUNTIME_INTERNAL
684 debug(user_context) <<
"RegionAllocator: Destroying all block regions ("
685 <<
"user_context=" << (
void *)(user_context) <<
") ...\n";
689 if (is_last_block_region(user_context, block_region)) {
690 destroy_block_region(user_context, block_region);
691 block_region =
nullptr;
694 block_region = block_region->
next_ptr;
695 destroy_block_region(user_context, prev_region);
728 size_t RegionAllocator::region_count(
void *user_context)
const {
729 if (block ==
nullptr) {
733 for (BlockRegion
const *region = block->
regions; !is_last_block_region(user_context, region); region = region->next_ptr) {
749 #endif // HALIDE_RUNTIME_REGION_ALLOCATOR_H