1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // Copyright (c) 2019 Intel Corporation
3 // This source code is licensed under both the GPLv2 (found in the
4 // COPYING file in the root directory) and Apache 2.0 License
5 // (found in the LICENSE.Apache file in the root directory).
9 #include "memory/jemalloc_nodump_allocator.h"
10 #include "memory/memkind_kmem_allocator.h"
11 #include "rocksdb/cache.h"
12 #include "rocksdb/convenience.h"
13 #include "rocksdb/db.h"
14 #include "rocksdb/options.h"
15 #include "table/block_based/block_based_table_factory.h"
16 #include "test_util/testharness.h"
17 #include "utilities/memory_allocators.h"
19 namespace ROCKSDB_NAMESPACE
{
21 // TODO: the tests do not work in LITE mode due to relying on
22 // `CreateFromString()` to create non-default memory allocators.
25 class MemoryAllocatorTest
26 : public testing::Test
,
27 public ::testing::WithParamInterface
<std::tuple
<std::string
, bool>> {
29 MemoryAllocatorTest() {
30 std::tie(id_
, supported_
) = GetParam();
32 MemoryAllocator::CreateFromString(ConfigOptions(), id_
, &allocator_
);
33 EXPECT_EQ(supported_
, s
.ok());
35 bool IsSupported() { return supported_
; }
37 std::shared_ptr
<MemoryAllocator
> allocator_
;
44 TEST_P(MemoryAllocatorTest
, Allocate
) {
48 void* p
= allocator_
->Allocate(1024);
49 ASSERT_NE(p
, nullptr);
50 size_t size
= allocator_
->UsableSize(p
, 1024);
51 ASSERT_GE(size
, 1024);
52 allocator_
->Deallocate(p
);
55 TEST_P(MemoryAllocatorTest
, CreateAllocator
) {
56 ConfigOptions config_options
;
57 config_options
.ignore_unknown_options
= false;
58 config_options
.ignore_unsupported_options
= false;
59 std::shared_ptr
<MemoryAllocator
> orig
, copy
;
60 Status s
= MemoryAllocator::CreateFromString(config_options
, id_
, &orig
);
62 ASSERT_TRUE(s
.IsNotSupported());
65 ASSERT_NE(orig
, nullptr);
67 std::string str
= orig
->ToString(config_options
);
68 ASSERT_OK(MemoryAllocator::CreateFromString(config_options
, str
, ©
));
69 ASSERT_EQ(orig
, copy
);
70 #endif // ROCKSDB_LITE
74 TEST_P(MemoryAllocatorTest
, DatabaseBlockCache
) {
76 // Check if a memory node is available for allocation
79 // Create database with block cache using the MemoryAllocator
81 std::string dbname
= test::PerThreadDBPath("allocator_test");
82 ASSERT_OK(DestroyDB(dbname
, options
));
84 options
.create_if_missing
= true;
85 BlockBasedTableOptions table_options
;
86 auto cache
= NewLRUCache(1024 * 1024, 6, false, 0.0, allocator_
);
87 table_options
.block_cache
= cache
;
88 options
.table_factory
.reset(NewBlockBasedTableFactory(table_options
));
90 Status s
= DB::Open(options
, dbname
, &db
);
92 ASSERT_NE(db
, nullptr);
93 ASSERT_LE(cache
->GetUsage(), 104); // Cache will contain stats
95 // Write 2kB (200 values, each 10 bytes)
98 std::string val
= "0123456789";
99 for (int i
= 0; i
< num_keys
; i
++) {
100 std::string key
= std::to_string(i
);
101 s
= db
->Put(wo
, Slice(key
), Slice(val
));
104 ASSERT_OK(db
->Flush(FlushOptions())); // Flush all data from memtable so that
105 // reads are from block cache
107 // Read and check block cache usage
110 for (int i
= 0; i
< num_keys
; i
++) {
111 std::string key
= std::to_string(i
);
112 s
= db
->Get(ro
, key
, &result
);
114 ASSERT_EQ(result
, val
);
116 ASSERT_GT(cache
->GetUsage(), 2000);
122 ASSERT_OK(DestroyDB(dbname
, options
));
125 class CreateMemoryAllocatorTest
: public testing::Test
{
127 CreateMemoryAllocatorTest() {
128 config_options_
.ignore_unknown_options
= false;
129 config_options_
.ignore_unsupported_options
= false;
131 ConfigOptions config_options_
;
134 TEST_F(CreateMemoryAllocatorTest
, JemallocOptionsTest
) {
135 std::shared_ptr
<MemoryAllocator
> allocator
;
136 std::string id
= std::string("id=") + JemallocNodumpAllocator::kClassName();
137 Status s
= MemoryAllocator::CreateFromString(config_options_
, id
, &allocator
);
138 if (!JemallocNodumpAllocator::IsSupported()) {
140 ROCKSDB_GTEST_BYPASS("JEMALLOC not supported");
144 ASSERT_NE(allocator
, nullptr);
145 JemallocAllocatorOptions jopts
;
146 auto opts
= allocator
->GetOptions
<JemallocAllocatorOptions
>();
147 ASSERT_NE(opts
, nullptr);
148 ASSERT_EQ(opts
->limit_tcache_size
, jopts
.limit_tcache_size
);
149 ASSERT_EQ(opts
->tcache_size_lower_bound
, jopts
.tcache_size_lower_bound
);
150 ASSERT_EQ(opts
->tcache_size_upper_bound
, jopts
.tcache_size_upper_bound
);
152 ASSERT_NOK(MemoryAllocator::CreateFromString(
154 id
+ "; limit_tcache_size=true; tcache_size_lower_bound=4096; "
155 "tcache_size_upper_bound=1024",
157 ASSERT_OK(MemoryAllocator::CreateFromString(
159 id
+ "; limit_tcache_size=false; tcache_size_lower_bound=4096; "
160 "tcache_size_upper_bound=1024",
162 opts
= allocator
->GetOptions
<JemallocAllocatorOptions
>();
163 ASSERT_NE(opts
, nullptr);
164 ASSERT_EQ(opts
->limit_tcache_size
, false);
165 ASSERT_EQ(opts
->tcache_size_lower_bound
, 4096U);
166 ASSERT_EQ(opts
->tcache_size_upper_bound
, 1024U);
167 ASSERT_OK(MemoryAllocator::CreateFromString(
169 id
+ "; limit_tcache_size=true; tcache_size_upper_bound=4096; "
170 "tcache_size_lower_bound=1024",
172 opts
= allocator
->GetOptions
<JemallocAllocatorOptions
>();
173 ASSERT_NE(opts
, nullptr);
174 ASSERT_EQ(opts
->limit_tcache_size
, true);
175 ASSERT_EQ(opts
->tcache_size_lower_bound
, 1024U);
176 ASSERT_EQ(opts
->tcache_size_upper_bound
, 4096U);
179 TEST_F(CreateMemoryAllocatorTest
, NewJemallocNodumpAllocator
) {
180 JemallocAllocatorOptions jopts
;
181 std::shared_ptr
<MemoryAllocator
> allocator
;
183 jopts
.limit_tcache_size
= true;
184 jopts
.tcache_size_lower_bound
= 2 * 1024;
185 jopts
.tcache_size_upper_bound
= 1024;
187 ASSERT_NOK(NewJemallocNodumpAllocator(jopts
, nullptr));
188 Status s
= NewJemallocNodumpAllocator(jopts
, &allocator
);
190 if (!JemallocNodumpAllocator::IsSupported(&msg
)) {
192 ROCKSDB_GTEST_BYPASS("JEMALLOC not supported");
195 ASSERT_NOK(s
); // Invalid options
196 ASSERT_EQ(allocator
, nullptr);
198 jopts
.tcache_size_upper_bound
= 4 * 1024;
199 ASSERT_OK(NewJemallocNodumpAllocator(jopts
, &allocator
));
200 ASSERT_NE(allocator
, nullptr);
201 auto opts
= allocator
->GetOptions
<JemallocAllocatorOptions
>();
202 ASSERT_EQ(opts
->tcache_size_upper_bound
, jopts
.tcache_size_upper_bound
);
203 ASSERT_EQ(opts
->tcache_size_lower_bound
, jopts
.tcache_size_lower_bound
);
204 ASSERT_EQ(opts
->limit_tcache_size
, jopts
.limit_tcache_size
);
206 jopts
.limit_tcache_size
= false;
207 ASSERT_OK(NewJemallocNodumpAllocator(jopts
, &allocator
));
208 ASSERT_NE(allocator
, nullptr);
209 opts
= allocator
->GetOptions
<JemallocAllocatorOptions
>();
210 ASSERT_EQ(opts
->tcache_size_upper_bound
, jopts
.tcache_size_upper_bound
);
211 ASSERT_EQ(opts
->tcache_size_lower_bound
, jopts
.tcache_size_lower_bound
);
212 ASSERT_EQ(opts
->limit_tcache_size
, jopts
.limit_tcache_size
);
215 INSTANTIATE_TEST_CASE_P(DefaultMemoryAllocator
, MemoryAllocatorTest
,
216 ::testing::Values(std::make_tuple(
217 DefaultMemoryAllocator::kClassName(), true)));
219 INSTANTIATE_TEST_CASE_P(
220 MemkindkMemAllocator
, MemoryAllocatorTest
,
221 ::testing::Values(std::make_tuple(MemkindKmemAllocator::kClassName(),
222 MemkindKmemAllocator::IsSupported())));
225 #ifdef ROCKSDB_JEMALLOC
226 INSTANTIATE_TEST_CASE_P(
227 JemallocNodumpAllocator
, MemoryAllocatorTest
,
228 ::testing::Values(std::make_tuple(JemallocNodumpAllocator::kClassName(),
229 JemallocNodumpAllocator::IsSupported())));
230 #endif // ROCKSDB_JEMALLOC
232 #endif // ROCKSDB_LITE
234 } // namespace ROCKSDB_NAMESPACE
236 int main(int argc
, char** argv
) {
237 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
238 ::testing::InitGoogleTest(&argc
, argv
);
239 return RUN_ALL_TESTS();