commit
48cf64e833
@ -1 +1,11 @@
|
||||
add_subdirectory(detail)
|
||||
|
||||
cc_library(memory SRCS memory.cc)
|
||||
|
||||
cc_library(paddle_memory
|
||||
DEPS
|
||||
memory meta_data
|
||||
meta_cache memory_block
|
||||
buddy_allocator system_allocator)
|
||||
|
||||
cc_test(memory_test SRCS memory_test.cc DEPS place paddle_memory)
|
||||
|
@ -1,7 +1,15 @@
|
||||
if(${WITH_GPU})
|
||||
nv_library(system_allocator SRCS system_allocator.cc DEPS gflags)
|
||||
nv_test(system_allocator_test SRCS system_allocator_test.cc DEPS system_allocator gflags)
|
||||
nv_library(system_allocator SRCS system_allocator.cc DEPS gflags cpu_info gpu_info)
|
||||
else(${WITH_GPU})
|
||||
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_library(system_allocator SRCS system_allocator.cc DEPS gflags cpu_info)
|
||||
endif(${WITH_GPU})
|
||||
|
||||
cc_test(system_allocator_test SRCS system_allocator_test.cc DEPS system_allocator)
|
||||
|
||||
cc_library(meta_data SRCS meta_data.cc)
|
||||
|
||||
cc_library(meta_cache SRCS meta_cache.cc)
|
||||
|
||||
cc_library(memory_block SRCS memory_block.cc)
|
||||
|
||||
cc_library(buddy_allocator SRCS buddy_allocator.cc DEPS glog)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,157 @@
|
||||
/* 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. */
|
||||
|
||||
#include "paddle/memory/detail/memory_block.h"
|
||||
#include "paddle/memory/detail/meta_cache.h"
|
||||
#include "paddle/memory/detail/meta_data.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, Metadata(t, index, size - sizeof(Metadata), 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
|
||||
PADDLE_ASSERT(total_size(cache) >= size);
|
||||
|
||||
// bail out if there is no room for another partition
|
||||
if (total_size(cache) - size <= sizeof(Metadata)) {
|
||||
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),
|
||||
Metadata(FREE_CHUNK, index(cache), remaining_size - sizeof(Metadata),
|
||||
remaining_size, this, new_block_right_buddy));
|
||||
|
||||
metadata.right_buddy = static_cast<MemoryBlock*>(right_partition);
|
||||
metadata.size = size - sizeof(Metadata);
|
||||
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
|
||||
PADDLE_ASSERT(type(cache) == FREE_CHUNK);
|
||||
PADDLE_ASSERT(right_buddy->type(cache) == FREE_CHUNK);
|
||||
|
||||
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, Metadata(INVALID_CHUNK, 0, 0, 0, nullptr, nullptr));
|
||||
}
|
||||
|
||||
void MemoryBlock::mark_as_free(MetadataCache& cache) {
|
||||
// check for double free or corruption
|
||||
PADDLE_ASSERT(type(cache) != FREE_CHUNK);
|
||||
PADDLE_ASSERT(type(cache) != INVALID_CHUNK);
|
||||
|
||||
set_type(cache, FREE_CHUNK);
|
||||
}
|
||||
|
||||
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<Metadata*>(reinterpret_cast<const Metadata*>(this)) + 1;
|
||||
}
|
||||
|
||||
MemoryBlock* MemoryBlock::metadata() const {
|
||||
return const_cast<MemoryBlock*>(reinterpret_cast<const MemoryBlock*>(
|
||||
reinterpret_cast<const Metadata*>(this) - 1));
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // memory
|
||||
} // paddle
|
@ -0,0 +1,91 @@
|
||||
/* 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 <cstddef>
|
||||
|
||||
namespace paddle {
|
||||
namespace memory {
|
||||
namespace detail {
|
||||
|
||||
// Forward Declarations
|
||||
class MetadataCache;
|
||||
|
||||
/*! \brief A class used to interpret the contents of a memory block */
|
||||
class MemoryBlock {
|
||||
public:
|
||||
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
|
@ -0,0 +1,57 @@
|
||||
/* 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. */
|
||||
|
||||
#include "paddle/memory/detail/meta_cache.h"
|
||||
#include "paddle/memory/detail/memory_block.h"
|
||||
#include "paddle/platform/assert.h"
|
||||
|
||||
namespace paddle {
|
||||
namespace memory {
|
||||
namespace detail {
|
||||
|
||||
MetadataCache::MetadataCache(bool uses_gpu) : uses_gpu_(uses_gpu) {}
|
||||
|
||||
Metadata MetadataCache::load(const MemoryBlock* block) {
|
||||
if (uses_gpu_) {
|
||||
auto existing_metadata = cache_.find(block);
|
||||
PADDLE_ASSERT(existing_metadata->second.check_guards());
|
||||
return existing_metadata->second;
|
||||
} else {
|
||||
PADDLE_ASSERT(reinterpret_cast<const Metadata*>(block)->check_guards());
|
||||
return *reinterpret_cast<const Metadata*>(block);
|
||||
}
|
||||
}
|
||||
|
||||
void MetadataCache::store(MemoryBlock* block,
|
||||
const Metadata& original_metadata) {
|
||||
auto metadata = original_metadata;
|
||||
|
||||
metadata.update_guards();
|
||||
|
||||
if (uses_gpu_) {
|
||||
cache_[block] = metadata;
|
||||
} else {
|
||||
*reinterpret_cast<Metadata*>(block) = metadata;
|
||||
}
|
||||
}
|
||||
|
||||
void MetadataCache::invalidate(MemoryBlock* block) {
|
||||
if (uses_gpu_) {
|
||||
cache_.erase(block);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace memory
|
||||
} // namespace paddle
|
@ -0,0 +1,64 @@
|
||||
/* 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/memory_block.h"
|
||||
#include "paddle/memory/detail/meta_data.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace paddle {
|
||||
namespace memory {
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* \brief A cache for accessing memory block meta-data that may be expensive
|
||||
* to access directly.
|
||||
*
|
||||
* \note This class exists to unify the metadata format between GPU and CPU
|
||||
* allocations. It should be removed when the CPU can access all GPU
|
||||
* allocations directly via UVM.
|
||||
*/
|
||||
class MetadataCache {
|
||||
public:
|
||||
MetadataCache(bool uses_gpu);
|
||||
|
||||
public:
|
||||
/*! \brief Load the associated metadata for the specified memory block. */
|
||||
Metadata load(const MemoryBlock*);
|
||||
|
||||
/*! \brief Store the associated metadata for the specified memory block. */
|
||||
void store(MemoryBlock*, const Metadata&);
|
||||
|
||||
/*! \brief Indicate that the specified metadata will no longer be used. */
|
||||
void invalidate(MemoryBlock*);
|
||||
|
||||
public:
|
||||
MetadataCache(const MetadataCache&) = delete;
|
||||
MetadataCache& operator=(const MetadataCache&) = delete;
|
||||
|
||||
private:
|
||||
bool uses_gpu_;
|
||||
|
||||
private:
|
||||
typedef std::unordered_map<const MemoryBlock*, Metadata> MetadataMap;
|
||||
|
||||
private:
|
||||
MetadataMap cache_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace memory
|
||||
} // namespace paddle
|
@ -0,0 +1,70 @@
|
||||
/* 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. */
|
||||
|
||||
#include "paddle/memory/detail/meta_data.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace paddle {
|
||||
namespace memory {
|
||||
namespace detail {
|
||||
|
||||
Metadata::Metadata(MemoryBlock::Type t, size_t i, size_t s, size_t ts,
|
||||
MemoryBlock* l, MemoryBlock* r)
|
||||
: type(t),
|
||||
index(i),
|
||||
size(s),
|
||||
total_size(ts),
|
||||
left_buddy(l),
|
||||
right_buddy(r) {}
|
||||
|
||||
Metadata::Metadata()
|
||||
: type(MemoryBlock::INVALID_CHUNK),
|
||||
index(0),
|
||||
size(0),
|
||||
total_size(0),
|
||||
left_buddy(nullptr),
|
||||
right_buddy(nullptr) {}
|
||||
|
||||
template <class T>
|
||||
inline void hash_combine(std::size_t& seed, const T& v) {
|
||||
std::hash<T> hasher;
|
||||
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
inline size_t hash(const Metadata* metadata, size_t initial_seed) {
|
||||
size_t seed = initial_seed;
|
||||
|
||||
hash_combine(seed, (size_t)metadata->type);
|
||||
hash_combine(seed, metadata->index);
|
||||
hash_combine(seed, metadata->size);
|
||||
hash_combine(seed, metadata->total_size);
|
||||
hash_combine(seed, metadata->left_buddy);
|
||||
hash_combine(seed, metadata->right_buddy);
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
void Metadata::update_guards() {
|
||||
guard_begin = hash(this, 1);
|
||||
guard_end = hash(this, 2);
|
||||
}
|
||||
|
||||
bool Metadata::check_guards() const {
|
||||
return guard_begin == hash(this, 1) && guard_end == hash(this, 2);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace memory
|
||||
} // namespace paddle
|
@ -0,0 +1,54 @@
|
||||
/* 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/memory_block.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace paddle {
|
||||
namespace memory {
|
||||
namespace detail {
|
||||
|
||||
class Metadata {
|
||||
public:
|
||||
Metadata(MemoryBlock::Type t, size_t i, size_t s, size_t ts, MemoryBlock* l,
|
||||
MemoryBlock* r);
|
||||
Metadata();
|
||||
|
||||
public:
|
||||
/*! \brief Update the guards when metadata is changed */
|
||||
void update_guards();
|
||||
|
||||
/*! \brief Check consistency to previous modification */
|
||||
bool check_guards() const;
|
||||
|
||||
public:
|
||||
// TODO(gangliao): compress this
|
||||
// clang-format off
|
||||
size_t guard_begin = 0;
|
||||
MemoryBlock::Type type = MemoryBlock::INVALID_CHUNK;
|
||||
size_t index = 0;
|
||||
size_t size = 0;
|
||||
size_t total_size = 0;
|
||||
MemoryBlock* left_buddy = nullptr;
|
||||
MemoryBlock* right_buddy = nullptr;
|
||||
size_t guard_end = 0;
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace memory
|
||||
} // namespace paddle
|
@ -0,0 +1,138 @@
|
||||
/* 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. */
|
||||
|
||||
#include "paddle/memory/memory.h"
|
||||
#include "paddle/memory/detail/memory_block.h"
|
||||
#include "paddle/memory/detail/meta_data.h"
|
||||
|
||||
#include "paddle/platform/cpu_info.h"
|
||||
#include "paddle/platform/gpu_info.h"
|
||||
#include "paddle/platform/place.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <unordered_map>
|
||||
|
||||
inline bool is_aligned(void const *p) {
|
||||
return 0 == (reinterpret_cast<uintptr_t>(p) & 0x3);
|
||||
}
|
||||
|
||||
size_t align(size_t size, paddle::platform::CPUPlace place) {
|
||||
size += sizeof(paddle::memory::detail::Metadata);
|
||||
size_t alignment = paddle::platform::CpuMinChunkSize();
|
||||
size_t remaining = size % alignment;
|
||||
return remaining == 0 ? size : size + (alignment - remaining);
|
||||
}
|
||||
|
||||
TEST(BuddyAllocator, CPUAllocation) {
|
||||
void *p = nullptr;
|
||||
|
||||
EXPECT_EQ(p, nullptr);
|
||||
|
||||
paddle::platform::CPUPlace cpu;
|
||||
p = paddle::memory::Alloc(cpu, 4096);
|
||||
|
||||
EXPECT_NE(p, nullptr);
|
||||
|
||||
paddle::memory::Free(cpu, p);
|
||||
}
|
||||
|
||||
TEST(BuddyAllocator, CPUMultAlloc) {
|
||||
paddle::platform::CPUPlace cpu;
|
||||
|
||||
std::unordered_map<void *, size_t> ps;
|
||||
|
||||
size_t total_size = paddle::memory::Used(cpu);
|
||||
EXPECT_EQ(total_size, 0UL);
|
||||
|
||||
for (auto size :
|
||||
{128, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304}) {
|
||||
ps[paddle::memory::Alloc(cpu, size)] = size;
|
||||
|
||||
// Buddy Allocator doesn't manage too large memory chunk
|
||||
if (paddle::memory::Used(cpu) == total_size) continue;
|
||||
|
||||
size_t aligned_size = align(size, cpu);
|
||||
total_size += aligned_size;
|
||||
EXPECT_EQ(total_size, paddle::memory::Used(cpu));
|
||||
}
|
||||
|
||||
for (auto p : ps) {
|
||||
EXPECT_EQ(is_aligned(p.first), true);
|
||||
paddle::memory::Free(cpu, p.first);
|
||||
|
||||
// Buddy Allocator doesn't manage too large memory chunk
|
||||
if (paddle::memory::Used(cpu) == total_size) continue;
|
||||
|
||||
size_t aligned_size = align(p.second, cpu);
|
||||
total_size -= aligned_size;
|
||||
EXPECT_EQ(total_size, paddle::memory::Used(cpu));
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef PADDLE_ONLY_CPU
|
||||
|
||||
size_t align(size_t size, paddle::platform::GPUPlace place) {
|
||||
size += sizeof(paddle::memory::detail::Metadata);
|
||||
size_t alignment = paddle::platform::GpuMinChunkSize();
|
||||
size_t remaining = size % alignment;
|
||||
return remaining == 0 ? size : size + (alignment - remaining);
|
||||
}
|
||||
|
||||
TEST(BuddyAllocator, GPUAllocation) {
|
||||
void *p = nullptr;
|
||||
|
||||
EXPECT_EQ(p, nullptr);
|
||||
|
||||
paddle::platform::GPUPlace gpu(0);
|
||||
p = paddle::memory::Alloc(gpu, 4096);
|
||||
|
||||
EXPECT_NE(p, nullptr);
|
||||
|
||||
paddle::memory::Free(gpu, p);
|
||||
}
|
||||
|
||||
TEST(BuddyAllocator, GPUMultAlloc) {
|
||||
paddle::platform::GPUPlace gpu;
|
||||
|
||||
std::unordered_map<void *, size_t> ps;
|
||||
|
||||
size_t total_size = paddle::memory::Used(gpu);
|
||||
EXPECT_EQ(total_size, 0UL);
|
||||
|
||||
for (auto size :
|
||||
{128, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304}) {
|
||||
ps[paddle::memory::Alloc(gpu, size)] = size;
|
||||
|
||||
// Buddy Allocator doesn't manage too large memory chunk
|
||||
if (paddle::memory::Used(gpu) == total_size) continue;
|
||||
|
||||
size_t aligned_size = align(size, gpu);
|
||||
total_size += aligned_size;
|
||||
EXPECT_EQ(total_size, paddle::memory::Used(gpu));
|
||||
}
|
||||
|
||||
for (auto p : ps) {
|
||||
EXPECT_EQ(is_aligned(p.first), true);
|
||||
paddle::memory::Free(gpu, p.first);
|
||||
|
||||
// Buddy Allocator doesn't manage too large memory chunk
|
||||
if (paddle::memory::Used(gpu) == total_size) continue;
|
||||
|
||||
size_t aligned_size = align(p.second, gpu);
|
||||
total_size -= aligned_size;
|
||||
EXPECT_EQ(total_size, paddle::memory::Used(gpu));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PADDLE_ONLY_CPU
|
@ -0,0 +1,67 @@
|
||||
/* 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. */
|
||||
|
||||
#include "paddle/platform/cpu_info.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include "paddle/platform/error.h"
|
||||
|
||||
DEFINE_double(fraction_of_cpu_memory_to_use, 1,
|
||||
"Default use 100% of CPU memory for PaddlePaddle,"
|
||||
"reserve the rest for page tables, etc");
|
||||
|
||||
namespace paddle {
|
||||
namespace platform {
|
||||
|
||||
inline size_t CpuTotalPhysicalMemory() {
|
||||
#ifdef __APPLE__
|
||||
int mib[2];
|
||||
mib[0] = CTL_HW;
|
||||
mib[1] = HW_MEMSIZE;
|
||||
int64_t size = 0;
|
||||
size_t len = sizeof(size);
|
||||
if (sysctl(mib, 2, &size, &len, NULL, 0) == 0) return (size_t)size;
|
||||
return 0L;
|
||||
#else
|
||||
long pages = sysconf(_SC_PHYS_PAGES);
|
||||
long page_size = sysconf(_SC_PAGE_SIZE);
|
||||
return pages * page_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t CpuMaxAllocSize() {
|
||||
// For distributed systems, it requires configuring and limiting
|
||||
// the fraction of memory to use.
|
||||
return FLAGS_fraction_of_cpu_memory_to_use * CpuTotalPhysicalMemory();
|
||||
}
|
||||
|
||||
size_t CpuMinChunkSize() {
|
||||
// Allow to allocate the minimum chunk size is 4 KB.
|
||||
return 1 << 12;
|
||||
}
|
||||
|
||||
size_t CpuMaxChunkSize() {
|
||||
// Allow to allocate the maximum chunk size is roughly 3% of CPU memory.
|
||||
return CpuMaxAllocSize() / 32;
|
||||
}
|
||||
|
||||
} // namespace platform
|
||||
} // namespace paddle
|
@ -0,0 +1,32 @@
|
||||
/* 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 <stddef.h>
|
||||
|
||||
namespace paddle {
|
||||
namespace platform {
|
||||
|
||||
//! Get the maximum allocation size for a machine.
|
||||
size_t CpuMaxAllocSize();
|
||||
|
||||
//! Get the minimum chunk size for buddy allocator.
|
||||
size_t CpuMinChunkSize();
|
||||
|
||||
//! Get the maximum chunk size for buddy allocator.
|
||||
size_t CpuMaxChunkSize();
|
||||
|
||||
} // namespace platform
|
||||
} // namespace paddle
|
@ -0,0 +1,21 @@
|
||||
#include "paddle/platform/cpu_info.h"
|
||||
#include "paddle/string/printf.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include "glog/logging.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
DECLARE_double(fraction_of_cpu_memory_to_use);
|
||||
|
||||
TEST(CpuMemoryUsage, Print) {
|
||||
std::stringstream ss;
|
||||
size_t memory_size = paddle::platform::CpuMaxAllocSize() / 1024 / 1024 / 1024;
|
||||
float use_percent = FLAGS_fraction_of_cpu_memory_to_use * 100;
|
||||
|
||||
std::cout << paddle::string::Sprintf("\n%.2f %% of CPU Memory Usage: %d GB\n",
|
||||
use_percent, memory_size)
|
||||
<< std::endl;
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
#include <cuda_runtime.h>
|
||||
#include <stdio.h>
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#define CHECK_ERR(x) \
|
||||
if (x != cudaSuccess) { \
|
||||
fprintf(stderr, \
|
||||
"%s in %s at line %d\n", \
|
||||
cudaGetErrorString(err), \
|
||||
__FILE__, \
|
||||
__LINE__); \
|
||||
exit(-1); \
|
||||
}
|
||||
|
||||
__global__ void vecAdd(float *d_A, float *d_B, float *d_C, int n) {
|
||||
int i = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
if (i < n) {
|
||||
d_C[i] = d_A[i] + d_B[i];
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Cuda, Equality) {
|
||||
int n = 10;
|
||||
// Memory allocation for h_A, h_B and h_C (in the host)
|
||||
float h_A[10] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 0.0};
|
||||
float h_B[10] = {0.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0};
|
||||
float h_C[10];
|
||||
float *d_A, *d_B, *d_C;
|
||||
cudaError_t err;
|
||||
// Memory allocation for d_A, d_B and d_C (in the device)
|
||||
err = cudaMalloc((void **)&d_A, sizeof(float) * n);
|
||||
CHECK_ERR(err);
|
||||
|
||||
err = cudaMalloc((void **)&d_B, sizeof(float) * n);
|
||||
CHECK_ERR(err);
|
||||
|
||||
err = cudaMalloc((void **)&d_C, sizeof(float) * n);
|
||||
CHECK_ERR(err);
|
||||
|
||||
// Copying memory to device
|
||||
err = cudaMemcpy(d_A, h_A, sizeof(float) * n, cudaMemcpyHostToDevice);
|
||||
CHECK_ERR(err);
|
||||
|
||||
err = cudaMemcpy(d_B, h_B, sizeof(float) * n, cudaMemcpyHostToDevice);
|
||||
CHECK_ERR(err);
|
||||
|
||||
// Calling the kernel
|
||||
vecAdd<<<ceil(n / 256.0), 256>>>(d_A, d_B, d_C, n);
|
||||
|
||||
// Copying results back to host
|
||||
err = cudaMemcpy(h_C, d_C, sizeof(float) * n, cudaMemcpyDeviceToHost);
|
||||
CHECK_ERR(err);
|
||||
|
||||
EXPECT_EQ(h_C[0], 1.0);
|
||||
for (int i = 1; i < n - 1; ++i) {
|
||||
EXPECT_EQ(h_C[i], 11.0);
|
||||
}
|
||||
EXPECT_EQ(h_C[9], 1.0);
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#ifndef PADDLE_ONLY_CPU
|
||||
|
||||
#include <cublas_v2.h>
|
||||
#include <cudnn.h>
|
||||
#include <curand.h>
|
||||
#include <thrust/system/cuda/error.h>
|
||||
#include <thrust/system_error.h>
|
||||
|
||||
#endif // PADDLE_ONLY_CPU
|
||||
|
||||
namespace paddle {
|
||||
namespace platform {
|
||||
|
||||
#ifndef PADDLE_ONLY_CPU
|
||||
|
||||
inline void throw_on_error(cudaError_t e, const char* message) {
|
||||
if (e) {
|
||||
throw thrust::system_error(e, thrust::cuda_category(), message);
|
||||
}
|
||||
}
|
||||
|
||||
inline void throw_on_error(curandStatus_t stat, const char* message) {
|
||||
if (stat != CURAND_STATUS_SUCCESS) {
|
||||
throw thrust::system_error(cudaErrorLaunchFailure, thrust::cuda_category(),
|
||||
message);
|
||||
}
|
||||
}
|
||||
|
||||
inline void throw_on_error(cudnnStatus_t stat, const char* message) {
|
||||
std::stringstream ss;
|
||||
if (stat == CUDNN_STATUS_SUCCESS) {
|
||||
return;
|
||||
} else {
|
||||
ss << cudnnGetErrorString(stat);
|
||||
ss << ", " << message;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
inline void throw_on_error(cublasStatus_t stat, const char* message) {
|
||||
std::stringstream ss;
|
||||
if (stat == CUBLAS_STATUS_SUCCESS) {
|
||||
return;
|
||||
} else if (stat == CUBLAS_STATUS_NOT_INITIALIZED) {
|
||||
ss << "CUBLAS: not initialized";
|
||||
} else if (stat == CUBLAS_STATUS_ALLOC_FAILED) {
|
||||
ss << "CUBLAS: alloc failed";
|
||||
} else if (stat == CUBLAS_STATUS_INVALID_VALUE) {
|
||||
ss << "CUBLAS: invalid value";
|
||||
} else if (stat == CUBLAS_STATUS_ARCH_MISMATCH) {
|
||||
ss << "CUBLAS: arch mismatch";
|
||||
} else if (stat == CUBLAS_STATUS_MAPPING_ERROR) {
|
||||
ss << "CUBLAS: mapping error";
|
||||
} else if (stat == CUBLAS_STATUS_EXECUTION_FAILED) {
|
||||
ss << "CUBLAS: execution failed";
|
||||
} else if (stat == CUBLAS_STATUS_INTERNAL_ERROR) {
|
||||
ss << "CUBLAS: internal error";
|
||||
} else if (stat == CUBLAS_STATUS_NOT_SUPPORTED) {
|
||||
ss << "CUBLAS: not supported";
|
||||
} else if (stat == CUBLAS_STATUS_LICENSE_ERROR) {
|
||||
ss << "CUBLAS: license error";
|
||||
}
|
||||
ss << ", " << message;
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
inline void throw_on_error(cublasStatus_t stat) {
|
||||
const char* message = "";
|
||||
throw_on_error(stat, message);
|
||||
}
|
||||
|
||||
#endif // PADDLE_ONLY_CPU
|
||||
|
||||
inline void throw_on_error(int stat, const char* message) {
|
||||
if (stat) {
|
||||
throw std::runtime_error(message + (", stat = " + std::to_string(stat)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace platform
|
||||
} // namespace paddle
|
@ -0,0 +1,86 @@
|
||||
/* 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. */
|
||||
|
||||
#include "paddle/platform/gpu_info.h"
|
||||
#include "gflags/gflags.h"
|
||||
#include "paddle/platform/error.h"
|
||||
|
||||
DEFINE_double(fraction_of_gpu_memory_to_use, 0.95,
|
||||
"Default use 95% of GPU memory for PaddlePaddle,"
|
||||
"reserve the rest for page tables, etc");
|
||||
|
||||
namespace paddle {
|
||||
namespace platform {
|
||||
|
||||
int GetDeviceCount() {
|
||||
int count;
|
||||
throw_on_error(
|
||||
cudaGetDeviceCount(&count),
|
||||
"cudaGetDeviceCount failed in paddle::platform::GetDeviceCount");
|
||||
return count;
|
||||
}
|
||||
|
||||
int GetCurrentDeviceId() {
|
||||
int device_id;
|
||||
throw_on_error(
|
||||
cudaGetDevice(&device_id),
|
||||
"cudaGetDevice failed in paddle::platform::GetCurrentDeviceId");
|
||||
return device_id;
|
||||
}
|
||||
|
||||
void SetDeviceId(int id) {
|
||||
throw_on_error(cudaSetDevice(id),
|
||||
"cudaSetDevice failed in paddle::platform::SetDeviceId");
|
||||
}
|
||||
|
||||
void GpuMemoryUsage(size_t& available, size_t& total) {
|
||||
throw_on_error(cudaMemGetInfo(&available, &total),
|
||||
"cudaMemGetInfo failed in paddle::platform::GetMemoryUsage");
|
||||
}
|
||||
|
||||
size_t GpuMaxAllocSize() {
|
||||
size_t total = 0;
|
||||
size_t available = 0;
|
||||
|
||||
GpuMemoryUsage(available, total);
|
||||
|
||||
// Reserve the rest for page tables, etc.
|
||||
return static_cast<size_t>(total * FLAGS_fraction_of_gpu_memory_to_use);
|
||||
}
|
||||
|
||||
size_t GpuMinChunkSize() {
|
||||
// Allow to allocate the minimum chunk size is 256 bytes.
|
||||
return 1 << 8;
|
||||
}
|
||||
|
||||
size_t GpuMaxChunkSize() {
|
||||
size_t total = 0;
|
||||
size_t available = 0;
|
||||
|
||||
GpuMemoryUsage(available, total);
|
||||
|
||||
// Reserving the rest memory for page tables, etc.
|
||||
size_t reserving = (1 - FLAGS_fraction_of_gpu_memory_to_use) * total;
|
||||
|
||||
// If available less than minimum chunk size, no usable memory exists.
|
||||
available = std::max(available, GpuMinChunkSize()) - GpuMinChunkSize();
|
||||
|
||||
// If available less than reserving, no usable memory exists.
|
||||
size_t usable = std::max(available, reserving) - reserving;
|
||||
|
||||
return usable;
|
||||
}
|
||||
|
||||
} // namespace platform
|
||||
} // namespace paddle
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue