@@ -200,8 +200,123 @@ namespace mongo {
200200 int bigHash ( size_t region ) {
201201 return hash ( region ) % BigHashSize;
202202 }
203+
204+ /* * This class is a thread-local cache of the pages we have seen */
205+ class PointerTable {
206+ public:
207+ /* * call this to check a page has been seen yet. */
208+ bool seen (size_t ptr) {
209+ resetIfNeeded ();
210+
211+ // A bucket contains 4 superpages each containing 16 contiguous pages
212+ // See below for a more detailed explanation of superpages
213+ size_t * bucket = _table[bucketFor (ptr)];
214+
215+ for (int i = 0 ; i < bucketSize; i++) {
216+ if (superpageOf (ptr) == superpageOf (bucket[i])) {
217+ if (haveSeenPage (bucket[i], ptr))
218+ return true ;
219+
220+ markPageSeen (bucket[i], ptr);
221+ return false ;
222+ }
223+ }
224+
225+ // superpage isn't in thread-local cache
226+ // slide bucket forward and add new superpage at front
227+ for (int i = bucketSize-1 ; i > 0 ; i--)
228+ bucket[i] = bucket[i-1 ];
229+
230+ bucket[0 ] = superpageOf (ptr);
231+ markPageSeen (bucket[0 ], ptr);
232+
233+ return false ;
234+ }
235+
236+ static PointerTable* get ();
237+
238+ private:
239+ void resetIfNeeded () {
240+ const long long now = Listener::getElapsedTimeMillis ();
241+ if (MONGO_unlikely (now - _lastReset > RotateTimeSecs*1000 ))
242+ reset ();
243+ }
244+
245+ void reset () {
246+ memset (_table, 0 , sizeof (_table));
247+ _lastReset = Listener::getElapsedTimeMillis ();
248+ }
249+
250+ static size_t superpageOf (size_t ptr) {
251+ return ptr & superpageMask;
252+ }
253+
254+ static size_t bucketFor (size_t ptr) {
255+ return (ptr >> superpageShift) % buckets;
256+ }
257+
258+ static bool haveSeenPage (size_t superpage, size_t ptr) {
259+ return superpage & pageBitOf (ptr);
260+ }
261+
262+ static void markPageSeen (size_t & superpage, size_t ptr) {
263+ superpage |= pageBitOf (ptr);
264+ }
265+
266+ static size_t pageBitOf (size_t ptr) {
267+ return 1 << ((ptr & pageSelectorMask) >> pageSelectorShift);
268+ }
269+
270+ enum {
271+ /* A "superpage" is a group of 16 contiguous pages that differ
272+ * only in the low-order 16 bits. This means that there is
273+ * enough room in the low-order bits to store a bitmap for each
274+ * page in the superpage.
275+ */
276+ superpageMask = ~0xffffLL ,
277+ superpageShift = 16 ,
278+ pageSelectorMask = 0xf000LL , // selects a page in a superpage
279+ pageSelectorShift = 12 ,
280+
281+ // Tunables
282+ capacity = 128 , // in superpages
283+ bucketSize = 4 , // half cache line
284+ buckets = capacity/bucketSize,
285+ };
286+
287+ /* * organized similar to a CPU cache
288+ * bucketSize-way set associative
289+ * least-recently-inserted replacement policy
290+ */
291+ size_t _table[buckets][bucketSize];
292+ long long _lastReset; // time in millis
293+ };
203294 }
204295
296+
297+ // These need to be outside the ps namespace due to the way they are defined
298+ #if defined(__linux__) && defined(__GNUC__)
299+ __thread ps::PointerTable _pointerTable;
300+ ps::PointerTable* ps::PointerTable::get () {
301+ return &_pointerTable;
302+ }
303+ #elif defined(_WIN32)
304+ __declspec ( thread ) ps::PointerTable _pointerTable;
305+ ps::PointerTable* ps::PointerTable::get () {
306+ return &_pointerTable;
307+ }
308+ #else
309+ boost::thread_specific_ptr<ps::PointerTable> _pointerTable;
310+ ps::PointerTable* ps::PointerTable::get () {
311+ PointerTable* pt = _pointerTable.get ();
312+ if ( ! pt ) {
313+ pt = new ps::PointerTable ();
314+ _pointerTable.reset ( pt );
315+ }
316+ return pt;
317+ }
318+ #endif
319+
205320 bool Record::MemoryTrackingEnabled = true ;
206321
207322 volatile int __record_touch_dummy = 1 ; // this is used to make sure the compiler doesn't get too smart on us
@@ -268,7 +383,9 @@ namespace mongo {
268383 const size_t region = page >> 6 ;
269384 const size_t offset = page & 0x3f ;
270385
271- if ( ps::rolling[ps::bigHash (region)].access ( region , offset , false ) ) {
386+ const bool seen = ps::PointerTable::get ()->seen (reinterpret_cast <size_t >(data));
387+ if (seen || ps::rolling[ps::bigHash (region)].access ( region , offset , false ) ) {
388+
272389#ifdef _DEBUG
273390 if ( blockSupported && ! ProcessInfo::blockInMemory ( const_cast <char *>(data) ) ) {
274391 warning () << " we think data is in ram but system says no" << endl;
@@ -289,10 +406,14 @@ namespace mongo {
289406
290407
291408 Record* Record::accessed () {
292- const size_t page = (size_t )_data >> 12 ;
293- const size_t region = page >> 6 ;
294- const size_t offset = page & 0x3f ;
295- ps::rolling[ps::bigHash (region)].access ( region , offset , true );
409+ const bool seen = ps::PointerTable::get ()->seen (reinterpret_cast <size_t >(_data));
410+ if (!seen){
411+ const size_t page = (size_t )_data >> 12 ;
412+ const size_t region = page >> 6 ;
413+ const size_t offset = page & 0x3f ;
414+ ps::rolling[ps::bigHash (region)].access ( region , offset , true );
415+ }
416+
296417 return this ;
297418 }
298419
0 commit comments