2019-04-17 10:49:11 +08:00
|
|
|
//
|
|
|
|
// BufferAllocator.cpp
|
|
|
|
// MNN
|
|
|
|
//
|
|
|
|
// Created by MNN on 2018/12/30.
|
|
|
|
// Copyright © 2018, Alibaba Group Holding Limited
|
|
|
|
//
|
|
|
|
|
2019-12-27 22:16:57 +08:00
|
|
|
#include "core/BufferAllocator.hpp"
|
|
|
|
#include "core/Macro.h"
|
2019-04-17 10:49:11 +08:00
|
|
|
|
|
|
|
//#define DUMP_USAGE
|
2020-11-05 16:41:56 +08:00
|
|
|
//#define MNN_DEBUG_MEMORY
|
2019-04-17 10:49:11 +08:00
|
|
|
namespace MNN {
|
2020-12-15 14:12:35 +08:00
|
|
|
class DefaultAllocator : public BufferAllocator::Allocator {
|
|
|
|
public:
|
|
|
|
DefaultAllocator() {
|
|
|
|
// Do nothing
|
|
|
|
}
|
|
|
|
virtual ~ DefaultAllocator() {
|
|
|
|
// Do nothing
|
|
|
|
}
|
2022-02-18 11:30:27 +08:00
|
|
|
virtual std::pair<void*, size_t> onAlloc(size_t size, size_t align) {
|
2020-12-15 14:12:35 +08:00
|
|
|
return std::make_pair(MNNMemoryAllocAlign(size, MNN_MEMORY_ALIGN_DEFAULT), 0);
|
|
|
|
}
|
2022-02-18 11:30:27 +08:00
|
|
|
virtual void onRelease(std::pair<void*, size_t> ptr) {
|
2020-12-15 14:12:35 +08:00
|
|
|
MNN_ASSERT(ptr.second == 0);
|
|
|
|
MNNMemoryFreeAlign(ptr.first);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
class RecurseAllocator : public BufferAllocator::Allocator {
|
|
|
|
public:
|
|
|
|
RecurseAllocator(BufferAllocator* parent) {
|
|
|
|
mParent = parent;
|
|
|
|
}
|
|
|
|
virtual ~ RecurseAllocator() {
|
|
|
|
// Do nothing
|
|
|
|
}
|
2022-02-18 11:30:27 +08:00
|
|
|
virtual std::pair<void*, size_t> onAlloc(size_t size, size_t align) override {
|
2021-11-30 10:10:53 +08:00
|
|
|
return mParent->alloc(size, false, align);
|
2020-12-15 14:12:35 +08:00
|
|
|
}
|
2022-02-18 11:30:27 +08:00
|
|
|
virtual void onRelease(std::pair<void*, size_t> ptr) override {
|
2020-12-15 14:12:35 +08:00
|
|
|
mParent->free(ptr);
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
BufferAllocator* mParent;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::shared_ptr<BufferAllocator::Allocator> BufferAllocator::Allocator::createDefault() {
|
|
|
|
std::shared_ptr<BufferAllocator::Allocator> _res;
|
|
|
|
_res.reset(new DefaultAllocator);
|
|
|
|
return _res;
|
|
|
|
}
|
|
|
|
std::shared_ptr<BufferAllocator::Allocator> BufferAllocator::Allocator::createRecurse(BufferAllocator* parent) {
|
|
|
|
std::shared_ptr<BufferAllocator::Allocator> _res;
|
|
|
|
_res.reset(new RecurseAllocator(parent));
|
|
|
|
return _res;
|
|
|
|
}
|
|
|
|
|
2019-04-17 10:49:11 +08:00
|
|
|
BufferAllocator::Node::~Node() {
|
2021-04-08 15:34:23 +08:00
|
|
|
if (nullptr == parent.get()) {
|
2020-12-15 14:12:35 +08:00
|
|
|
outside->onRelease(pointer);
|
2019-04-17 10:49:11 +08:00
|
|
|
}
|
|
|
|
}
|
2022-05-27 23:46:44 +08:00
|
|
|
std::pair<void*, size_t> BufferAllocator::alloc(size_t size, bool separate, size_t align) {
|
2019-04-17 10:49:11 +08:00
|
|
|
#ifdef DUMP_USAGE
|
|
|
|
auto memoryUsed = size / 1024.0f / 1024.0f;
|
|
|
|
MNN_PRINT("Alloc: %f\n", memoryUsed);
|
|
|
|
#endif
|
2021-11-30 10:10:53 +08:00
|
|
|
if (0 == align) {
|
|
|
|
align = mAlign;
|
|
|
|
}
|
2022-02-18 11:30:27 +08:00
|
|
|
std::pair<void*, size_t> pointer;
|
2019-04-17 10:49:11 +08:00
|
|
|
// reuse if possible
|
2022-05-27 23:46:44 +08:00
|
|
|
if (!separate) {
|
2020-10-25 11:01:04 +08:00
|
|
|
if (nullptr != mCurrentFreeList) {
|
2021-11-30 10:10:53 +08:00
|
|
|
pointer = getFromFreeList(mCurrentFreeList, size, false, align);
|
2019-04-17 10:49:11 +08:00
|
|
|
}
|
2020-12-15 14:12:35 +08:00
|
|
|
if (nullptr != pointer.first) {
|
2019-04-17 10:49:11 +08:00
|
|
|
return pointer;
|
|
|
|
}
|
2021-11-30 10:10:53 +08:00
|
|
|
pointer = getFromFreeList(&mFreeList, size, true, align);
|
2020-12-15 14:12:35 +08:00
|
|
|
if (nullptr != pointer.first) {
|
2019-04-17 10:49:11 +08:00
|
|
|
return pointer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// alloc otherwise
|
2021-11-30 10:10:53 +08:00
|
|
|
pointer = mAllocator->onAlloc(size, align);
|
2020-12-15 14:12:35 +08:00
|
|
|
if (nullptr == pointer.first) {
|
|
|
|
return pointer;
|
2019-04-17 10:49:11 +08:00
|
|
|
}
|
|
|
|
mTotalSize += size;
|
|
|
|
|
|
|
|
// save node
|
2021-04-08 15:34:23 +08:00
|
|
|
SharedPtr<Node> node(new Node);
|
2019-04-17 10:49:11 +08:00
|
|
|
node->size = size;
|
|
|
|
node->pointer = pointer;
|
|
|
|
mUsedList[pointer] = node;
|
2020-12-15 14:12:35 +08:00
|
|
|
node->outside = mAllocator.get();
|
2021-11-30 10:10:53 +08:00
|
|
|
MNN_ASSERT(pointer.second % align == 0);
|
2019-04-17 10:49:11 +08:00
|
|
|
|
|
|
|
#ifdef DUMP_USAGE
|
|
|
|
MNN_PRINT("mTotalSize: %f\n", mTotalSize / 1024.0f / 1024.0f);
|
|
|
|
#endif
|
|
|
|
return pointer;
|
|
|
|
}
|
|
|
|
|
2021-04-08 15:34:23 +08:00
|
|
|
void BufferAllocator::returnMemory(FREELIST* listP, SharedPtr<Node> node, bool permitMerge) {
|
2019-04-17 10:49:11 +08:00
|
|
|
auto& list = *listP;
|
|
|
|
list.insert(std::make_pair(node->size, node));
|
|
|
|
// update parent use count
|
2021-04-08 15:34:23 +08:00
|
|
|
if (nullptr != node->parent.get() && permitMerge) {
|
2019-04-17 10:49:11 +08:00
|
|
|
auto parent = node->parent;
|
|
|
|
parent->useCount -= 1;
|
|
|
|
|
|
|
|
// merge if all subnodes were freed
|
|
|
|
auto needMerge = parent->useCount == 0;
|
|
|
|
while (needMerge) {
|
|
|
|
// collect all subnodes
|
|
|
|
for (auto iter = list.begin(); iter != list.end();) {
|
2021-04-08 15:34:23 +08:00
|
|
|
if (iter->second->parent.get() == parent.get()) {
|
2019-04-17 10:49:11 +08:00
|
|
|
iter = list.erase(iter);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
iter++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// do merge downside up
|
|
|
|
list.insert(std::make_pair(parent->size, parent));
|
|
|
|
needMerge = false;
|
2021-04-08 15:34:23 +08:00
|
|
|
if (parent->parent.get() != nullptr) {
|
2019-04-17 10:49:11 +08:00
|
|
|
parent = parent->parent;
|
|
|
|
parent->useCount -= 1;
|
|
|
|
needMerge = parent->useCount == 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-18 11:30:27 +08:00
|
|
|
bool BufferAllocator::free(std::pair<void*, size_t> pointer) {
|
2019-04-17 10:49:11 +08:00
|
|
|
// get node
|
|
|
|
auto x = mUsedList.find(pointer);
|
|
|
|
if (x == mUsedList.end()) {
|
|
|
|
MNN_ASSERT(false);
|
2021-11-30 10:10:53 +08:00
|
|
|
|
2019-04-17 10:49:11 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// mark as reusable
|
|
|
|
auto node = x->second;
|
|
|
|
mUsedList.erase(x);
|
2020-10-25 11:01:04 +08:00
|
|
|
if (nullptr != mCurrentFreeList) {
|
|
|
|
returnMemory(mCurrentFreeList, node, false);
|
2019-04-17 10:49:11 +08:00
|
|
|
} else {
|
|
|
|
returnMemory(&mFreeList, node);
|
|
|
|
}
|
|
|
|
#ifdef DUMP_USAGE
|
|
|
|
auto memoryUsed = x->second->size / 1024.0f / 1024.0f;
|
|
|
|
MNN_PRINT("Free: %f\n", memoryUsed);
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-01-15 13:33:47 +08:00
|
|
|
void BufferAllocator::release(bool allRelease) {
|
2020-11-09 21:51:44 +08:00
|
|
|
MNN_ASSERT(mGroups.empty());
|
2020-01-15 13:33:47 +08:00
|
|
|
if (allRelease) {
|
|
|
|
mUsedList.clear();
|
|
|
|
mFreeList.clear();
|
|
|
|
mTotalSize = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (auto f : mFreeList) {
|
2021-04-08 15:34:23 +08:00
|
|
|
if (f.second->parent.get() == nullptr) {
|
2020-11-05 16:41:56 +08:00
|
|
|
MNN_ASSERT(mTotalSize >= f.first);
|
|
|
|
mTotalSize -= f.first;
|
|
|
|
}
|
2020-01-15 13:33:47 +08:00
|
|
|
}
|
2019-04-17 10:49:11 +08:00
|
|
|
mFreeList.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void BufferAllocator::barrierBegin() {
|
|
|
|
MNN_ASSERT(mGroups.empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
void BufferAllocator::barrierEnd() {
|
|
|
|
for (auto& freeGroup : mGroups) {
|
|
|
|
auto freeList = *freeGroup;
|
|
|
|
for (auto& iter : freeList) {
|
|
|
|
returnMemory(&mFreeList, iter.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mGroups.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void BufferAllocator::beginGroup() {
|
|
|
|
std::shared_ptr<FREELIST> newFreeList(new FREELIST);
|
2020-10-25 11:01:04 +08:00
|
|
|
mCurrentFreeList = newFreeList.get();
|
2019-04-17 10:49:11 +08:00
|
|
|
mGroups.emplace_back(newFreeList);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BufferAllocator::endGroup() {
|
2020-10-25 11:01:04 +08:00
|
|
|
mCurrentFreeList = nullptr;
|
2019-04-17 10:49:11 +08:00
|
|
|
}
|
|
|
|
|
2022-02-18 11:30:27 +08:00
|
|
|
std::pair<void*, size_t> BufferAllocator::getFromFreeList(FREELIST* list, size_t size, bool permiteSplit, size_t align) {
|
2019-04-17 10:49:11 +08:00
|
|
|
#ifdef MNN_DEBUG_MEMORY
|
2020-12-15 14:12:35 +08:00
|
|
|
return std::make_pair(nullptr, 0);
|
2019-04-17 10:49:11 +08:00
|
|
|
#endif
|
2022-02-18 11:30:27 +08:00
|
|
|
size_t realSize = size;
|
2021-11-30 10:10:53 +08:00
|
|
|
bool needExtraSize = mAlign % align != 0;
|
|
|
|
if (needExtraSize) {
|
|
|
|
realSize = size + align - 1;
|
|
|
|
}
|
2019-04-17 10:49:11 +08:00
|
|
|
// get node larger than size
|
2021-11-30 10:10:53 +08:00
|
|
|
auto x = list->lower_bound(realSize);
|
2019-04-17 10:49:11 +08:00
|
|
|
if (x == list->end()) {
|
2020-12-15 14:12:35 +08:00
|
|
|
return std::make_pair(nullptr, 0);
|
2019-04-17 10:49:11 +08:00
|
|
|
}
|
|
|
|
// update parent use count
|
2020-12-15 14:12:35 +08:00
|
|
|
auto pointer = x->second->pointer;
|
2021-11-30 10:10:53 +08:00
|
|
|
// Align offset
|
|
|
|
if (needExtraSize) {
|
2022-02-18 11:30:27 +08:00
|
|
|
size_t originOffset = pointer.second;
|
2021-11-30 10:10:53 +08:00
|
|
|
pointer.second = UP_DIV(originOffset, align) * align;
|
|
|
|
realSize = size + pointer.second - originOffset;
|
|
|
|
}
|
2021-04-08 15:34:23 +08:00
|
|
|
if (permiteSplit && nullptr != x->second->parent.get()) {
|
2019-04-17 10:49:11 +08:00
|
|
|
x->second->parent->useCount += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// uses up all aligned space
|
2021-11-30 10:10:53 +08:00
|
|
|
auto sizeAlign = UP_DIV(realSize, mAlign) * mAlign;
|
2019-04-17 10:49:11 +08:00
|
|
|
if (sizeAlign >= x->first || (!permiteSplit)) {
|
|
|
|
mUsedList.insert(std::make_pair(pointer, x->second));
|
|
|
|
list->erase(x);
|
2021-11-30 10:10:53 +08:00
|
|
|
MNN_ASSERT(pointer.second % align == 0);
|
2019-04-17 10:49:11 +08:00
|
|
|
return pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
// split otherwise
|
2021-04-08 15:34:23 +08:00
|
|
|
SharedPtr<Node> first(new Node);
|
2019-04-17 10:49:11 +08:00
|
|
|
first->parent = x->second;
|
|
|
|
first->size = sizeAlign;
|
|
|
|
first->pointer = x->second->pointer;
|
|
|
|
mUsedList.insert(std::make_pair(pointer, first));
|
|
|
|
x->second->useCount += 1;
|
|
|
|
|
2021-04-08 15:34:23 +08:00
|
|
|
SharedPtr<Node> second(new Node);
|
2019-04-17 10:49:11 +08:00
|
|
|
second->parent = x->second;
|
|
|
|
second->size = x->second->size - sizeAlign;
|
2020-12-15 14:12:35 +08:00
|
|
|
second->pointer.first = x->second->pointer.first;
|
|
|
|
second->pointer.second = x->second->pointer.second + sizeAlign;
|
2019-04-17 10:49:11 +08:00
|
|
|
list->erase(x);
|
2020-11-22 10:21:50 +08:00
|
|
|
list->insert(std::make_pair(second->size, second));
|
2021-11-30 10:10:53 +08:00
|
|
|
MNN_ASSERT(pointer.second % align == 0);
|
2019-04-17 10:49:11 +08:00
|
|
|
return pointer;
|
|
|
|
}
|
|
|
|
} // namespace MNN
|