libfoedus-core
FOEDUS Core Library
page_pool.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2015, Hewlett-Packard Development Company, LP.
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU General Public License as published by the Free
5  * Software Foundation; either version 2 of the License, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11  * more details. You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14  *
15  * HP designates this particular file as subject to the "Classpath" exception
16  * as provided by HP in the LICENSE.txt file that accompanied this code.
17  */
19 
20 #include <glog/logging.h>
21 
22 #include <algorithm>
23 #include <cstring>
24 #include <string>
25 
26 #include "foedus/engine.hpp"
31 
32 namespace foedus {
33 namespace memory {
35  uint32_t count = end - begin;
36  ASSERT_ND(size_ + count <= kMaxSize);
37  std::memcpy(chunk_ + size_, begin, count * sizeof(PagePoolOffset));
38  size_ += count;
39 }
40 void PagePoolOffsetChunk::move_to(PagePoolOffset* destination, uint32_t count) {
41  ASSERT_ND(size_ >= count);
42  std::memcpy(destination, chunk_ + (size_ - count), count * sizeof(PagePoolOffset));
43  // we move from the tail of this chunk. so, just decrementing the count is enough.
44  // no need to move the remainings back to the beginning
45  size_ -= count;
46 }
47 
48 void PagePoolOffsetDynamicChunk::move_to(PagePoolOffset* destination, uint32_t count) {
49  ASSERT_ND(size_ >= count);
50  std::memcpy(destination, chunk_, count * sizeof(PagePoolOffset));
51 
52  // Skip the consumed entries
53  chunk_ += count;
54  size_ -= count;
55 }
56 
59  OffsetAndEpoch dummy;
60  dummy.safe_epoch_ = threshold.value();
61  struct CompareEpoch {
62  bool operator() (const OffsetAndEpoch& left, const OffsetAndEpoch& right) {
63  return Epoch(left.safe_epoch_) < Epoch(right.safe_epoch_);
64  }
65  };
66  const OffsetAndEpoch* result = std::lower_bound(chunk_, chunk_ + size_, dummy, CompareEpoch());
67  ASSERT_ND(result);
68  ASSERT_ND(result - chunk_ <= size_);
69  return result - chunk_;
70 }
71 
72 void PagePoolOffsetAndEpochChunk::move_to(PagePoolOffset* destination, uint32_t count) {
73  ASSERT_ND(size_ >= count);
74  // we can't do memcpy. Just copy one by one
75  for (uint32_t i = 0; i < count; ++i) {
76  destination[i] = chunk_[i].offset_;
77  }
78  // Also, unlike PagePoolOffsetChunk, we copied from the head (we have to because epoch matters).
79  // So, we also have to move remainings to the beginning
80  if (size_ > count) {
81  std::memmove(chunk_, chunk_ + count, (size_ - count) * sizeof(OffsetAndEpoch));
82  }
83  size_ -= count;
85 }
86 
88  for (uint32_t i = 1; i < size_; ++i) {
89  ASSERT_ND(chunk_[i].offset_);
90  if (Epoch(chunk_[i - 1U].safe_epoch_) > Epoch(chunk_[i].safe_epoch_)) {
91  return false;
92  }
93  }
94  return true;
95 }
96 
97 
99  pimpl_ = new PagePoolPimpl();
100 }
102  PagePoolControlBlock* control_block,
103  void* memory,
104  uint64_t memory_size,
105  bool owns,
106  bool rigorous_page_boundary_check) {
107  pimpl_->attach(control_block, memory, memory_size, owns, rigorous_page_boundary_check);
108 }
110  delete pimpl_;
111  pimpl_ = nullptr;
112 }
113 
115 bool PagePool::is_initialized() const { return pimpl_->is_initialized(); }
117 uint64_t PagePool::get_memory_size() const { return pimpl_->memory_size_; }
118 PagePool::Stat PagePool::get_stat() const { return pimpl_->get_stat(); }
119 storage::Page* PagePool::get_base() const { return pimpl_->pool_base_; }
120 uint64_t PagePool::get_free_pool_capacity() const { return pimpl_->get_free_pool_capacity(); }
121 std::string PagePool::get_debug_pool_name() const { return pimpl_->get_debug_pool_name(); }
122 void PagePool::set_debug_pool_name(const std::string& name) { pimpl_->set_debug_pool_name(name); }
123 
125  return std::min<uint32_t>(1U << 12, pimpl_->free_pool_capacity_ / 8U);
126 }
127 
128 
129 ErrorCode PagePool::grab(uint32_t desired_grab_count, PagePoolOffsetChunk* chunk) {
130  return pimpl_->grab(desired_grab_count, chunk);
131 }
132 ErrorCode PagePool::grab_one(PagePoolOffset* offset) { return pimpl_->grab_one(offset); }
133 
134 void PagePool::release(uint32_t desired_release_count, PagePoolOffsetChunk *chunk) {
135  pimpl_->release(desired_release_count, chunk);
136 }
137 void PagePool::release(uint32_t desired_release_count, PagePoolOffsetDynamicChunk* chunk) {
138  pimpl_->release(desired_release_count, chunk);
139 }
140 void PagePool::release(uint32_t desired_release_count, PagePoolOffsetAndEpochChunk* chunk) {
141  pimpl_->release(desired_release_count, chunk);
142 }
143 
144 void PagePool::release_one(PagePoolOffset offset) { pimpl_->release_one(offset); }
145 
146 const LocalPageResolver& PagePool::get_resolver() const { return pimpl_->get_resolver(); }
147 
148 std::ostream& operator<<(std::ostream& o, const PagePool& v) {
149  o << v.pimpl_;
150  return o;
151 }
152 
154  : engine_(engine), numa_node_count_(engine->get_options().thread_.group_count_) {
155  std::memset(chunks_, 0, sizeof(ChunkPtr) * 256);
156  for (thread::ThreadGroupId i = 0; i < numa_node_count_; ++i) {
157  chunks_[i] = new PagePoolOffsetChunk;
158  }
159 }
160 
162  release_all();
163  for (thread::ThreadGroupId i = 0; i < numa_node_count_; ++i) {
164  delete chunks_[i];
165  chunks_[i] = nullptr;
166  }
167 }
168 
170  ASSERT_ND(numa_node < numa_node_count_);
171  if (chunks_[numa_node]->full()) {
172  release_chunk(numa_node);
173  }
174  ASSERT_ND(!chunks_[numa_node]->full());
175  chunks_[numa_node]->push_back(offset);
176 }
177 
179  ASSERT_ND(numa_node < numa_node_count_);
180  if (chunks_[numa_node]->empty()) {
181  return;
182  }
183 
184  engine_->get_memory_manager()->get_node_memory(numa_node)->get_volatile_pool()->release(
185  chunks_[numa_node]->size(), chunks_[numa_node]);
186  ASSERT_ND(chunks_[numa_node]->empty());
187 }
188 
190  for (thread::ThreadGroupId i = 0; i < numa_node_count_; ++i) {
191  release_chunk(i);
192  }
193 }
195 : engine_(engine), numa_node_count_(engine->get_options().thread_.group_count_), current_node_(0) {
196 }
197 
199  release_all();
200 }
201 
203  if (chunk_.empty()) {
204  const thread::ThreadGroupId old = current_node_;
205  while (true) {
206  ++current_node_;
207  if (current_node_ >= numa_node_count_) {
208  current_node_ = 0;
209  }
210  PagePool* pool = engine_->get_memory_manager()->get_node_memory(current_node_)->
211  get_volatile_pool();
212  uint32_t grab_count = std::min<uint32_t>(
213  chunk_.capacity(),
215  ErrorCode code = pool->grab(grab_count, &chunk_);
216  if (code == kErrorCodeOk) {
217  break;
218  }
219 
220  if (code == kErrorCodeMemoryNoFreePages) {
221  LOG(WARNING) << "NUMA node-" << current_node_ << " has no more free pages."
222  << " trying another node..";
223  if (current_node_ == old) {
224  print_backtrace();
225  LOG(FATAL) << "No NUMA node has any free pages. This situation is so far "
226  " not handled. Aborting";
227  }
228  } else {
229  LOG(FATAL) << "Unexpected error code.. wtf error="
230  << code << "(" << get_error_name(code) << ")";
231  }
232  }
233  ASSERT_ND(!chunk_.empty());
234  }
235 
237  ret.set(current_node_, chunk_.pop_back());
238  return ret;
239 }
240 
242  if (chunk_.empty()) {
243  return;
244  }
245 
246  engine_->get_memory_manager()->get_node_memory(current_node_)->get_volatile_pool()->release(
247  chunk_.size(), &chunk_);
248  ASSERT_ND(chunk_.empty());
249 }
250 
251 
253 : engine_(engine), node_count_(engine->get_options().thread_.group_count_) {
254  chunks_ = new PagePoolOffsetChunk[node_count_];
255  for (uint16_t node = 0; node < node_count_; ++node) {
256  ASSERT_ND(chunks_[node].empty());
257  }
258 }
259 
261  release_all();
262  delete[] chunks_;
263  chunks_ = nullptr;
264 }
265 
266 
268  ASSERT_ND(node < node_count_);
269  if (chunks_[node].empty()) {
270  PagePool* pool = engine_->get_memory_manager()->get_node_memory(node)->get_volatile_pool();
271  uint32_t grab_count = std::min<uint32_t>(
272  chunks_[node].capacity(),
274  ErrorCode code = pool->grab(grab_count, chunks_ + node);
275  if (code == kErrorCodeMemoryNoFreePages) {
276  LOG(FATAL) << "NUMA node " << static_cast<int>(node) << " has no free pages. This situation "
277  " is so far not handled in DivvyupPageGrabBatch. Aborting";
278  } else if (code != kErrorCodeOk) {
279  LOG(FATAL) << "Unexpected error code.. wtf error="
280  << code << "(" << get_error_name(code) << ")";
281  }
282  }
284  ret.set(node, chunks_[node].pop_back());
285  return ret;
286 }
287 
290  if (node_count_ == 1U) {
291  node = 0;
292  } else {
293  node = cur * node_count_ / total;
294  }
295  return grab(node);
296 }
297 
299  for (uint16_t node = 0; node < node_count_; ++node) {
300  if (chunks_[node].empty()) {
301  continue;
302  }
304  chunks_[node].size(), chunks_ + node);
305  ASSERT_ND(chunks_[node].empty());
306  }
307 }
308 } // namespace memory
309 } // namespace foedus
void attach(PagePoolControlBlock *control_block, void *memory, uint64_t memory_size, bool owns, bool rigorous_page_boundary_check)
Definition: page_pool.cpp:101
void attach(PagePoolControlBlock *control_block, void *memory, uint64_t memory_size, bool owns, bool rigorous_page_boundary_check)
void release_all()
Called at the end to return all batched pages to their pools.
Definition: page_pool.cpp:189
void move_to(PagePoolOffset *destination, uint32_t count)
Definition: page_pool.cpp:40
std::string get_debug_pool_name() const
Page pool for volatile read/write store (VolatilePage) and the read-only bufferpool (SnapshotPage)...
Definition: page_pool.hpp:173
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
ErrorCode grab_one(PagePoolOffset *offset)
Grab only one page.
Definition: page_pool.cpp:132
Stat get_stat() const
Definition: page_pool.cpp:118
uint32_t PagePoolOffset
Offset in PagePool that compactly represents the page address (unlike 8 bytes pointer).
Definition: memory_id.hpp:44
void set_debug_pool_name(const std::string &name)
void release_one(PagePoolOffset offset)
Returns only one page.
Definition: page_pool.cpp:144
Represents a pointer to a volatile page with modification count for preventing ABA.
Definition: storage_id.hpp:194
storage::VolatilePagePointer grab(thread::ThreadGroupId node)
Grabs an in-memory page in specified NUMA node.
Definition: page_pool.cpp:267
ErrorStack uninitialize() override final
Typical implementation of Initializable::uninitialize() that provides uninitialize-once semantics...
storage::Page * pool_base_
Just an auxiliary variable to the beginning of the pool.
Brings error stacktrace information as return value of functions.
Definition: error_stack.hpp:81
uint64_t free_pool_capacity_
Size of free_pool_.
Represents a time epoch.
Definition: epoch.hpp:61
void release_one(PagePoolOffset offset)
ErrorCode grab(uint32_t desired_grab_count, PagePoolOffsetChunk *chunk)
Adds the specified number of free pages to the chunk.
Definition: page_pool.cpp:129
ErrorCode grab(uint32_t desired_grab_count, PagePoolOffsetChunk *chunk)
Max number of pointers to pack.
Definition: page_pool.hpp:54
ErrorStack uninitialize() override
An idempotent method to release all resources of this object, if any.
Definition: page_pool.cpp:116
uint32_t get_recommended_pages_per_grab() const
Definition: page_pool.cpp:124
const char * get_error_name(ErrorCode code)
Returns the names of ErrorCode enum defined in error_code.xmacro.
Definition: error_code.hpp:108
PagePool::Stat get_stat() const
void release_chunk(thread::ThreadGroupId numa_node)
Return all batched pages in the given chunk to its pool.
Definition: page_pool.cpp:178
storage::Page * get_base() const
Definition: page_pool.cpp:119
ErrorStack initialize() override
Acquires resources in this object, usually called right after constructor.
Definition: page_pool.cpp:114
0 means no-error.
Definition: error_code.hpp:87
void move_to(PagePoolOffset *destination, uint32_t count)
Definition: page_pool.cpp:48
ErrorStack initialize() override final
Typical implementation of Initializable::initialize() that provides initialize-once semantics...
Pimpl object of PagePool.
Database engine object that holds all resources and provides APIs.
Definition: engine.hpp:109
void release(uint32_t desired_release_count, PagePoolOffsetChunk *chunk)
NumaNodeMemoryRef * get_node_memory(foedus::thread::ThreadGroupId group) const
Just a marker to denote that the memory region represents a data page.
Definition: page.hpp:334
ErrorCode grab_one(PagePoolOffset *offset)
void set(uint8_t numa_node, memory::PagePoolOffset offset)
Definition: storage_id.hpp:212
void release_all()
Called at the end to return all remaining pages to their pools.
Definition: page_pool.cpp:298
std::ostream & operator<<(std::ostream &o, const AlignedMemory &v)
0x0301 : "MEMORY : Not enough free volatile pages. Check the config of MemoryOptions" ...
Definition: error_code.hpp:142
void release(uint32_t desired_release_count, PagePoolOffsetChunk *chunk)
Returns the specified number of free pages from the chunk.
Definition: page_pool.cpp:134
Used to point to an already existing array.
Definition: page_pool.hpp:81
void release_all()
Called at the end to return all remaining pages to their pools.
Definition: page_pool.cpp:241
void move_to(PagePoolOffset *destination, uint32_t count)
Note that the destination is PagePoolOffset* because that's the only usecase.
Definition: page_pool.cpp:72
Shared data in PagePoolPimpl.
const LocalPageResolver & get_resolver() const
void push_back(PagePoolOffset pointer)
Definition: page_pool.hpp:68
storage::VolatilePagePointer grab()
Grabs an in-memory page in some NUMA node.
Definition: page_pool.cpp:202
To reduce the overhead of grabbing/releasing pages from pool, we pack this many pointers for each gra...
Definition: page_pool.hpp:47
void release(storage::VolatilePagePointer page_id)
Returns the given in-memory volatile page to appropriate NUMA node.
Definition: page_pool.hpp:292
Resolves an offset in local (same NUMA node) page pool to a pointer and vice versa.
std::string print_backtrace()
Prints out backtrace.
Definition: assert_nd.cpp:37
uint64_t get_memory_size() const
Definition: page_pool.cpp:117
bool is_initialized() const override
Returns whether the object has been already initialized or not.
Definition: page_pool.cpp:115
Used to store an epoch value with each entry in PagePoolOffsetChunk.
Definition: page_pool.hpp:108
const LocalPageResolver & get_resolver() const
Gives an object to resolve an offset in this page pool (thus local) to an actual pointer and vice ver...
Definition: page_pool.cpp:146
uint64_t get_free_pool_capacity() const
Definition: page_pool.cpp:120
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72
uint64_t get_free_pool_capacity() const
uint8_t ThreadGroupId
Typedef for an ID of ThreadGroup (NUMA node).
Definition: thread_id.hpp:38
uint32_t get_safe_offset_count(const Epoch &threshold) const
Returns the number of offsets (always from index-0) whose safe_epoch_ is strictly-before the given ep...
Definition: page_pool.cpp:57
memory::EngineMemory * get_memory_manager() const
See Memory Manager.
Definition: engine.cpp:50
ErrorCode
Enum of error codes defined in error_code.xmacro.
Definition: error_code.hpp:85
bool is_initialized() const override final
Returns whether the object has been already initialized or not.
std::string get_debug_pool_name() const
Definition: page_pool.cpp:121
uint64_t memory_size_
Byte size of this page pool.
storage::VolatilePagePointer grab_evenly(uint64_t cur, uint64_t total)
Grabs an in-memory page evenly and contiguously from each NUMA node.
Definition: page_pool.cpp:288
void set_debug_pool_name(const std::string &name)
Call this anytime after attach()
Definition: page_pool.cpp:122
EpochInteger value() const
Returns the raw integer representation.
Definition: epoch.hpp:102