Arena.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. * Copyright 2012-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #pragma once
  17. #define FOLLY_ARENA_H_
  18. #include <cassert>
  19. #include <limits>
  20. #include <stdexcept>
  21. #include <utility>
  22. #include <boost/intrusive/slist.hpp>
  23. #include <folly/Conv.h>
  24. #include <folly/Likely.h>
  25. #include <folly/Memory.h>
  26. #include <folly/lang/Align.h>
  27. #include <folly/lang/Exception.h>
  28. #include <folly/memory/Malloc.h>
  29. namespace folly {
  30. /**
  31. * Simple arena: allocate memory which gets freed when the arena gets
  32. * destroyed.
  33. *
  34. * The arena itself allocates memory using a custom allocator which conforms
  35. * to the C++ concept Allocator.
  36. *
  37. * http://en.cppreference.com/w/cpp/concept/Allocator
  38. *
  39. * You may also specialize ArenaAllocatorTraits for your allocator type to
  40. * provide:
  41. *
  42. * size_t goodSize(const Allocator& alloc, size_t size) const;
  43. * Return a size (>= the provided size) that is considered "good" for your
  44. * allocator (for example, if your allocator allocates memory in 4MB
  45. * chunks, size should be rounded up to 4MB). The provided value is
  46. * guaranteed to be rounded up to a multiple of the maximum alignment
  47. * required on your system; the returned value must be also.
  48. *
  49. * An implementation that uses malloc() / free() is defined below, see SysArena.
  50. */
  51. template <class Alloc>
  52. struct ArenaAllocatorTraits;
  53. template <class Alloc>
  54. class Arena {
  55. public:
  56. explicit Arena(
  57. const Alloc& alloc,
  58. size_t minBlockSize = kDefaultMinBlockSize,
  59. size_t sizeLimit = kNoSizeLimit,
  60. size_t maxAlign = kDefaultMaxAlign)
  61. : allocAndSize_(alloc, minBlockSize),
  62. ptr_(nullptr),
  63. end_(nullptr),
  64. totalAllocatedSize_(0),
  65. bytesUsed_(0),
  66. sizeLimit_(sizeLimit),
  67. maxAlign_(maxAlign) {
  68. if ((maxAlign_ & (maxAlign_ - 1)) || maxAlign_ > alignof(Block)) {
  69. throw_exception(std::invalid_argument(
  70. folly::to<std::string>("Invalid maxAlign: ", maxAlign_)));
  71. }
  72. }
  73. ~Arena();
  74. void* allocate(size_t size) {
  75. size = roundUp(size);
  76. bytesUsed_ += size;
  77. assert(ptr_ <= end_);
  78. if (LIKELY((size_t)(end_ - ptr_) >= size)) {
  79. // Fast path: there's enough room in the current block
  80. char* r = ptr_;
  81. ptr_ += size;
  82. assert(isAligned(r));
  83. return r;
  84. }
  85. // Not enough room in the current block
  86. void* r = allocateSlow(size);
  87. assert(isAligned(r));
  88. return r;
  89. }
  90. void deallocate(void* /* p */, size_t = 0) {
  91. // Deallocate? Never!
  92. }
  93. // Transfer ownership of all memory allocated from "other" to "this".
  94. void merge(Arena&& other);
  95. // Gets the total memory used by the arena
  96. size_t totalSize() const {
  97. return totalAllocatedSize_ + sizeof(Arena);
  98. }
  99. // Gets the total number of "used" bytes, i.e. bytes that the arena users
  100. // allocated via the calls to `allocate`. Doesn't include fragmentation, e.g.
  101. // if block size is 4KB and you allocate 2 objects of 3KB in size,
  102. // `bytesUsed()` will be 6KB, while `totalSize()` will be 8KB+.
  103. size_t bytesUsed() const {
  104. return bytesUsed_;
  105. }
  106. // not copyable or movable
  107. Arena(const Arena&) = delete;
  108. Arena& operator=(const Arena&) = delete;
  109. Arena(Arena&&) = delete;
  110. Arena& operator=(Arena&&) = delete;
  111. private:
  112. struct Block;
  113. typedef boost::intrusive::slist_member_hook<boost::intrusive::tag<Arena>>
  114. BlockLink;
  115. struct alignas(max_align_v) Block {
  116. BlockLink link;
  117. // Allocate a block with at least size bytes of storage.
  118. // If allowSlack is true, allocate more than size bytes if convenient
  119. // (via ArenaAllocatorTraits::goodSize()) as we'll try to pack small
  120. // allocations in this block.
  121. static std::pair<Block*, size_t>
  122. allocate(Alloc& alloc, size_t size, bool allowSlack);
  123. void deallocate(Alloc& alloc);
  124. char* start() {
  125. return reinterpret_cast<char*>(this + 1);
  126. }
  127. private:
  128. Block() = default;
  129. ~Block() = default;
  130. };
  131. public:
  132. static constexpr size_t kDefaultMinBlockSize = 4096 - sizeof(Block);
  133. static constexpr size_t kNoSizeLimit = 0;
  134. static constexpr size_t kDefaultMaxAlign = alignof(Block);
  135. static constexpr size_t kBlockOverhead = sizeof(Block);
  136. private:
  137. bool isAligned(uintptr_t address) const {
  138. return (address & (maxAlign_ - 1)) == 0;
  139. }
  140. bool isAligned(void* p) const {
  141. return isAligned(reinterpret_cast<uintptr_t>(p));
  142. }
  143. // Round up size so it's properly aligned
  144. size_t roundUp(size_t size) const {
  145. return (size + maxAlign_ - 1) & ~(maxAlign_ - 1);
  146. }
  147. // cache_last<true> makes the list keep a pointer to the last element, so we
  148. // have push_back() and constant time splice_after()
  149. typedef boost::intrusive::slist<
  150. Block,
  151. boost::intrusive::member_hook<Block, BlockLink, &Block::link>,
  152. boost::intrusive::constant_time_size<false>,
  153. boost::intrusive::cache_last<true>>
  154. BlockList;
  155. void* allocateSlow(size_t size);
  156. // Empty member optimization: package Alloc with a non-empty member
  157. // in case Alloc is empty (as it is in the case of SysAllocator).
  158. struct AllocAndSize : public Alloc {
  159. explicit AllocAndSize(const Alloc& a, size_t s)
  160. : Alloc(a), minBlockSize(s) {}
  161. size_t minBlockSize;
  162. };
  163. size_t minBlockSize() const {
  164. return allocAndSize_.minBlockSize;
  165. }
  166. Alloc& alloc() {
  167. return allocAndSize_;
  168. }
  169. const Alloc& alloc() const {
  170. return allocAndSize_;
  171. }
  172. AllocAndSize allocAndSize_;
  173. BlockList blocks_;
  174. char* ptr_;
  175. char* end_;
  176. size_t totalAllocatedSize_;
  177. size_t bytesUsed_;
  178. const size_t sizeLimit_;
  179. const size_t maxAlign_;
  180. };
  181. template <class Alloc>
  182. struct AllocatorHasTrivialDeallocate<Arena<Alloc>> : std::true_type {};
  183. /**
  184. * By default, don't pad the given size.
  185. */
  186. template <class Alloc>
  187. struct ArenaAllocatorTraits {
  188. static size_t goodSize(const Alloc& /* alloc */, size_t size) {
  189. return size;
  190. }
  191. };
  192. template <>
  193. struct ArenaAllocatorTraits<SysAllocator<void>> {
  194. static size_t goodSize(const SysAllocator<void>& /* alloc */, size_t size) {
  195. return goodMallocSize(size);
  196. }
  197. };
  198. /**
  199. * Arena that uses the system allocator (malloc / free)
  200. */
  201. class SysArena : public Arena<SysAllocator<void>> {
  202. public:
  203. explicit SysArena(
  204. size_t minBlockSize = kDefaultMinBlockSize,
  205. size_t sizeLimit = kNoSizeLimit,
  206. size_t maxAlign = kDefaultMaxAlign)
  207. : Arena<SysAllocator<void>>({}, minBlockSize, sizeLimit, maxAlign) {}
  208. };
  209. template <>
  210. struct AllocatorHasTrivialDeallocate<SysArena> : std::true_type {};
  211. template <typename T, typename Alloc>
  212. using ArenaAllocator = CxxAllocatorAdaptor<T, Arena<Alloc>>;
  213. template <typename T>
  214. using SysArenaAllocator = ArenaAllocator<T, SysAllocator<void>>;
  215. } // namespace folly
  216. #include <folly/memory/Arena-inl.h>