Make MemoryAllocator into a Customizable class (#8980)
Summary: - Make MemoryAllocator and its implementations into a Customizable class. - Added a "DefaultMemoryAllocator" which uses new and delete - Added a "CountedMemoryAllocator" that counts the number of allocs and free - Updated the existing tests to use these new allocators - Changed the memkind allocator test into a generic test that can test the various allocators. - Added tests for creating all of the allocators - Added tests to verify/create the JemallocNodumpAllocator using its options. Pull Request resolved: https://github.com/facebook/rocksdb/pull/8980 Reviewed By: zhichao-cao Differential Revision: D32990403 Pulled By: mrambacher fbshipit-source-id: 6fdfe8218c10dd8dfef34344a08201be1fa95c76main
parent
9828b6d5fd
commit
423538a816
@ -1,102 +0,0 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// Copyright (c) 2019 Intel Corporation
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include <cstdio> |
||||
|
||||
#ifdef MEMKIND |
||||
#include "memkind_kmem_allocator.h" |
||||
#include "rocksdb/cache.h" |
||||
#include "rocksdb/db.h" |
||||
#include "rocksdb/options.h" |
||||
#include "table/block_based/block_based_table_factory.h" |
||||
#include "test_util/testharness.h" |
||||
|
||||
namespace ROCKSDB_NAMESPACE { |
||||
TEST(MemkindKmemAllocatorTest, Allocate) { |
||||
MemkindKmemAllocator allocator; |
||||
void* p; |
||||
try { |
||||
p = allocator.Allocate(1024); |
||||
} catch (const std::bad_alloc& e) { |
||||
return; |
||||
} |
||||
ASSERT_NE(p, nullptr); |
||||
size_t size = allocator.UsableSize(p, 1024); |
||||
ASSERT_GE(size, 1024); |
||||
allocator.Deallocate(p); |
||||
} |
||||
|
||||
TEST(MemkindKmemAllocatorTest, DatabaseBlockCache) { |
||||
// Check if a memory node is available for allocation
|
||||
try { |
||||
MemkindKmemAllocator allocator; |
||||
allocator.Allocate(1024); |
||||
} catch (const std::bad_alloc& e) { |
||||
return; // if no node available, skip the test
|
||||
} |
||||
|
||||
// Create database with block cache using MemkindKmemAllocator
|
||||
Options options; |
||||
std::string dbname = test::PerThreadDBPath("memkind_kmem_allocator_test"); |
||||
ASSERT_OK(DestroyDB(dbname, options)); |
||||
|
||||
options.create_if_missing = true; |
||||
std::shared_ptr<Cache> cache = NewLRUCache( |
||||
1024 * 1024, 6, false, false, std::make_shared<MemkindKmemAllocator>()); |
||||
BlockBasedTableOptions table_options; |
||||
table_options.block_cache = cache; |
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options)); |
||||
|
||||
DB* db = nullptr; |
||||
Status s = DB::Open(options, dbname, &db); |
||||
ASSERT_OK(s); |
||||
ASSERT_NE(db, nullptr); |
||||
ASSERT_EQ(cache->GetUsage(), 0); |
||||
|
||||
// Write 2kB (200 values, each 10 bytes)
|
||||
int num_keys = 200; |
||||
WriteOptions wo; |
||||
std::string val = "0123456789"; |
||||
for (int i = 0; i < num_keys; i++) { |
||||
std::string key = std::to_string(i); |
||||
s = db->Put(wo, Slice(key), Slice(val)); |
||||
ASSERT_OK(s); |
||||
} |
||||
ASSERT_OK(db->Flush(FlushOptions())); // Flush all data from memtable so that
|
||||
// reads are from block cache
|
||||
|
||||
// Read and check block cache usage
|
||||
ReadOptions ro; |
||||
std::string result; |
||||
for (int i = 0; i < num_keys; i++) { |
||||
std::string key = std::to_string(i); |
||||
s = db->Get(ro, key, &result); |
||||
ASSERT_OK(s); |
||||
ASSERT_EQ(result, val); |
||||
} |
||||
ASSERT_GT(cache->GetUsage(), 2000); |
||||
|
||||
// Close database
|
||||
s = db->Close(); |
||||
ASSERT_OK(s); |
||||
ASSERT_OK(DestroyDB(dbname, options)); |
||||
} |
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
||||
|
||||
#else |
||||
|
||||
int main(int /*argc*/, char** /*argv*/) { |
||||
printf( |
||||
"Skip memkind_kmem_allocator_test as the required library memkind is " |
||||
"missing."); |
||||
} |
||||
|
||||
#endif // MEMKIND
|
@ -0,0 +1,91 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "rocksdb/memory_allocator.h" |
||||
|
||||
#include "memory/jemalloc_nodump_allocator.h" |
||||
#include "memory/memkind_kmem_allocator.h" |
||||
#include "rocksdb/utilities/customizable_util.h" |
||||
#include "rocksdb/utilities/object_registry.h" |
||||
#include "rocksdb/utilities/options_type.h" |
||||
#include "utilities/memory_allocators.h" |
||||
|
||||
namespace ROCKSDB_NAMESPACE { |
||||
namespace { |
||||
static std::unordered_map<std::string, OptionTypeInfo> ma_wrapper_type_info = { |
||||
#ifndef ROCKSDB_LITE |
||||
{"target", OptionTypeInfo::AsCustomSharedPtr<MemoryAllocator>( |
||||
0, OptionVerificationType::kByName, OptionTypeFlags::kNone)}, |
||||
#endif // ROCKSDB_LITE
|
||||
}; |
||||
|
||||
#ifndef ROCKSDB_LITE |
||||
static int RegisterBuiltinAllocators(ObjectLibrary& library, |
||||
const std::string& /*arg*/) { |
||||
library.Register<MemoryAllocator>( |
||||
DefaultMemoryAllocator::kClassName(), |
||||
[](const std::string& /*uri*/, std::unique_ptr<MemoryAllocator>* guard, |
||||
std::string* /*errmsg*/) { |
||||
guard->reset(new DefaultMemoryAllocator()); |
||||
return guard->get(); |
||||
}); |
||||
library.Register<MemoryAllocator>( |
||||
CountedMemoryAllocator::kClassName(), |
||||
[](const std::string& /*uri*/, std::unique_ptr<MemoryAllocator>* guard, |
||||
std::string* /*errmsg*/) { |
||||
guard->reset(new CountedMemoryAllocator( |
||||
std::make_shared<DefaultMemoryAllocator>())); |
||||
return guard->get(); |
||||
}); |
||||
library.Register<MemoryAllocator>( |
||||
JemallocNodumpAllocator::kClassName(), |
||||
[](const std::string& /*uri*/, std::unique_ptr<MemoryAllocator>* guard, |
||||
std::string* errmsg) { |
||||
if (JemallocNodumpAllocator::IsSupported(errmsg)) { |
||||
JemallocAllocatorOptions options; |
||||
guard->reset(new JemallocNodumpAllocator(options)); |
||||
} |
||||
return guard->get(); |
||||
}); |
||||
library.Register<MemoryAllocator>( |
||||
MemkindKmemAllocator::kClassName(), |
||||
[](const std::string& /*uri*/, std::unique_ptr<MemoryAllocator>* guard, |
||||
std::string* errmsg) { |
||||
if (MemkindKmemAllocator::IsSupported(errmsg)) { |
||||
guard->reset(new MemkindKmemAllocator()); |
||||
} |
||||
return guard->get(); |
||||
}); |
||||
size_t num_types; |
||||
return static_cast<int>(library.GetFactoryCount(&num_types)); |
||||
} |
||||
#endif // ROCKSDB_LITE
|
||||
} // namespace
|
||||
|
||||
MemoryAllocatorWrapper::MemoryAllocatorWrapper( |
||||
const std::shared_ptr<MemoryAllocator>& t) |
||||
: target_(t) { |
||||
RegisterOptions("", &target_, &ma_wrapper_type_info); |
||||
} |
||||
|
||||
Status MemoryAllocator::CreateFromString( |
||||
const ConfigOptions& options, const std::string& value, |
||||
std::shared_ptr<MemoryAllocator>* result) { |
||||
#ifndef ROCKSDB_LITE |
||||
static std::once_flag once; |
||||
std::call_once(once, [&]() { |
||||
RegisterBuiltinAllocators(*(ObjectLibrary::Default().get()), ""); |
||||
}); |
||||
#else |
||||
if (value == DefaultMemoryAllocator::kClassName()) { |
||||
result->reset(new DefaultMemoryAllocator()); |
||||
return Status::OK(); |
||||
} |
||||
#endif // ROCKSDB_LITE
|
||||
ConfigOptions copy = options; |
||||
copy.invoke_prepare_options = true; |
||||
return LoadManagedObject<MemoryAllocator>(copy, value, result); |
||||
} |
||||
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,236 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// Copyright (c) 2019 Intel Corporation
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include <cstdio> |
||||
|
||||
#include "memory/jemalloc_nodump_allocator.h" |
||||
#include "memory/memkind_kmem_allocator.h" |
||||
#include "rocksdb/cache.h" |
||||
#include "rocksdb/convenience.h" |
||||
#include "rocksdb/db.h" |
||||
#include "rocksdb/options.h" |
||||
#include "table/block_based/block_based_table_factory.h" |
||||
#include "test_util/testharness.h" |
||||
#include "utilities/memory_allocators.h" |
||||
|
||||
namespace ROCKSDB_NAMESPACE { |
||||
class MemoryAllocatorTest |
||||
: public testing::Test, |
||||
public ::testing::WithParamInterface<std::tuple<std::string, bool>> { |
||||
public: |
||||
MemoryAllocatorTest() { |
||||
std::tie(id_, supported_) = GetParam(); |
||||
Status s = |
||||
MemoryAllocator::CreateFromString(ConfigOptions(), id_, &allocator_); |
||||
if (supported_) { |
||||
EXPECT_OK(s); |
||||
} else if (!s.ok()) { |
||||
EXPECT_TRUE(s.IsNotSupported()); |
||||
} |
||||
} |
||||
bool IsSupported() { return supported_; } |
||||
|
||||
std::shared_ptr<MemoryAllocator> allocator_; |
||||
std::string id_; |
||||
|
||||
private: |
||||
bool supported_; |
||||
}; |
||||
|
||||
TEST_P(MemoryAllocatorTest, Allocate) { |
||||
if (!IsSupported()) { |
||||
return; |
||||
} |
||||
void* p = allocator_->Allocate(1024); |
||||
ASSERT_NE(p, nullptr); |
||||
size_t size = allocator_->UsableSize(p, 1024); |
||||
ASSERT_GE(size, 1024); |
||||
allocator_->Deallocate(p); |
||||
} |
||||
|
||||
TEST_P(MemoryAllocatorTest, CreateAllocator) { |
||||
ConfigOptions config_options; |
||||
config_options.ignore_unknown_options = false; |
||||
config_options.ignore_unsupported_options = false; |
||||
std::shared_ptr<MemoryAllocator> orig, copy; |
||||
Status s = MemoryAllocator::CreateFromString(config_options, id_, &orig); |
||||
if (!IsSupported()) { |
||||
ASSERT_TRUE(s.IsNotSupported()); |
||||
} else { |
||||
ASSERT_OK(s); |
||||
ASSERT_NE(orig, nullptr); |
||||
#ifndef ROCKSDB_LITE |
||||
std::string str = orig->ToString(config_options); |
||||
ASSERT_OK(MemoryAllocator::CreateFromString(config_options, str, ©)); |
||||
ASSERT_EQ(orig, copy); |
||||
#endif // ROCKSDB_LITE
|
||||
} |
||||
} |
||||
|
||||
TEST_P(MemoryAllocatorTest, DatabaseBlockCache) { |
||||
if (!IsSupported()) { |
||||
// Check if a memory node is available for allocation
|
||||
} |
||||
|
||||
// Create database with block cache using the MemoryAllocator
|
||||
Options options; |
||||
std::string dbname = test::PerThreadDBPath("allocator_test"); |
||||
ASSERT_OK(DestroyDB(dbname, options)); |
||||
|
||||
options.create_if_missing = true; |
||||
BlockBasedTableOptions table_options; |
||||
auto cache = NewLRUCache(1024 * 1024, 6, false, false, allocator_); |
||||
table_options.block_cache = cache; |
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options)); |
||||
DB* db = nullptr; |
||||
Status s = DB::Open(options, dbname, &db); |
||||
ASSERT_OK(s); |
||||
ASSERT_NE(db, nullptr); |
||||
ASSERT_LE(cache->GetUsage(), 104); // Cache will contain stats
|
||||
|
||||
// Write 2kB (200 values, each 10 bytes)
|
||||
int num_keys = 200; |
||||
WriteOptions wo; |
||||
std::string val = "0123456789"; |
||||
for (int i = 0; i < num_keys; i++) { |
||||
std::string key = std::to_string(i); |
||||
s = db->Put(wo, Slice(key), Slice(val)); |
||||
ASSERT_OK(s); |
||||
} |
||||
ASSERT_OK(db->Flush(FlushOptions())); // Flush all data from memtable so that
|
||||
// reads are from block cache
|
||||
|
||||
// Read and check block cache usage
|
||||
ReadOptions ro; |
||||
std::string result; |
||||
for (int i = 0; i < num_keys; i++) { |
||||
std::string key = std::to_string(i); |
||||
s = db->Get(ro, key, &result); |
||||
ASSERT_OK(s); |
||||
ASSERT_EQ(result, val); |
||||
} |
||||
ASSERT_GT(cache->GetUsage(), 2000); |
||||
|
||||
// Close database
|
||||
s = db->Close(); |
||||
ASSERT_OK(s); |
||||
delete db; |
||||
ASSERT_OK(DestroyDB(dbname, options)); |
||||
} |
||||
|
||||
class CreateMemoryAllocatorTest : public testing::Test { |
||||
public: |
||||
CreateMemoryAllocatorTest() { |
||||
config_options_.ignore_unknown_options = false; |
||||
config_options_.ignore_unsupported_options = false; |
||||
} |
||||
ConfigOptions config_options_; |
||||
}; |
||||
|
||||
TEST_F(CreateMemoryAllocatorTest, JemallocOptionsTest) { |
||||
std::shared_ptr<MemoryAllocator> allocator; |
||||
std::string id = std::string("id=") + JemallocNodumpAllocator::kClassName(); |
||||
Status s = MemoryAllocator::CreateFromString(config_options_, id, &allocator); |
||||
if (!JemallocNodumpAllocator::IsSupported()) { |
||||
ASSERT_TRUE(s.IsNotSupported()); |
||||
ROCKSDB_GTEST_SKIP("JEMALLOC not supported"); |
||||
return; |
||||
} |
||||
ASSERT_OK(s); |
||||
ASSERT_NE(allocator, nullptr); |
||||
JemallocAllocatorOptions jopts; |
||||
auto opts = allocator->GetOptions<JemallocAllocatorOptions>(); |
||||
ASSERT_NE(opts, nullptr); |
||||
ASSERT_EQ(opts->limit_tcache_size, jopts.limit_tcache_size); |
||||
ASSERT_EQ(opts->tcache_size_lower_bound, jopts.tcache_size_lower_bound); |
||||
ASSERT_EQ(opts->tcache_size_upper_bound, jopts.tcache_size_upper_bound); |
||||
|
||||
ASSERT_NOK(MemoryAllocator::CreateFromString( |
||||
config_options_, |
||||
id + "; limit_tcache_size=true; tcache_size_lower_bound=4096; " |
||||
"tcache_size_upper_bound=1024", |
||||
&allocator)); |
||||
ASSERT_OK(MemoryAllocator::CreateFromString( |
||||
config_options_, |
||||
id + "; limit_tcache_size=false; tcache_size_lower_bound=4096; " |
||||
"tcache_size_upper_bound=1024", |
||||
&allocator)); |
||||
opts = allocator->GetOptions<JemallocAllocatorOptions>(); |
||||
ASSERT_NE(opts, nullptr); |
||||
ASSERT_EQ(opts->limit_tcache_size, false); |
||||
ASSERT_EQ(opts->tcache_size_lower_bound, 4096U); |
||||
ASSERT_EQ(opts->tcache_size_upper_bound, 1024U); |
||||
ASSERT_OK(MemoryAllocator::CreateFromString( |
||||
config_options_, |
||||
id + "; limit_tcache_size=true; tcache_size_upper_bound=4096; " |
||||
"tcache_size_lower_bound=1024", |
||||
&allocator)); |
||||
opts = allocator->GetOptions<JemallocAllocatorOptions>(); |
||||
ASSERT_NE(opts, nullptr); |
||||
ASSERT_EQ(opts->limit_tcache_size, true); |
||||
ASSERT_EQ(opts->tcache_size_lower_bound, 1024U); |
||||
ASSERT_EQ(opts->tcache_size_upper_bound, 4096U); |
||||
} |
||||
|
||||
TEST_F(CreateMemoryAllocatorTest, NewJemallocNodumpAllocator) { |
||||
JemallocAllocatorOptions jopts; |
||||
std::shared_ptr<MemoryAllocator> allocator; |
||||
|
||||
jopts.limit_tcache_size = true; |
||||
jopts.tcache_size_lower_bound = 2 * 1024; |
||||
jopts.tcache_size_upper_bound = 1024; |
||||
|
||||
ASSERT_NOK(NewJemallocNodumpAllocator(jopts, nullptr)); |
||||
Status s = NewJemallocNodumpAllocator(jopts, &allocator); |
||||
std::string msg; |
||||
if (!JemallocNodumpAllocator::IsSupported(&msg)) { |
||||
ASSERT_TRUE(s.IsNotSupported()); |
||||
ROCKSDB_GTEST_SKIP("JEMALLOC not supported"); |
||||
return; |
||||
} |
||||
ASSERT_NOK(s); // Invalid options
|
||||
ASSERT_EQ(allocator, nullptr); |
||||
|
||||
jopts.tcache_size_upper_bound = 4 * 1024; |
||||
ASSERT_OK(NewJemallocNodumpAllocator(jopts, &allocator)); |
||||
ASSERT_NE(allocator, nullptr); |
||||
auto opts = allocator->GetOptions<JemallocAllocatorOptions>(); |
||||
ASSERT_EQ(opts->tcache_size_upper_bound, jopts.tcache_size_upper_bound); |
||||
ASSERT_EQ(opts->tcache_size_lower_bound, jopts.tcache_size_lower_bound); |
||||
ASSERT_EQ(opts->limit_tcache_size, jopts.limit_tcache_size); |
||||
|
||||
jopts.limit_tcache_size = false; |
||||
ASSERT_OK(NewJemallocNodumpAllocator(jopts, &allocator)); |
||||
ASSERT_NE(allocator, nullptr); |
||||
opts = allocator->GetOptions<JemallocAllocatorOptions>(); |
||||
ASSERT_EQ(opts->tcache_size_upper_bound, jopts.tcache_size_upper_bound); |
||||
ASSERT_EQ(opts->tcache_size_lower_bound, jopts.tcache_size_lower_bound); |
||||
ASSERT_EQ(opts->limit_tcache_size, jopts.limit_tcache_size); |
||||
} |
||||
|
||||
INSTANTIATE_TEST_CASE_P(DefaultMemoryAllocator, MemoryAllocatorTest, |
||||
::testing::Values(std::make_tuple( |
||||
DefaultMemoryAllocator::kClassName(), true))); |
||||
#ifdef MEMKIND |
||||
INSTANTIATE_TEST_CASE_P( |
||||
MemkindkMemAllocator, MemoryAllocatorTest, |
||||
::testing::Values(std::make_tuple(MemkindKmemAllocator::kClassName(), |
||||
MemkindKmemAllocator::IsSupported()))); |
||||
#endif // MEMKIND
|
||||
|
||||
#ifdef ROCKSDB_JEMALLOC |
||||
INSTANTIATE_TEST_CASE_P( |
||||
JemallocNodumpAllocator, MemoryAllocatorTest, |
||||
::testing::Values(std::make_tuple(JemallocNodumpAllocator::kClassName(), |
||||
JemallocNodumpAllocator::IsSupported()))); |
||||
#endif // ROCKSDB_JEMALLOC
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -0,0 +1,104 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once |
||||
|
||||
#include <atomic> |
||||
|
||||
#include "rocksdb/memory_allocator.h" |
||||
|
||||
namespace ROCKSDB_NAMESPACE { |
||||
// A memory allocator using new/delete
|
||||
class DefaultMemoryAllocator : public MemoryAllocator { |
||||
public: |
||||
static const char* kClassName() { return "DefaultMemoryAllocator"; } |
||||
const char* Name() const override { return kClassName(); } |
||||
void* Allocate(size_t size) override { |
||||
return static_cast<void*>(new char[size]); |
||||
} |
||||
|
||||
void Deallocate(void* p) override { delete[] static_cast<char*>(p); } |
||||
}; |
||||
|
||||
// Base class for a MemoryAllocator. This implementation does nothing
|
||||
// and implements the methods in failuse mode (assert if the methods are
|
||||
// invoked). Implementations can extend this class and override these methods
|
||||
// when they are enabled via compiler switches (e.g., the
|
||||
// JeMallocMemoryAllocator can define these methods if ROCKSDB_JEMALLOC is
|
||||
// defined at compile time. If compiled in "disabled" mode, this class provides
|
||||
// default/failure implementations. If compiled in "enabled" mode, the derived
|
||||
// class needs to provide the appopriate "enabled" methods for the "real"
|
||||
// implementation. Failure of the "real" implementation to implement ovreride
|
||||
// any of these methods will result in an assert failure.
|
||||
class BaseMemoryAllocator : public MemoryAllocator { |
||||
public: |
||||
void* Allocate(size_t /*size*/) override { |
||||
assert(false); |
||||
return nullptr; |
||||
} |
||||
|
||||
void Deallocate(void* /*p*/) override { assert(false); } |
||||
}; |
||||
|
||||
// A Wrapped MemoryAllocator. Delegates the memory allcator functions to the
|
||||
// wrapped one.
|
||||
class MemoryAllocatorWrapper : public MemoryAllocator { |
||||
public: |
||||
// Initialize an MemoryAllocatorWrapper that delegates all calls to *t
|
||||
explicit MemoryAllocatorWrapper(const std::shared_ptr<MemoryAllocator>& t); |
||||
~MemoryAllocatorWrapper() override {} |
||||
|
||||
// Return the target to which to forward all calls
|
||||
MemoryAllocator* target() const { return target_.get(); } |
||||
// Allocate a block of at least size. Has to be thread-safe.
|
||||
void* Allocate(size_t size) override { return target_->Allocate(size); } |
||||
|
||||
// Deallocate previously allocated block. Has to be thread-safe.
|
||||
void Deallocate(void* p) override { return target_->Deallocate(p); } |
||||
|
||||
// Returns the memory size of the block allocated at p. The default
|
||||
// implementation that just returns the original allocation_size is fine.
|
||||
size_t UsableSize(void* p, size_t allocation_size) const override { |
||||
return target_->UsableSize(p, allocation_size); |
||||
} |
||||
|
||||
const Customizable* Inner() const override { return target_.get(); } |
||||
|
||||
protected: |
||||
std::shared_ptr<MemoryAllocator> target_; |
||||
}; |
||||
|
||||
// A memory allocator that counts the number of allocations and deallocations
|
||||
// This class is useful if the number of memory allocations/dellocations is
|
||||
// important.
|
||||
class CountedMemoryAllocator : public MemoryAllocatorWrapper { |
||||
public: |
||||
CountedMemoryAllocator() |
||||
: MemoryAllocatorWrapper(std::make_shared<DefaultMemoryAllocator>()), |
||||
allocations_(0), |
||||
deallocations_(0) {} |
||||
|
||||
explicit CountedMemoryAllocator(const std::shared_ptr<MemoryAllocator>& t) |
||||
: MemoryAllocatorWrapper(t), allocations_(0), deallocations_(0) {} |
||||
static const char* kClassName() { return "CountedMemoryAllocator"; } |
||||
const char* Name() const override { return kClassName(); } |
||||
std::string GetId() const override { return std::string(Name()); } |
||||
void* Allocate(size_t size) override { |
||||
allocations_++; |
||||
return MemoryAllocatorWrapper::Allocate(size); |
||||
} |
||||
|
||||
void Deallocate(void* p) override { |
||||
deallocations_++; |
||||
MemoryAllocatorWrapper::Deallocate(p); |
||||
} |
||||
uint64_t GetNumAllocations() const { return allocations_; } |
||||
uint64_t GetNumDeallocations() const { return deallocations_; } |
||||
|
||||
private: |
||||
std::atomic<uint64_t> allocations_; |
||||
std::atomic<uint64_t> deallocations_; |
||||
}; |
||||
} // namespace ROCKSDB_NAMESPACE
|
Loading…
Reference in new issue