提交 4e1617d0 编写于 作者: L liaogang

ENH: add buddy alloctor Free

上级 bbd3eab7
...@@ -7,3 +7,6 @@ else(${WITH_GPU}) ...@@ -7,3 +7,6 @@ else(${WITH_GPU})
cc_library(system_allocator SRCS system_allocator.cc DEPS gflags) cc_library(system_allocator SRCS system_allocator.cc DEPS gflags)
cc_test(system_allocator_test SRCS system_allocator_test.cc DEPS system_allocator gflags) cc_test(system_allocator_test SRCS system_allocator_test.cc DEPS system_allocator gflags)
endif(${WITH_GPU}) endif(${WITH_GPU})
cc_library(metadata SRCS metadata.cc)
cc_library(buddy_allocator SRCS buddy_allocator.cc)
...@@ -58,17 +58,16 @@ void* BuddyAllocator::Alloc(size_t unaligned_size) { ...@@ -58,17 +58,16 @@ void* BuddyAllocator::Alloc(size_t unaligned_size) {
// refill the pool if failure // refill the pool if failure
if (it == pool_.end()) { if (it == pool_.end()) {
it = RefillPool(); it = RefillPool();
// if still failure, fail fatally
if (it == pool_.end()) {
return nullptr;
}
} else { } else {
DLOG(INFO) << " Allocation from existing memory block " << std::get<2>(*it) DLOG(INFO) << " Allocation from existing memory block " << std::get<2>(*it)
<< " at address " << " at address "
<< reinterpret_cast<MemoryBlock*>(std::get<2>(*it))->data(); << reinterpret_cast<MemoryBlock*>(std::get<2>(*it))->data();
} }
// if still failure, fail fatally
if (it == pool_.end()) {
return nullptr;
}
total_used_ += size; total_used_ += size;
total_free_ -= size; total_free_ -= size;
...@@ -76,6 +75,13 @@ void* BuddyAllocator::Alloc(size_t unaligned_size) { ...@@ -76,6 +75,13 @@ void* BuddyAllocator::Alloc(size_t unaligned_size) {
return reinterpret_cast<MemoryBlock*>(SplitToAlloc(it, size))->data(); return reinterpret_cast<MemoryBlock*>(SplitToAlloc(it, size))->data();
} }
void BuddyAllocator::Free(void* p) {
auto block = static_cast<MemoryBlock*>(p)->metadata();
// acquire the allocator lock
std::lock_guard<std::mutex> lock(mutex_);
}
void* BuddyAllocator::SystemAlloc(size_t size) { void* BuddyAllocator::SystemAlloc(size_t size) {
size_t index = 0; size_t index = 0;
void* p = system_allocator_->Alloc(index, size); void* p = system_allocator_->Alloc(index, size);
...@@ -140,17 +146,14 @@ BuddyAllocator::PoolSet::iterator BuddyAllocator::FindExistChunk(size_t size) { ...@@ -140,17 +146,14 @@ BuddyAllocator::PoolSet::iterator BuddyAllocator::FindExistChunk(size_t size) {
void* BuddyAllocator::SplitToAlloc(BuddyAllocator::PoolSet::iterator it, void* BuddyAllocator::SplitToAlloc(BuddyAllocator::PoolSet::iterator it,
size_t size) { size_t size) {
auto block = static_cast<MemoryBlock*>(std::get<2>(*it)); auto block = static_cast<MemoryBlock*>(std::get<2>(*it));
pool_.erase(it); pool_.erase(it);
DLOG(INFO) << " Split block (" << block << ", " << block->total_size(cache_) DLOG(INFO) << " Split block (" << block << ", " << block->total_size(cache_)
<< ") into"; << ") into";
block->split(cache_, size); block->split(cache_, size);
DLOG(INFO) << " Left block (" << block << ", " << block->total_size(cache_) DLOG(INFO) << " Left block (" << block << ", " << block->total_size(cache_)
<< ")"; << ")";
block->set_type(cache_, MemoryBlock::ARENA_CHUNK); block->set_type(cache_, MemoryBlock::ARENA_CHUNK);
// the rest of memory if exist // the rest of memory if exist
......
...@@ -14,16 +14,16 @@ ...@@ -14,16 +14,16 @@
#pragma once #pragma once
#include "paddle/memory/detail/system_allocator.h"
#include "paddle/memory/detail/metadata.h" #include "paddle/memory/detail/metadata.h"
#include "paddle/memory/detail/system_allocator.h"
#include "paddle/platform/assert.h" #include "paddle/platform/assert.h"
#include "paddle/platform/cpu_info.h" #include "paddle/platform/cpu_info.h"
#include "paddle/platform/gpu_info.h" #include "paddle/platform/gpu_info.h"
#include <set>
#include <mutex> #include <mutex>
#include <vector> #include <set>
#include <unordered_map> #include <unordered_map>
#include <vector>
namespace paddle { namespace paddle {
namespace memory { namespace memory {
...@@ -57,9 +57,9 @@ class BuddyAllocator { ...@@ -57,9 +57,9 @@ class BuddyAllocator {
/*! \brief If existing chunks are not suitable, refill pool */ /*! \brief If existing chunks are not suitable, refill pool */
PoolSet::iterator RefillPool(); PoolSet::iterator RefillPool();
/** /**
* \brief Find the suitable chunk from existing pool * \brief Find the suitable chunk from existing pool
* *
* \param it pool iterator which contains suitable block. * \param it pool iterator which contains suitable block.
* \param size the size of allocation. * \param size the size of allocation.
*/ */
......
#include "paddle/memory/detail/memory_block.h"
#include "paddle/platform/assert.h"
namespace paddle {
namespace memory {
namespace detail {
void MemoryBlock::init(MetadataCache& cache, Type t, size_t index, size_t size,
void* left_buddy, void* right_buddy) {
cache.store(this,
MemoryBlockMetadata(t, index, size - overhead(), size,
static_cast<MemoryBlock*>(left_buddy),
static_cast<MemoryBlock*>(right_buddy)));
}
MemoryBlock::Type MemoryBlock::type(MetadataCache& cache) const {
return cache.load(this).type;
}
size_t MemoryBlock::size(MetadataCache& cache) const {
return cache.load(this).size;
}
size_t MemoryBlock::total_size(MetadataCache& cache) const {
return cache.load(this).total_size;
}
MemoryBlock* MemoryBlock::left_buddy(MetadataCache& cache) const {
return cache.load(this).left_buddy;
}
MemoryBlock* MemoryBlock::right_buddy(MetadataCache& cache) const {
return cache.load(this).right_buddy;
}
void MemoryBlock::split(MetadataCache& cache, size_t size) {
// make sure the split fits
assert(total_size(cache) >= size);
// bail out if there is no room for another partition
if (total_size(cache) - size <= overhead()) {
return;
}
// find the position of the split
void* right_partition = reinterpret_cast<uint8_t*>(this) + size;
size_t remaining_size = total_size(cache) - size;
// Add the new block as a buddy
auto metadata = cache.load(this);
// Write the metadata for the new block
auto new_block_right_buddy = metadata.right_buddy;
cache.store(static_cast<MemoryBlock*>(right_partition),
MemoryBlockMetadata(FREE_MEMORY, index(cache),
remaining_size - overhead(), remaining_size,
this, new_block_right_buddy));
metadata.right_buddy = static_cast<MemoryBlock*>(right_partition);
metadata.size = size - overhead();
metadata.total_size = size;
cache.store(this, metadata);
// Write metadata for the new block's right buddy
if (new_block_right_buddy != nullptr) {
auto buddy_metadata = cache.load(new_block_right_buddy);
buddy_metadata.left_buddy = static_cast<MemoryBlock*>(right_partition);
cache.store(new_block_right_buddy, buddy_metadata);
}
}
void MemoryBlock::merge(MetadataCache& cache, MemoryBlock* right_buddy) {
// only free blocks can be merged
assert(type(cache) == FREE_MEMORY);
assert(right_buddy->type(cache) == FREE_MEMORY);
auto metadata = cache.load(this);
// link this->buddy's buddy
metadata.right_buddy = right_buddy->right_buddy(cache);
// link buddy's buddy -> this
if (metadata.right_buddy != nullptr) {
auto buddy_metadata = cache.load(metadata.right_buddy);
buddy_metadata.left_buddy = this;
cache.store(metadata.right_buddy, buddy_metadata);
}
metadata.size += right_buddy->total_size(cache);
metadata.total_size += right_buddy->total_size(cache);
cache.store(this, metadata);
cache.store(right_buddy,
MemoryBlockMetadata(INVALID_MEMORY, 0, 0, 0, nullptr, nullptr));
}
void MemoryBlock::mark_as_free(MetadataCache& cache) {
// check for double free or corruption
assert(type(cache) != FREE_MEMORY);
assert(type(cache) != INVALID_MEMORY);
set_type(cache, FREE_MEMORY);
}
void MemoryBlock::set_type(MetadataCache& cache, Type t) {
auto metadata = cache.load(this);
metadata.type = t;
cache.store(this, metadata);
}
bool MemoryBlock::has_left_buddy(MetadataCache& cache) const {
return left_buddy(cache) != nullptr;
}
bool MemoryBlock::has_right_buddy(MetadataCache& cache) const {
return right_buddy(cache) != nullptr;
}
size_t MemoryBlock::index(MetadataCache& cache) const {
return cache.load(this).index;
}
void* MemoryBlock::data() const {
return const_cast<MemoryBlockMetadata*>(
reinterpret_cast<const MemoryBlockMetadata*>(this)) +
1;
}
MemoryBlock* MemoryBlock::metadata() const {
return const_cast<MemoryBlock*>(reinterpret_cast<const MemoryBlock*>(
reinterpret_cast<const MemoryBlockMetadata*>(this) - 1));
}
} // detail
} // memory
} // paddle
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.
Licensed 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. */
#pragma once
#include "paddle/memory/detail/metadata.h"
#include <cstddef>
#include <unordered_map>
namespace paddle {
namespace memory {
namespace detail {
// Forward Declaration
class Metadata;
/*! \brief A class used to interpret the contents of a memory block */
class MemoryBlock {
public:
// Unify the metadata format between GPU and CPU allocations
using MetadataCache = std::unordered_map<const MemoryBlock*, Metadata>;
enum Type {
FREE_CHUNK, // memory is free and idle
ARENA_CHUNK, // memory is being occupied
HUGE_CHUNK, // memory is out of management
INVALID_CHUNK // memory is invalid
};
public:
void init(MetadataCache& cache, Type t, size_t index, size_t size,
void* left_buddy, void* right_buddy);
public:
/*! \brief The type of the allocation */
Type type(MetadataCache& cache) const;
/*! \brief The size of the data region */
size_t size(MetadataCache& cache) const;
/*! \brief An index to track the allocator */
size_t index(MetadataCache& cache) const;
/*! \brief The total size of the block */
size_t total_size(MetadataCache& cache) const;
/*! \brief Check the left buddy of the block */
bool has_left_buddy(MetadataCache& cache) const;
/*! \brief Check the right buddy of the block */
bool has_right_buddy(MetadataCache& cache) const;
/*! \brief Get the left buddy */
MemoryBlock* left_buddy(MetadataCache& cache) const;
/*! \brief Get the right buddy */
MemoryBlock* right_buddy(MetadataCache& cache) const;
public:
/*! \brief Split the allocation into left/right blocks */
void split(MetadataCache& cache, size_t size);
/*! \brief Merge left and right blocks together */
void merge(MetadataCache& cache, MemoryBlock* right_buddy);
/*! \brief Mark the allocation as free */
void mark_as_free(MetadataCache& cache);
/*! \brief Change the type of the allocation */
void set_type(MetadataCache& cache, Type t);
public:
/*! \brief Get a pointer to the memory block's data */
void* data() const;
/*! \brief Get a pointer to the memory block's metadata */
MemoryBlock* metadata() const;
public:
static size_t overhead();
};
} // namespace detail
} // namespace memory
} // namespace paddle
...@@ -48,6 +48,7 @@ class GPUAllocator : public SystemAllocator { ...@@ -48,6 +48,7 @@ class GPUAllocator : public SystemAllocator {
virtual void* Alloc(size_t& index, size_t size); virtual void* Alloc(size_t& index, size_t size);
virtual void Free(void* p, size_t size, size_t index); virtual void Free(void* p, size_t size, size_t index);
virtual bool UseGpu(); virtual bool UseGpu();
private: private:
size_t gpu_alloc_size_ = 0; size_t gpu_alloc_size_ = 0;
size_t fallback_alloc_size_ = 0; size_t fallback_alloc_size_ = 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册