// 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). // // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. // Arena is an implementation of Allocator class. For a request of small size, // it allocates a block with pre-defined block size. For a request of big // size, it uses malloc to directly get the requested size. #pragma once #ifndef OS_WIN #include #endif #include #include #include #include #include #include "util/allocator.h" #include "util/mutexlock.h" namespace rocksdb { class Arena : public Allocator { public: // No copying allowed Arena(const Arena&) = delete; void operator=(const Arena&) = delete; static const size_t kInlineSize = 2048; static const size_t kMinBlockSize; static const size_t kMaxBlockSize; // huge_page_size: if 0, don't use huge page TLB. If > 0 (should set to the // supported hugepage size of the system), block allocation will try huge // page TLB first. If allocation fails, will fall back to normal case. explicit Arena(size_t block_size = kMinBlockSize, AllocTracker* tracker = nullptr, size_t huge_page_size = 0); ~Arena(); char* Allocate(size_t bytes) override; // huge_page_size: if >0, will try to allocate from huage page TLB. // The argument will be the size of the page size for huge page TLB. Bytes // will be rounded up to multiple of the page size to allocate through mmap // anonymous option with huge page on. The extra space allocated will be // wasted. If allocation fails, will fall back to normal case. To enable it, // need to reserve huge pages for it to be allocated, like: // sysctl -w vm.nr_hugepages=20 // See linux doc Documentation/vm/hugetlbpage.txt for details. // huge page allocation can fail. In this case it will fail back to // normal cases. The messages will be logged to logger. So when calling with // huge_page_tlb_size > 0, we highly recommend a logger is passed in. // Otherwise, the error message will be printed out to stderr directly. char* AllocateAligned(size_t bytes, size_t huge_page_size = 0, Logger* logger = nullptr) override; // Returns an estimate of the total memory usage of data allocated // by the arena (exclude the space allocated but not yet used for future // allocations). size_t ApproximateMemoryUsage() const { return blocks_memory_ + blocks_.capacity() * sizeof(char*) - alloc_bytes_remaining_; } size_t MemoryAllocatedBytes() const { return blocks_memory_; } size_t AllocatedAndUnused() const { return alloc_bytes_remaining_; } // If an allocation is too big, we'll allocate an irregular block with the // same size of that allocation. size_t IrregularBlockNum() const { return irregular_block_num; } size_t BlockSize() const override { return kBlockSize; } bool IsInInlineBlock() const { return blocks_.empty(); } private: char inline_block_[kInlineSize] __attribute__((__aligned__(sizeof(void*)))); // Number of bytes allocated in one block const size_t kBlockSize; // Array of new[] allocated memory blocks typedef std::vector Blocks; Blocks blocks_; struct MmapInfo { void* addr_; size_t length_; MmapInfo(void* addr, size_t length) : addr_(addr), length_(length) {} }; std::vector huge_blocks_; size_t irregular_block_num = 0; // Stats for current active block. // For each block, we allocate aligned memory chucks from one end and // allocate unaligned memory chucks from the other end. Otherwise the // memory waste for alignment will be higher if we allocate both types of // memory from one direction. char* unaligned_alloc_ptr_ = nullptr; char* aligned_alloc_ptr_ = nullptr; // How many bytes left in currently active block? size_t alloc_bytes_remaining_ = 0; #ifdef MAP_HUGETLB size_t hugetlb_size_ = 0; #endif // MAP_HUGETLB char* AllocateFromHugePage(size_t bytes); char* AllocateFallback(size_t bytes, bool aligned); char* AllocateNewBlock(size_t block_bytes); // Bytes of memory in blocks allocated so far size_t blocks_memory_ = 0; AllocTracker* tracker_; }; inline char* Arena::Allocate(size_t bytes) { // The semantics of what to return are a bit messy if we allow // 0-byte allocations, so we disallow them here (we don't need // them for our internal use). assert(bytes > 0); if (bytes <= alloc_bytes_remaining_) { unaligned_alloc_ptr_ -= bytes; alloc_bytes_remaining_ -= bytes; return unaligned_alloc_ptr_; } return AllocateFallback(bytes, false /* unaligned */); } // check and adjust the block_size so that the return value is // 1. in the range of [kMinBlockSize, kMaxBlockSize]. // 2. the multiple of align unit. extern size_t OptimizeBlockSize(size_t block_size); } // namespace rocksdb