diff options
Diffstat (limited to 'db/record.cpp')
-rw-r--r-- | db/record.cpp | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/db/record.cpp b/db/record.cpp new file mode 100644 index 0000000..51dc520 --- /dev/null +++ b/db/record.cpp @@ -0,0 +1,230 @@ +// record.cpp + +#include "pch.h" +#include "pdfile.h" +#include "../util/processinfo.h" +#include "../util/net/listen.h" + +namespace mongo { + + namespace ps { + + enum State { + In , Out, Unk + }; + + enum Constants { + SliceSize = 65536 , + MaxChain = 20 , // intentionally very low + NumSlices = 10 , + RotateTimeSecs = 90 + }; + + int hash( size_t region ) { + return + abs( ( ( 7 + (int)(region & 0xFFFF) ) + * ( 11 + (int)( ( region >> 16 ) & 0xFFFF ) ) +#if defined(_WIN64) || defined(__amd64__) + * ( 13 + (int)( ( region >> 32 ) & 0xFFFF ) ) + * ( 17 + (int)( ( region >> 48 ) & 0xFFFF ) ) +#endif + ) % SliceSize ); + } + + + /** + * simple hash map for region -> status + * this constitures a single region of time + * it does chaining, but very short chains + */ + class Slice { + + struct Entry { + size_t region; + unsigned long long value; + }; + + public: + + Slice() { + reset(); + } + + void reset() { + memset( _data , 0 , SliceSize * sizeof(Entry) ); + } + + State get( int regionHash , size_t region , short offset ) { + DEV assert( hash( region ) == regionHash ); + + Entry * e = _get( regionHash , region , false ); + if ( ! e ) + return Unk; + + return ( e->value & ( ((unsigned long long)1) << offset ) ) ? In : Out; + } + + /** + * @return true if added, false if full + */ + bool in( int regionHash , size_t region , short offset ) { + DEV assert( hash( region ) == regionHash ); + + Entry * e = _get( regionHash , region , true ); + if ( ! e ) + return false; + + e->value |= ((unsigned long long)1) << offset; + return true; + } + + private: + + Entry* _get( int start , size_t region , bool add ) { + for ( int i=0; i<MaxChain; i++ ) { + + int bucket = ( start + i ) % SliceSize; + + if ( _data[bucket].region == 0 ) { + if ( ! add ) + return 0; + + _data[bucket].region = region; + return &_data[bucket]; + } + + if ( _data[bucket].region == region ) { + return &_data[bucket]; + } + } + return 0; + } + + Entry _data[SliceSize]; + }; + + + /** + * this contains many slices of times + * the idea you put mem status in the current time slice + * and then after a certain period of time, it rolls off so we check again + */ + class Rolling { + + public: + Rolling() { + _curSlice = 0; + _lastRotate = Listener::getElapsedTimeMillis(); + } + + + /** + * after this call, we assume the page is in ram + * @param doHalf if this is a known good access, want to put in first half + * @return whether we know the page is in ram + */ + bool access( size_t region , short offset , bool doHalf ) { + int regionHash = hash(region); + + scoped_spinlock lk( _lock ); + + static int rarely_count = 0; + if ( rarely_count++ % 2048 == 0 ) { + long long now = Listener::getElapsedTimeMillis(); + RARELY if ( now == 0 ) { + tlog() << "warning Listener::getElapsedTimeMillis returning 0ms" << endl; + } + + if ( now - _lastRotate > ( 1000 * RotateTimeSecs ) ) { + _rotate(); + } + } + + for ( int i=0; i<NumSlices / ( doHalf ? 2 : 1 ); i++ ) { + int pos = (_curSlice+i)%NumSlices; + State s = _slices[pos].get( regionHash , region , offset ); + + if ( s == In ) + return true; + + if ( s == Out ) { + _slices[pos].in( regionHash , region , offset ); + return false; + } + } + + // we weren't in any slice + // so add to cur + if ( ! _slices[_curSlice].in( regionHash , region , offset ) ) { + _rotate(); + _slices[_curSlice].in( regionHash , region , offset ); + } + return false; + } + + private: + + void _rotate() { + _curSlice = ( _curSlice + 1 ) % NumSlices; + _slices[_curSlice].reset(); + _lastRotate = Listener::getElapsedTimeMillis(); + } + + int _curSlice; + long long _lastRotate; + Slice _slices[NumSlices]; + + SpinLock _lock; + } rolling; + + } + + bool Record::MemoryTrackingEnabled = true; + + + volatile int __record_touch_dummy = 1; // this is used to make sure the compiler doesn't get too smart on us + void Record::touch( bool entireRecrd ) { + + if ( lengthWithHeaders > HeaderSize ) { // this also makes sure lengthWithHeaders is in memory + char * addr = data; + char * end = data + netLength(); + for ( ; addr <= end ; addr += 2048 ) { + __record_touch_dummy += addr[0]; + + break; // TODO: remove this, pending SERVER-3711 + + if ( ! entireRecrd ) + break; + } + } + + } + + bool Record::likelyInPhysicalMemory() { + if ( ! MemoryTrackingEnabled ) + return true; + + static bool blockSupported = ProcessInfo::blockCheckSupported(); + + const size_t page = (size_t)data >> 12; + const size_t region = page >> 6; + const size_t offset = page & 0x3f; + + if ( ps::rolling.access( region , offset , false ) ) + return true; + + if ( ! blockSupported ) + return false; + return ProcessInfo::blockInMemory( data ); + } + + Record* Record::accessed() { + const size_t page = (size_t)data >> 12; + const size_t region = page >> 6; + const size_t offset = page & 0x3f; + + ps::rolling.access( region , offset , true ); + return this; + } + +} |