Add some debug flags to auto growth allocator (#21766)
* add some debug flags to auto growth allocator, test=develop * add comments about auto growth, test=develop1.6.2
parent
c50ebeac12
commit
aa4d6a5d6c
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "paddle/fluid/memory/allocation/auto_growth_best_fit_allocator.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <vector>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
DECLARE_bool(free_idle_chunk);
|
||||||
|
DECLARE_bool(free_when_no_cache_hit);
|
||||||
|
|
||||||
|
namespace paddle {
|
||||||
|
namespace memory {
|
||||||
|
namespace allocation {
|
||||||
|
|
||||||
|
class RecordedAllocator : public Allocator {
|
||||||
|
protected:
|
||||||
|
Allocation *AllocateImpl(size_t size) override {
|
||||||
|
allocated_size_ += size;
|
||||||
|
return new Allocation(malloc(size), size, platform::CPUPlace());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeImpl(Allocation *allocation) {
|
||||||
|
allocated_size_ -= allocation->size();
|
||||||
|
free(allocation->ptr());
|
||||||
|
delete allocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
size_t AllocatedSize() const { return allocated_size_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t allocated_size_{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
static void TestFreeIdleChunk(bool free_idle_chunk,
|
||||||
|
bool free_when_no_cache_hit) {
|
||||||
|
FLAGS_free_idle_chunk = free_idle_chunk;
|
||||||
|
FLAGS_free_when_no_cache_hit = free_when_no_cache_hit;
|
||||||
|
auto recorded_allocator = std::make_shared<RecordedAllocator>();
|
||||||
|
size_t alignment = 4096;
|
||||||
|
size_t memory_size = 8192;
|
||||||
|
auto ag_allocator = std::make_shared<AutoGrowthBestFitAllocator>(
|
||||||
|
recorded_allocator, alignment);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 10; ++i) {
|
||||||
|
auto allocation = ag_allocator->Allocate(memory_size);
|
||||||
|
ASSERT_EQ(recorded_allocator->AllocatedSize(), memory_size + alignment);
|
||||||
|
allocation.reset();
|
||||||
|
if (free_idle_chunk) {
|
||||||
|
ASSERT_EQ(recorded_allocator->AllocatedSize(), 0UL);
|
||||||
|
} else {
|
||||||
|
ASSERT_EQ(recorded_allocator->AllocatedSize(), memory_size + alignment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LimitedResourceAllocator : public Allocator {
|
||||||
|
public:
|
||||||
|
explicit LimitedResourceAllocator(size_t capacity) : capacity_(capacity) {}
|
||||||
|
|
||||||
|
size_t AllocatedSize() const { return allocated_size_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Allocation *AllocateImpl(size_t size) override {
|
||||||
|
if (allocated_size_ + size > capacity_) {
|
||||||
|
throw BadAlloc("", __FILE__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
allocated_size_ += size;
|
||||||
|
return new Allocation(malloc(size), size, platform::CPUPlace());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeImpl(Allocation *allocation) {
|
||||||
|
allocated_size_ -= allocation->size();
|
||||||
|
free(allocation->ptr());
|
||||||
|
delete allocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t allocated_size_{0};
|
||||||
|
const size_t capacity_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void TestFreeWhenNoCacheHit(bool free_when_no_cache_hit) {
|
||||||
|
FLAGS_free_idle_chunk = false;
|
||||||
|
FLAGS_free_when_no_cache_hit = free_when_no_cache_hit;
|
||||||
|
size_t alignment = 256;
|
||||||
|
size_t base_memory_size = 4096;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Suppose that we have 3 memory allocation request, that is:
|
||||||
|
* - allocate x1, and then free x1
|
||||||
|
* - allocate x2, and then free x2
|
||||||
|
* - allocate x3, and then free x3
|
||||||
|
*
|
||||||
|
* where:
|
||||||
|
* - x1 + alignment < x2
|
||||||
|
* - x2 + alignment < x3
|
||||||
|
* - x1 + x2 <= memory_capacity < x1 + x2 + x3
|
||||||
|
*
|
||||||
|
* In this unittest, we obtain memory_capacity by
|
||||||
|
* ((x1 + x2) + (x1 + x2 + x3) / 2 = x1 + x2 + x3 / 2.
|
||||||
|
*
|
||||||
|
* In this case, when FLAGS_free_when_no_cache_hit is true,
|
||||||
|
* the cached memory size when each allocation request ends
|
||||||
|
* would be: x1 + alignment, x2 + alignment, x3 + alignment.
|
||||||
|
*
|
||||||
|
* When FLAGS_free_when_no_cache_hit is false, the cached
|
||||||
|
* memory size when each allocation request ends would be:
|
||||||
|
* x1 + alignment, x1 + x2 + 2 * alignment, x3 + alignment.
|
||||||
|
*/
|
||||||
|
std::vector<size_t> allocate_size = {base_memory_size,
|
||||||
|
base_memory_size + alignment * 2,
|
||||||
|
base_memory_size + alignment * 4};
|
||||||
|
size_t memory_capacity =
|
||||||
|
allocate_size[0] + allocate_size[1] + allocate_size[2] / 2;
|
||||||
|
|
||||||
|
auto underlying_allocator =
|
||||||
|
std::make_shared<LimitedResourceAllocator>(memory_capacity);
|
||||||
|
auto ag_allocator = std::make_shared<AutoGrowthBestFitAllocator>(
|
||||||
|
underlying_allocator, alignment);
|
||||||
|
|
||||||
|
ag_allocator->Allocate(allocate_size[0]);
|
||||||
|
ASSERT_EQ(underlying_allocator->AllocatedSize(),
|
||||||
|
allocate_size[0] + alignment);
|
||||||
|
|
||||||
|
ag_allocator->Allocate(allocate_size[1]);
|
||||||
|
if (free_when_no_cache_hit) {
|
||||||
|
ASSERT_EQ(underlying_allocator->AllocatedSize(),
|
||||||
|
allocate_size[1] + alignment);
|
||||||
|
} else {
|
||||||
|
ASSERT_EQ(underlying_allocator->AllocatedSize(),
|
||||||
|
allocate_size[0] + allocate_size[1] + 2 * alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
ag_allocator->Allocate(allocate_size[2]);
|
||||||
|
ASSERT_EQ(underlying_allocator->AllocatedSize(),
|
||||||
|
allocate_size[2] + alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(test_auto_growth_allocator, test_free_idle_chunk) {
|
||||||
|
for (auto free_idle_chunk : {false, true}) {
|
||||||
|
for (auto free_when_no_cache_hit : {false, true}) {
|
||||||
|
TestFreeIdleChunk(free_idle_chunk, free_when_no_cache_hit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(test_auto_growth_allocator, test_free_when_no_cache_hit) {
|
||||||
|
TestFreeWhenNoCacheHit(false);
|
||||||
|
TestFreeWhenNoCacheHit(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace allocation
|
||||||
|
} // namespace memory
|
||||||
|
} // namespace paddle
|
Loading…
Reference in new issue