GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 6.3% 28 / 0 / 444
Functions: 12.1% 11 / 0 / 91
Branches: 1.6% 10 / 0 / 624

memory/allocators/persist_loop_slab_allocator.h
Line Branch Exec Source
1 #pragma once
2
3 #include <fcntl.h>
4 #include <sys/mman.h>
5
6 #include <deque>
7 #include <fstream>
8 #include <functional>
9 #include <memory>
10 #include <mutex>
11 #include <queue>
12 #include <set>
13 #include <string>
14 #include <thread>
15 #include <unordered_map>
16 #include <utility>
17 #include <vector>
18 #include "base/factory.h"
19 #include "base/async_time.h"
20 #include "base/bitmap.h"
21 #include "base/counter.h"
22 #include "base/hashtable.h"
23 #include "base/string.h"
24 #include "memory/malloc.h"
25 #include "memory/shm_file.h"
26 namespace base {
27
28 class PersistSimpleMalloc : public MallocApi {
29 public:
30 PersistSimpleMalloc(
31 const std::string& filename, int64 memory_size, int slab_size)
32 : memory_size_(memory_size), slab_size_(slab_size) {
33 bool file_exists = base::file_util::PathExists(filename);
34
35 meta_data_memory_size_ = memory_size_ / slab_size_ * sizeof(uint64_t);
36
37 shm_file_ = ShmFile::New(ShmFile::ConfigForMedium(
38 "DRAM", filename, memory_size + meta_data_memory_size_));
39 if (!shm_file_) {
40 file_exists = false;
41 CHECK(base::file_util::Delete(filename, false));
42 shm_file_ = ShmFile::New(ShmFile::ConfigForMedium(
43 "DRAM", filename, memory_size + meta_data_memory_size_));
44 CHECK(shm_file_) << filename << " " << memory_size;
45 }
46 Initialize();
47 meta_data_block_ = (uint64_t*)shm_file_->Data();
48 data_ = shm_file_->Data() + meta_data_memory_size_;
49 if (!file_exists) {
50 LOG(INFO) << "PersistSimpleMalloc: first initialization.";
51 } else {
52 LOG(INFO) << "PersistSimpleMalloc: Recovery from shutdown.";
53 }
54 LOG(WARNING) << "skip the first allocate";
55 allocated_.fetch_add(slab_size_);
56 }
57
58 char* New(int malloc_size) override {
59 CHECK_EQ(malloc_size, slab_size_) << "Simple Malloc, size inconsistent";
60 auto offset = allocated_.fetch_add(malloc_size);
61 nr_malloc_++;
62 // TODO allocate
63 if (offset + slab_size_ >= memory_size_) {
64 offset = 0;
65 allocated_ = 0;
66 }
67 CHECK_LE(offset + slab_size_, memory_size_);
68 meta_data_block_[offset / slab_size_] = malloc_size;
69 return data_ + offset;
70 }
71
72 bool Free(void* memory_data) override {
73 LOG(FATAL) << "not implement";
74 return false;
75 }
76
77 void AddMallocs4Recovery(int64_t shm_offset) override {
78 LOG(FATAL) << "not implement";
79 }
80
81 std::string GetInfo() const override {
82 std::string info;
83 info.append(base::StringPrintf(
84 "allocated/memory_size/util: %ld/%ld/%ld\n",
85 allocated_.load(),
86 memory_size_,
87 allocated_.load() * 100 / memory_size_));
88 return info;
89 }
90
91 uint64_t total_malloc() const override { return nr_malloc_; }
92
93 bool Healthy() const override { return true; }
94
95 void GetMallocsAppend(std::vector<char*>* mallocs_data) const override {
96 LOG(FATAL) << "not implement";
97 }
98 void GetMallocsAppend(std::vector<int64>* mallocs_offset) const override {
99 LOG(FATAL) << "not implement";
100 }
101 void Initialize() override { allocated_.store(0); }
102
103 char* GetMallocData(int64 offset) const override { return data_ + offset; }
104
105 int GetMallocSize(int64 offset) const override {
106 return meta_data_block_[offset / slab_size_];
107 }
108
109 int64 GetMallocOffset(const char* data) const override {
110 int64 offset = data - data_;
111 CHECK(data_ <= data);
112 CHECK(data + slab_size_ < data_ + memory_size_);
113 return offset;
114 }
115 int GetMallocSize(const char* data) const override {
116 return GetMallocSize(GetMallocOffset(data));
117 }
118
119 private:
120 std::unique_ptr<ShmFile> shm_file_;
121 std::atomic<uint64_t> allocated_{0};
122 std::atomic<uint64_t> nr_malloc_{0};
123 char* data_;
124 uint64_t* meta_data_block_;
125 int64 memory_size_;
126 int64 meta_data_memory_size_;
127 int64 slab_size_;
128 DISALLOW_COPY_AND_ASSIGN(PersistSimpleMalloc);
129 };
130
131 template <bool PERFECT_FIT_MOD = true>
132 class PersistMemoryPool : public MallocApi {
133 static constexpr uint64_t kChunkSize = 2 * 1024 * 1024LL;
134 static constexpr int kMetaDataSize = 8;
135
136 class Chunk {
137 public:
138 Chunk(char* chunk_start, uint64 chunk_id) {
139 header_ = (ChunkHeader*)chunk_start;
140 data_ = nullptr;
141 chunk_id_ = chunk_id;
142 allocated_entries_ = 0;
143 }
144
145 void Initialize() { header_->is_used = false; }
146
147 void Use(int slab_size) {
148 // header(nr_entries) + nr_entries * slab_size < kChunkSize
149 int nr_entries = kChunkSize / slab_size;
150 nr_entries -= (nr_entries % 64);
151
152 while (ChunkHeader::HeaderSize(nr_entries) + nr_entries * slab_size >
153 kChunkSize) {
154 nr_entries -= 64;
155 }
156
157 CHECK_GT(nr_entries, 0);
158
159 header_->Initialize(slab_size, nr_entries);
160 data_ = (char*)header_ + header_->HeaderSize();
161 // TODO: flush the meta data
162 allocated_entries_ = 0;
163 is_recovered = true;
164 }
165
166 bool ValidPointerRange(void* start, void* end) {
167 if ((char*)end - (char*)start != header_->slab_size_) {
168 return false;
169 }
170 if (start < data_ ||
171 end > data_ + header_->slab_size_ * header_->nr_entries_) {
172 return false;
173 }
174 return true;
175 }
176
177 void Recovery() {
178 if (UNLIKELY(!is_recovered.load())) {
179 allocated_entries_ = header_->bitmap_->NumberOfOnes();
180 data_ = (char*)header_ + header_->HeaderSize();
181 is_recovered = true;
182 }
183 }
184
185 bool IsChunkUsed() const { return header_->is_used; }
186
187 bool Full() const {
188 #ifdef DEBUG
189 CHECK_EQ(header_->bitmap_->FirstZeroPos() == -1,
190 allocated_entries_ == MaxEntryNumber());
191 #endif
192 return allocated_entries_ == MaxEntryNumber();
193 // return header_->bitmap_->FirstZeroPos() == -1;
194 }
195
196 char* Malloc() {
197 base::AutoLock lock(lock_);
198 Recovery();
199 int entry_id = header_->bitmap_->SetZeroPos();
200 if (entry_id == -1)
201 return nullptr;
202 allocated_entries_++;
203 return Entry(entry_id);
204 }
205
206 void Free(void* memory_data) {
207 base::AutoLock lock(lock_);
208 Recovery();
209 #ifdef DEBUG
210 CHECK_LE(slab_size_ + (char*)memory_data, data_ + kChunkSize);
211 CHECK_GE((char*)memory_data, data_);
212 CHECK(header_->bitmap_->Get(EntryId(memory_data)) == false);
213 #endif
214 allocated_entries_--;
215 header_->bitmap_->Clear(EntryId(memory_data));
216 }
217
218 int SlabSize() const { return header_->slab_size_; }
219
220 int AllocatedEntryNumber() const { return allocated_entries_; }
221
222 std::vector<int> GetMallocedIds() const {
223 std::vector<int> return_id;
224 for (int i = 0; i < MaxEntryNumber(); i++)
225 if (header_->bitmap_->Get(i)) {
226 return_id.push_back(i);
227 }
228 CHECK_EQ(return_id.size(), allocated_entries_)
229 << fmt::format("chunk id is {}", chunk_id_);
230 return return_id;
231 }
232
233 std::vector<char*> GetMallocedData() const {
234 std::vector<char*> return_data;
235 for (int i = 0; i < MaxEntryNumber(); i++)
236 if (header_->bitmap_->Get(i)) {
237 return_data.push_back(Entry(i));
238 }
239 CHECK_EQ(return_data.size(), allocated_entries_)
240 << fmt::format("chunk id is {}", chunk_id_);
241 return return_data;
242 }
243
244 private:
245 ALWAYS_INLINE int MaxEntryNumber() const { return header_->nr_entries_; }
246
247 int EntryId(void* memory_data) {
248 #ifdef DEBUG
249 CHECK_EQ((memory_data - data_) % SlabSize(), 0);
250 #endif
251 return ((char*)memory_data - data_) / SlabSize();
252 }
253
254 char* Entry(int entry_id) const {
255 #ifdef DEBUG
256 CHECK_GE(entry_id, 0);
257 CHECK_LT(entry_id, MaxEntryNumber());
258 #endif
259 return data_ + entry_id * SlabSize();
260 }
261
262 struct ChunkHeader {
263 void Initialize(int slab_size, int nr_entries) {
264 slab_size_ = slab_size;
265 nr_entries_ = nr_entries;
266 new (bitmap_) BitMap(nr_entries);
267 bitmap_->Clear();
268 is_used = true;
269 }
270
271 bool is_used = false;
272 int slab_size_;
273 int nr_entries_;
274 bool allocated;
275 base::BitMap bitmap_[0]; // bitmap_ must be always in the tail
276
277 size_t HeaderSize() const {
278 return sizeof(ChunkHeader) + base::BitMap::MemorySize(nr_entries_);
279 }
280 static size_t HeaderSize(int nr_entires) {
281 return sizeof(ChunkHeader) + base::BitMap::MemorySize(nr_entires);
282 }
283 DISALLOW_COPY_AND_ASSIGN(ChunkHeader);
284 };
285 ChunkHeader* header_;
286 char* data_;
287 std::atomic_bool is_recovered = false;
288 int allocated_entries_;
289 uint64 chunk_id_;
290 base::Lock lock_;
291 DISALLOW_COPY_AND_ASSIGN(Chunk);
292 };
293
294 public:
295 PersistMemoryPool(const std::string& filename,
296 int64 memory_size,
297 const std::vector<int>& slab_sizes)
298 : allocated_slab_sizes_(slab_sizes.begin(), slab_sizes.end()) {
299 bool file_exists = base::file_util::PathExists(filename);
300 shm_file_ =
301 ShmFile::New(ShmFile::ConfigForMedium("DRAM", filename, memory_size));
302 if (!shm_file_) {
303 file_exists = false;
304 CHECK(base::file_util::Delete(filename, false));
305 shm_file_ =
306 ShmFile::New(ShmFile::ConfigForMedium("DRAM", filename, memory_size));
307 CHECK(shm_file_) << filename << " " << memory_size;
308 }
309
310 memory_size -= memory_size % kChunkSize;
311 nr_chunks_ = memory_size / kChunkSize;
312
313 CHECK_GE(nr_chunks_, slab_sizes.size());
314
315 for (int64 i = 0; i < nr_chunks_; i++) {
316 chunks_.push_back(new Chunk(shm_file_->Data() + i * kChunkSize, i));
317 }
318
319 for (auto slab : slab_sizes) {
320 size_to_chunks_[slab] = new std::deque<Chunk*>();
321 }
322
323 if (!file_exists || !Valid()) {
324 LOG(INFO) << "PersistMemoryPool: first initialization.";
325 for (int i = 0; i < nr_chunks_; i++) {
326 chunks_[i]->Initialize();
327 free_chunks_.push_back(chunks_[i]);
328 }
329 return;
330 }
331 LOG(INFO) << "PersistMemoryPool: Recovery from shutdown.";
332 for (int i = 0; i < nr_chunks_; i++) {
333 auto each = chunks_[i];
334 if (each->IsChunkUsed()) {
335 auto it = size_to_chunks_.find(each->SlabSize());
336 size_to_chunks_[each->SlabSize()]->push_back(each);
337 each->Recovery();
338 LOG(INFO) << "each->AllocatedEntryNumber() = "
339 << each->AllocatedEntryNumber();
340 total_malloc_ += each->AllocatedEntryNumber();
341 } else {
342 free_chunks_.push_back(each);
343 }
344 }
345 }
346
347 bool Valid() const { return true; }
348
349 char* New(int memory_size) override {
350 if (PERFECT_FIT_MOD) {
351 #ifdef DEBUG
352 CHECK(allocated_slab_sizes_.find(memory_size) !=
353 allocated_slab_sizes_.end());
354 #endif
355 return NewInternal(memory_size);
356 } else {
357 auto iter =
358 allocated_slab_sizes_.lower_bound(kMetaDataSize + memory_size);
359 CHECK(iter != allocated_slab_sizes_.end());
360 char* ptr = NewInternal(*iter);
361 *(int*)ptr = memory_size;
362 return ptr + kMetaDataSize;
363 }
364 }
365
366 char* NewInternal(int slab_size) {
367 // TODO: fine grained lock
368 base::AutoLock lock(lock_);
369 void* return_ptr = nullptr;
370
371 Chunk* last_used_chunk = nullptr;
372 auto iter = size_to_last_used_chunk_.find(slab_size);
373 if (iter != size_to_last_used_chunk_.end())
374 last_used_chunk = iter->second;
375
376 if (last_used_chunk && !last_used_chunk->Full()) {
377 return_ptr = last_used_chunk->Malloc();
378 CHECK(return_ptr);
379 } else if (!free_chunks_.empty()) {
380 auto front = free_chunks_.front();
381 free_chunks_.pop_front();
382 front->Use(slab_size);
383 auto chunks = size_to_chunks_[slab_size];
384 chunks->push_back(front);
385 size_to_last_used_chunk_[slab_size] = front;
386 return_ptr = front->Malloc();
387 } else {
388 auto chunks = size_to_chunks_[slab_size];
389 for (auto chunk : *chunks) {
390 if (chunk->Full())
391 continue;
392 return_ptr = chunk->Malloc();
393 goto final;
394 }
395 LOG(FATAL) << fmt::format(
396 "Persist Memory Pool OOM, total_malloc={}", total_malloc_.load());
397 }
398 final:
399 CHECK(return_ptr);
400 total_malloc_++;
401 return (char*)return_ptr;
402 }
403
404 bool Free(void* memory_data) override {
405 if (!PERFECT_FIT_MOD)
406 memory_data = (char*)memory_data - kMetaDataSize;
407
408 int chunk_id = GetChunkID(memory_data);
409 if (0 <= chunk_id && chunk_id < chunks_.size()) {
410 chunks_[chunk_id]->Free(memory_data);
411 total_malloc_--;
412 return true;
413 } else {
414 return false;
415 }
416 }
417 void GetMallocsAppend(std::vector<char*>* mallocs_data) const override {
418 for (auto chunk : chunks_) {
419 auto chunk_data = chunk->GetMallocedData();
420 for (char* each : chunk_data) {
421 if (PERFECT_FIT_MOD)
422 mallocs_data->push_back(each);
423 else
424 mallocs_data->push_back((char*)each + kMetaDataSize);
425 }
426 }
427 }
428 void GetMallocsAppend(std::vector<int64>* mallocs_offset) const override {
429 std::vector<char*> mallocs_data;
430 GetMallocsAppend(&mallocs_data);
431 for (auto each : mallocs_data) {
432 mallocs_offset->push_back(each - shm_file_->Data());
433 }
434 }
435 void Initialize() override {}
436
437 std::string GetInfo() const override {
438 uint64 malloced_bytes = 0;
439 uint64 malloced_chunk = 0;
440 for (auto each : chunks_) {
441 if (each->IsChunkUsed()) {
442 malloced_bytes += each->AllocatedEntryNumber() * each->SlabSize();
443 malloced_chunk++;
444 }
445 }
446 return base::SFormat(
447 "[PersistMemoryPool] "
448 "Use {} of {} chunks, Util {} %",
449 malloced_chunk,
450 chunks_.size(),
451 100 * malloced_bytes / (kChunkSize * chunks_.size()));
452 }
453
454 char* GetMallocData(int64 offset) const override {
455 return shm_file_->Data() + offset;
456 }
457
458 int GetMallocSize(int64 offset) const override {
459 return GetMallocSize(GetMallocData(offset));
460 }
461
462 int64 GetMallocOffset(const char* data) const override {
463 auto ret = data - shm_file_->Data();
464 // see cache.SetShmMallocOffset
465 CHECK_EQ((ret >> 3) << 3, ret);
466 return ret;
467 }
468
469 int GetMallocSize(const char* data) const override {
470 if (PERFECT_FIT_MOD)
471 return chunks_[GetChunkID(data)]->SlabSize();
472 else
473 return *(int*)(data - kMetaDataSize);
474 }
475
476 uint64_t total_malloc() const override { return total_malloc_; }
477
478 ~PersistMemoryPool() override {
479 for (auto p : chunks_) {
480 delete p;
481 }
482 for (auto [size, q] : size_to_chunks_) {
483 delete q;
484 }
485 }
486
487 bool Healthy() const { return true; }
488 void AddMallocs4Recovery(int64_t shm_offset) {
489 RECSTORE_LOG_EVERY_MS(ERROR, 1000) << "AddMallocs4Recovery not implement";
490 }
491
492 private:
493 int64 GetChunkID(void* data) const { return GetChunkID((const char*)data); }
494 int64 GetChunkID(const char* data) const {
495 return GetChunkID(data - shm_file_->Data());
496 }
497
498 int64 GetChunkID(int64 offset) const { return offset / kChunkSize; }
499
500 private:
501 std::unique_ptr<ShmFile> shm_file_;
502 int64 nr_chunks_;
503 std::vector<Chunk*> chunks_;
504 std::deque<Chunk*> free_chunks_;
505
506 std::unordered_map<int, std::deque<Chunk*>*> size_to_chunks_;
507 std::unordered_map<int, Chunk*> size_to_last_used_chunk_;
508
509 std::set<int> allocated_slab_sizes_;
510
511 std::atomic<int64> total_malloc_ = 0;
512
513 base::Lock lock_;
514 // different slab
515 };
516
517 // Factory wrapper for DramValueStore; matches PetKV PersistMemoryPool<false>
518 // slabs.
519 class PersistMemoryPoolMalloc : public PersistMemoryPool<false> {
520 public:
521 PersistMemoryPoolMalloc(const std::string& filename,
522 int64 memory_size,
523 const std::string& /*medium*/)
524 : PersistMemoryPool<false>(
525 filename,
526 memory_size,
527 {8 + 32, 8 + 64, 8 + 128, 8 + 512, 8 + 1024}) {}
528 };
529
530 class PersistLoopShmMalloc : public MallocApi {
531 public:
532 static const int max_fast_list_type = 32;
533 static const int max_fast_list_num = 1 << 20;
534 // filename: 文件名, 内存大小
535 PersistLoopShmMalloc(const std::string& filename,
536 int64 memory_size,
537 std::string medium = "DRAM");
538 // 如果分配内存不是固定的几种大小,且我们需要利用循环首次适应的 LRU 特性,
539 // 关闭这个功能
540 void DisableFastMalloc() { enable_fast_malloc_ = false; }
541 char* New(int memory_size);
542 bool Free(void* memory_data);
543
544 bool load_success() const { return load_success_; }
545 void GetMallocsAppend(std::vector<char*>* mallocs_data) const;
546 void GetMallocsAppend(std::vector<int64>* mallocs_offset) const;
547 std::string GetInfo() const {
548 std::string info;
549 info.append(base::StringPrintf(
550 "used/healthy/total block: %ld/%ld/%ld\n",
551 total_used_,
552 healthy_used_,
553 block_num_));
554 info.append(total_fast_malloc_.Display() + "\n");
555 info.append(total_loop_malloc_.Display() + "\n");
556 return info;
557 }
558 564 void Initialize() {
559 564 total_used_ = 0;
560 564 total_malloc_ = 0;
561 564 last_malloc_block_ = 0;
562 564 memset(used_bits_, 0, block_num_ >> 3);
563 564 clflushopt_range(used_bits_, block_num_ >> 3);
564 564 }
565 // 一共使用了多少 8 字节的 block
566 int64 total_used() const { return total_used_; }
567 // 一共分配了多少块内存, 和 GetUsedBlockAppend 对应
568 uint64 total_malloc() const { return total_malloc_; }
569 bool Healthy() const { return total_used_ <= healthy_used_; }
570 int64 DataBaseOffset() const { return data_ - shm_file_->Data(); }
571 char* BackingData() const override { return data_; }
572 int64 BackingSize() const override { return block_num_ * 8L; }
573 244938 char* GetMallocData(int64 offset) const {
574
3/6
✓ Branch 0 taken 244938 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 244938 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 244938 times.
244938 if (offset < 8 || (offset & 7) != 0 || offset > block_num_ * 8L)
575 return NULL;
576 244938 return data_ + offset;
577 }
578 147854 int GetMallocSize(int64 offset) const {
579
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 147854 times.
147854 if (offset == block_num_ * 8L)
580 return 0;
581
3/6
✓ Branch 0 taken 147854 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 147854 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 147854 times.
147854 if (offset < 8 || (offset & 7) != 0 || offset > block_num_ * 8L)
582 return -1;
583 147854 return Block()[BlockIndex(offset) - 1];
584 }
585 164030 int64 GetMallocOffset(const char* data) const {
586 164030 int64 offset = data - data_;
587
3/6
✓ Branch 0 taken 164030 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 164030 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 164030 times.
164030 if (offset < 8 || (offset & 7) != 0 || offset > block_num_ * 8L)
588 return -1;
589 164030 return offset;
590 }
591 int GetMallocSize(const char* data) const {
592 int64 offset = GetMallocOffset(data);
593 if (offset == block_num_ * 8L)
594 return 0;
595 if (offset < 0)
596 return -1;
597 return Block()[BlockIndex(offset) - 1];
598 }
599
600 void AddMallocs4Recovery(int64_t shm_offset);
601
602 private:
603 216178 static int BlockNum(int memory_size) { return (memory_size + 7) >> 3; }
604 200002 static int64 BlockIndex(int64 offset) { return offset >> 3; }
605 bool UnusedMemoryValid(int64 block_index) const;
606 bool MemoryValid() const;
607 5806260 bool Used(int64 index) const {
608 5806260 return (used_bits_[index >> 6] & (1ul << (index & 63))) != 0;
609 }
610 200002 const uint64* Block() const { return reinterpret_cast<const uint64*>(data_); }
611 275912 uint64* Block() { return reinterpret_cast<uint64*>(data_); }
612 1788648 void UseBlock(int64 index) { used_bits_[index >> 6] |= 1ul << (index & 63); }
613 886020 void FreeBlock(int64 index) {
614 886020 used_bits_[index >> 6] &= ~(1ul << (index & 63));
615 886020 }
616 std::unique_ptr<ShmFile> shm_file_;
617 base::Lock lock_;
618 uint64* used_bits_;
619 char* data_;
620 char* start_address_;
621 int64 memory_size_;
622 int64 block_num_;
623 int64 last_malloc_block_;
624 int64 total_used_;
625 int64 total_malloc_;
626 int64 healthy_used_;
627 bool load_success_;
628 bool enable_fast_malloc_ = true;
629 StdAutoDeleteHash<std::deque<int64>> fast_malloc_lists_;
630 Counter total_fast_malloc_{"total_fast_malloc"};
631 Counter total_loop_malloc_{"total_loop_malloc"};
632
633 DISALLOW_COPY_AND_ASSIGN(PersistLoopShmMalloc);
634 };
635 FACTORY_REGISTER(MallocApi,
636 PersistLoopShmMalloc, // 注册名(通常同类名)
637 PersistLoopShmMalloc, // 具体类型
638 const std::string&,
639 int64,
640 const std::string&);
641 FACTORY_REGISTER(MallocApi,
642 PERSIST_LOOP_SLAB,
643 PersistLoopShmMalloc,
644 const std::string&,
645 int64,
646 const std::string&);
647 FACTORY_REGISTER(MallocApi,
648 PERSIST_MEMORY_POOL,
649 PersistMemoryPoolMalloc,
650 const std::string&,
651 int64,
652 const std::string&);
653 } // namespace base
654