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 |