Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[mc] Remove content addressable page store
[simgrid.git] / src / mc / PageStore.cpp
1 /* Copyright (c) 2015. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include <unistd.h>
8 #include <string.h> // memcpy, memcp
9
10 #include <sys/mman.h>
11
12 #include <boost/foreach.hpp>
13
14 #include <xbt.h>
15
16 #include "PageStore.hpp"
17
18 #include "mc_mmu.h"
19
20 extern "C" {
21
22 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_page_snapshot, mc,
23                                 "Logging specific to mc_page_snapshot");
24
25 namespace simgrid {
26 namespace mc {
27
28 /** @brief Compte a hash for the given memory page
29  *
30  *  The page is used before inserting the page in the page store
31  *  in order to find duplicate of this page in the page store.
32  *
33  *  @param data Memory page
34  *  @return hash off the page
35  */
36 static inline  __attribute__ ((always_inline))
37 PageStore::hash_type mc_hash_page(const void* data)
38 {
39   const uint64_t* values = (const uint64_t*) data;
40   size_t n = xbt_pagesize / sizeof(uint64_t);
41
42   // This djb2:
43   uint64_t hash = 5381;
44   for (size_t i=0; i!=n; ++i) {
45     hash = ((hash << 5) + hash) + values[i];
46   }
47   return hash;
48 }
49
50 // ***** snapshot_page_manager
51
52 PageStore::PageStore(size_t size) :
53   memory_(NULL), capacity_(0), top_index_(0)
54 {
55   // Using mmap in order to be able to expand the region
56   // by relocating it somewhere else in the virtual memory
57   // space:
58   void * memory = ::mmap(NULL, size << xbt_pagebits, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, -1, 0);
59   if (memory==MAP_FAILED) {
60     xbt_die("Could not mmap initial snapshot pages.");
61   }
62
63   this->top_index_ = 0;
64   this->capacity_ = size;
65   this->memory_ = memory;
66   this->page_counts_.resize(size);
67 }
68
69 PageStore::~PageStore()
70 {
71   ::munmap(this->memory_, this->capacity_ << xbt_pagebits);
72 }
73
74 void PageStore::resize(size_t size)
75 {
76   size_t old_bytesize = this->capacity_ << xbt_pagebits;
77   size_t new_bytesize = size << xbt_pagebits;
78
79   // Expand the memory region by moving it into another
80   // virtual memory address if necessary:
81   void* new_memory = mremap(this->memory_, old_bytesize, new_bytesize, MREMAP_MAYMOVE);
82   if (new_memory == MAP_FAILED) {
83     xbt_die("Could not mremap snapshot pages.");
84   }
85
86   this->capacity_ = size;
87   this->memory_ = new_memory;
88   this->page_counts_.resize(size, 0);
89 }
90
91 /** Allocate a free page
92  *
93  *  @return index of the free page
94  */
95 size_t PageStore::alloc_page()
96 {
97   if (this->free_pages_.empty()) {
98
99     // Expand the region:
100     if (this->top_index_ == this->capacity_) {
101       // All the pages are allocated, we need add more pages:
102       this->resize(2 * this->capacity_);
103     }
104
105     // Use a page from the top:
106     return this->top_index_++;
107
108   } else {
109
110     // Use a page from free_pages_ (inside of the region):
111     size_t res = this->free_pages_[this->free_pages_.size() - 1];
112     this->free_pages_.pop_back();
113     return res;
114
115   }
116 }
117
118 void PageStore::remove_page(size_t pageno)
119 {
120   this->free_pages_.push_back(pageno);
121   const void* page = this->get_page(pageno);
122   hash_type hash = mc_hash_page(page);
123   this->hash_index_[hash].erase(pageno);
124 }
125
126 /** Store a page in memory */
127 size_t PageStore::store_page(void* page)
128 {
129   xbt_assert(top_index_ <= this->capacity_, "top_index is not consistent");
130
131   // First, we check if a page with the same content is already in the page
132   // store:
133   //  1. compute the hash of the page;
134   //  2. find pages with the same hash using `hash_index_`;
135   //  3. find a page with the same content.
136   hash_type hash = mc_hash_page(page);
137
138   // Try to find a duplicate in set of pages with the same hash:
139   page_set_type& page_set = this->hash_index_[hash];
140   BOOST_FOREACH (size_t pageno, page_set) {
141     const void* snapshot_page = this->get_page(pageno);
142     if (memcmp(page, snapshot_page, xbt_pagesize) == 0) {
143
144       // If a page with the same content is already in the page store it is
145       // reused and its reference count is incremented.
146       page_counts_[pageno]++;
147       return pageno;
148
149     }
150   }
151
152   // Otherwise, a new page is allocated in the page store and the content
153   // of the page is `memcpy()`-ed to this new page.
154   size_t pageno = alloc_page();
155   xbt_assert(this->page_counts_[pageno]==0, "Allocated page is already used");
156   void* snapshot_page = (void*) this->get_page(pageno);
157   memcpy(snapshot_page, page, xbt_pagesize);
158   page_set.insert(pageno);
159   page_counts_[pageno]++;
160   return pageno;
161 }
162
163 }
164 }
165
166 #ifdef SIMGRID_TEST
167
168 #include <string.h>
169 #include <stdlib.h>
170 #include <stdint.h>
171 #include <unistd.h>
172 #include <sys/mman.h>
173
174 #include <memory>
175
176 #include "mc/PageStore.hpp"
177
178 static int value = 0;
179
180 static void new_content(void* data, size_t size)
181 {
182   memset(data, ++value, size);
183 }
184
185 static void* getpage()
186 {
187   return mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
188 }
189
190 extern "C" {
191
192 XBT_TEST_SUITE("mc_page_store", "Page store");
193
194 XBT_TEST_UNIT("base", test_mc_page_store, "Test adding/removing pages in the store")
195 {
196   xbt_test_add("Init");
197   size_t pagesize = (size_t) getpagesize();
198   std::unique_ptr<simgrid::mc::PageStore> store
199     = std::unique_ptr<simgrid::mc::PageStore>(new simgrid::mc::PageStore(500));
200   void* data = getpage();
201   xbt_test_assert(store->size()==0, "Bad size");
202
203   xbt_test_add("Store the page once");
204   new_content(data, pagesize);
205   size_t pageno1 = store->store_page(data);
206   xbt_test_assert(store->get_ref(pageno1)==1, "Bad refcount");
207   const void* copy = store->get_page(pageno1);
208   xbt_test_assert(memcmp(data, copy, pagesize)==0, "Page data should be the same");
209   xbt_test_assert(store->size()==1, "Bad size");
210
211   xbt_test_add("Store the same page again");
212   size_t pageno2 = store->store_page(data);
213   xbt_test_assert(pageno1==pageno2, "Page should be the same");
214   xbt_test_assert(store->get_ref(pageno1)==2, "Bad refcount");
215   xbt_test_assert(store->size()==1, "Bad size");
216
217   xbt_test_add("Store a new page");
218   new_content(data, pagesize);
219   size_t pageno3 = store->store_page(data);
220   xbt_test_assert(pageno1 != pageno3, "New page should be different");
221   xbt_test_assert(store->size()==2, "Bad size");
222
223   xbt_test_add("Unref pages");
224   store->unref_page(pageno1);
225   xbt_assert(store->get_ref(pageno1)==1, "Bad refcount");
226   xbt_assert(store->size()==2, "Bad size");
227   store->unref_page(pageno2);
228   xbt_test_assert(store->size()==1, "Bad size");
229
230   xbt_test_add("Reallocate page");
231   new_content(data, pagesize);
232   size_t pageno4 = store->store_page(data);
233   xbt_test_assert(pageno1 == pageno4, "Page was not reused");
234   xbt_test_assert(store->get_ref(pageno4)==1, "Bad refcount");
235   xbt_test_assert(store->size()==2, "Bad size");
236 }
237
238 }
239
240 #endif /* SIMGRID_TEST */
241
242 }