Skip to content

Commit 368c55d

Browse files
committed
SERVER-6572 use a thread local cache for better perforamnce in record caching
needs work on os x
1 parent e6d8466 commit 368c55d

File tree

1 file changed

+126
-5
lines changed

1 file changed

+126
-5
lines changed

src/mongo/db/record.cpp

Lines changed: 126 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)