/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /*! * * \file arena.h * \brief Arena allocator that allocates memory chunks and frees them all during destruction time. * * NOTE: This file is portable to bare-metal embedded devices. Don't use operator new (without * placement parameters) or malloc. */ #ifndef TVM_SUPPORT_GENERIC_ARENA_H_ #define TVM_SUPPORT_GENERIC_ARENA_H_ #ifndef TVM_ARENA_HAS_DESTRUCTOR #define TVM_ARENA_HAS_DESTRUCTOR 1 #endif #include #include namespace tvm { namespace support { namespace { template // For lvalues (T is T&), T&& forward(T&& param) { // take/return lvalue refs. return static_cast(param); // For rvalues (T is T), } // take/return rvalue refs. } // namespace /*! * \brief An arena page header. */ struct ArenaPageHeader { /*! \brief points to the next page. */ ArenaPageHeader* next; /*! * \brief Total size of the page. */ size_t size; /*! \brief memory allocator offset inside page. */ size_t offset; }; /*! * \brief Arena allocator that allocates memory from continuous * chunk and frees them all only during destruction. */ template class GenericArena { public: explicit GenericArena(PageAllocator alloc = PageAllocator()) : alloc_(alloc) { // eagerly allocate the first page. head_ = tail_ = alloc_.allocate(1); head_->next = nullptr; } #if TVM_ARENA_HAS_DESTRUCTOR ~GenericArena() { this->FreeAll(); } #endif /*! \brief Free all pages. */ void FreeAll() { FreePageList(&head_); FreePageList(&free_list_); } /*! \brief Recycle all the pages in the arena */ void RecycleAll() { // put all the current list to the free list. tail_->next = free_list_; // allocate the first in the free list to head free_list_ = head_->next; head_->next = nullptr; // Reset the head. head_->offset = sizeof(ArenaPageHeader); tail_ = head_; } /*! * \brief Allocate a space from Arena for type T * \param T the data type to be allocated * \param count Numberof elements * \note The space of T is not initialized. */ template T* allocate_(int count = 1) { static_assert(PageAllocator::kPageAlign % alignof(T) == 0, "To large alignment"); return static_cast(Alloc(sizeof(T) * count, alignof(T))); } /*! * \brief Create a new instance of type T. * \param args The constructor argument. * \tparam T the type to be created. * \tparam Args Arguments to the constructor. * * \return The allocated object. * \note The type T must be simple type, or only contain * memory allocated from the same arena. * Otherwise the destructor needs to be called explicitly. */ template T* make(Args&&... args) { T* ptr = allocate_(); new (ptr) T(forward(args)...); return ptr; } private: /*! \brief internal page allocator. */ PageAllocator alloc_; /* \brief The the head of the allocated list. */ ArenaPageHeader* head_{nullptr}; /*! \brief The tail of the allocated list. */ ArenaPageHeader* tail_{nullptr}; /* \brief List of free pages. */ ArenaPageHeader* free_list_{nullptr}; /*! * \brief Align ptr by upper bound. * \param offset The offset value. * \param align The alignment requirement. */ size_t UpperAlign(size_t offset, size_t align) { return offset + (align - (offset % align)) % align; } /*! * \brief Internal aligned alloc function. * \param size The size of the memory. * \param align The alignment requirement. */ void* Alloc(size_t size, size_t align) { size_t offset = UpperAlign(head_->offset, align); if (offset + size <= head_->size) { head_->offset = offset + size; return reinterpret_cast(head_) + offset; } else { ArenaPageHeader* new_head; offset = UpperAlign(sizeof(ArenaPageHeader), align); if (free_list_ != nullptr && offset + size <= free_list_->size) { new_head = free_list_; free_list_ = free_list_->next; } else { new_head = alloc_.allocate(offset + size); } new_head->next = head_; new_head->offset = offset + size; head_ = new_head; return reinterpret_cast(head_) + offset; } } /*! * \brief Free all the pages in the list. * \param ptr The head ptr. */ void FreePageList(ArenaPageHeader** ptr) { // delete all the allocated pages. while (ptr[0] != nullptr) { ArenaPageHeader* temp = ptr[0]; ptr[0] = ptr[0]->next; alloc_.deallocate(temp); } } }; } // namespace support } // namespace tvm #endif // TVM_SUPPORT_GENERIC_ARENA_H_