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