/** @file perftests.cpp.cpp : unit tests relating to performance The idea herein is tests that run fast and can be part of the normal CI suite. So no tests herein that take a long time to run. Obviously we need those too, but they will be separate. These tests use DBDirectClient; they are a bit white-boxish. */ /** * Copyright (C) 2008 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault #include "mongo/platform/basic.h" #include #include #include #include #include #include #include #include #include "mongo/db/db.h" #include "mongo/db/dbdirectclient.h" #include "mongo/db/json.h" #include "mongo/db/lasterror.h" #include "mongo/db/operation_context_impl.h" #include "mongo/db/storage/mmap_v1/durable_mapped_file.h" #include "mongo/db/storage/mmap_v1/dur_stats.h" #include "mongo/db/storage/mmap_v1/btree/key.h" #include "mongo/db/storage_options.h" #include "mongo/dbtests/dbtests.h" #include "mongo/dbtests/framework_options.h" #include "mongo/util/allocator.h" #include "mongo/util/checksum.h" #include "mongo/util/compress.h" #include "mongo/util/fail_point.h" #include "mongo/util/log.h" #include "mongo/util/mmap.h" #include "mongo/util/timer.h" #include "mongo/util/version.h" #include "mongo/util/version_reporting.h" #include "mongo/db/concurrency/lock_state.h" namespace PerfTests { using boost::shared_ptr; using std::cout; using std::endl; using std::fixed; using std::ifstream; using std::left; using std::min; using std::right; using std::setprecision; using std::setw; using std::string; using std::vector; const bool profiling = false; class ClientBase { public: ClientBase() : _client(&_txn) { _prevError = mongo::lastError._get( false ); mongo::lastError.release(); mongo::lastError.reset( new LastError() ); } virtual ~ClientBase() { mongo::lastError.reset( _prevError ); } protected: void insert( const char *ns, BSONObj o ) { _client.insert( ns, o ); } void update( const char *ns, BSONObj q, BSONObj o, bool upsert = 0 ) { _client.update( ns, Query( q ), o, upsert ); } bool error() { return !_client.getPrevError().getField( "err" ).isNull(); } DBClientBase* client() { return &_client; } OperationContext* txn() { return &_txn; } private: LastError* _prevError; OperationContextImpl _txn; DBDirectClient _client; }; /* if you want recording of the timings, place the password for the perf database in ./../settings.py: pstatspassword="" */ static boost::shared_ptr conn; static string _perfhostname; void pstatsConnect() { // no writing to perf db if _DEBUG DEV return; const char *fn = "../../settings.py"; if( !boost::filesystem::exists(fn) ) { if( boost::filesystem::exists("settings.py") ) fn = "settings.py"; else { cout << "no ../../settings.py or ./settings.py file found. will not write perf stats to pstats db." << endl; cout << "it is recommended this be enabled even on dev boxes" << endl; return; } } try { if( conn == 0 ) { MemoryMappedFile f; const char *p = (const char *) f.mapWithOptions(fn, MongoFile::READONLY); string pwd; { const char *q = str::after(p, "pstatspassword=\""); if( *q == 0 ) { cout << "info perftests.cpp: no pstatspassword= in settings.py" << endl; return; } else { pwd = str::before(q, '\"'); } } boost::shared_ptr c(new DBClientConnection(false, 0, 60)); string err; if( c->connect(HostAndPort("perfdb.10gen.cc"), err) ) { if( !c->auth("perf", "perf", pwd, err) ) { cout << "info: authentication with stats db failed: " << err << endl; verify(false); } conn = c; // override the hostname with the buildbot hostname, if present ifstream hostf( "../../info/host" ); if ( hostf.good() ) { char buf[1024]; hostf.getline(buf, sizeof(buf)); _perfhostname = buf; } else { _perfhostname = getHostName(); } } else { cout << err << " (to log perfstats)" << endl; } } } catch(...) { cout << "pstatsConnect() didn't work; ignoring" << endl; } } class B : public ClientBase { string _ns; protected: const char *ns() { return _ns.c_str(); } // anything you want to do before being timed virtual void prep() { } // anything you want to do before threaded test virtual void prepThreaded() {} virtual void timed() = 0; // optional 2nd test phase to be timed separately. You must provide it with a unique // name in order for it to run by overloading 'name2'. virtual void timed2(DBClientBase*) {} // return name of second test. virtual string name2() { return name(); } virtual void post() { } virtual string name() = 0; // how long to run test. 0 is a sentinel which means just run the timed() method once and time it. virtual int howLongMillis() { return profiling ? 30000 : 5000; } /* override if your test output doesn't need that */ virtual bool showDurStats() { return true; } public: virtual unsigned batchSize() { return 50; } void say(unsigned long long n, long long us, string s) { unsigned long long rps = (n*1000*1000)/(us > 0 ? us : 1); cout << "stats " << setw(42) << left << s << ' ' << right << setw(9) << rps << ' ' << right << setw(5) << us/1000 << "ms "; if (showDurStats()) { cout << dur::stats.curr()->_asCSV(); } cout << endl; if( conn && !conn->isFailed() ) { const char *ns = "perf.pstats"; if(frameworkGlobalParams.perfHist) { static bool needver = true; try { // try to report rps from last time */ Query q; { BSONObjBuilder b; b.append("host", _perfhostname); b.append("test", s); b.append("dur", storageGlobalParams.dur); DEV { b.append("info.DEBUG",true); } else b.appendNull("info.DEBUG"); if( sizeof(int*) == 4 ) b.append("info.bits", 32); else b.appendNull("info.bits"); q = Query(b.obj()).sort("when",-1); } BSONObj fields = BSON( "rps" << 1 << "info" << 1 ); vector v; conn->findN(v, ns, q, frameworkGlobalParams.perfHist, 0, &fields); for( vector::iterator i = v.begin(); i != v.end(); i++ ) { BSONObj o = *i; double lastrps = o["rps"].Number(); if( 0 && lastrps ) { cout << "stats " << setw(42) << right << "new/old:" << ' ' << setw(9); cout << fixed << setprecision(2) << rps / lastrps; if( needver ) { cout << " " << o.getFieldDotted("info.git").toString(); } cout << '\n'; } } } catch(...) { } cout.flush(); needver = false; } { bob b; b.append("host", _perfhostname); b.appendTimeT("when", time(0)); b.append("test", s); b.append("rps", (int) rps); b.append("millis", us/1000); b.appendBool("dur", storageGlobalParams.dur); if (showDurStats() && storageGlobalParams.dur) { b.append("durStats", dur::stats.asObj()); } { bob inf; inf.append("version", versionString); if( sizeof(int*) == 4 ) inf.append("bits", 32); DEV inf.append("DEBUG", true); #if defined(_WIN32) inf.append("os", "win"); #endif inf.append("git", gitVersion()); #ifdef MONGO_SSL inf.append("OpenSSL", openSSLVersion()); #endif inf.append("boost", BOOST_VERSION); b.append("info", inf.obj()); } BSONObj o = b.obj(); //cout << "inserting " << o.toString() << endl; try { conn->insert(ns, o); } catch ( std::exception& e ) { warning() << "couldn't save perf results: " << e.what() << endl; } } } } /** if true runs timed2() again with several threads (8 at time of this writing). */ virtual bool testThreaded() { return false; } int howLong() { int hlm = howLongMillis(); DEV { // don't run very long with _DEBUG - not very meaningful anyway on that build hlm = min(hlm, 500); } return hlm; } void run() { unsigned long long n = 0; _ns = string("perftest.") + name(); client()->dropCollection(ns()); prep(); int hlm = howLong(); mongo::Timer t; n = 0; const unsigned int Batch = batchSize(); if( hlm == 0 ) { // means just do once timed(); } else { do { unsigned int i; for( i = 0; i < Batch; i++ ) timed(); n += i; } while( t.micros() < (hlm * 1000) ); } client()->getLastError(); // block until all ops are finished say(n, t.micros(), name()); post(); string test2name = name2(); { if( test2name != name() ) { dur::stats.curr()->reset(); mongo::Timer t; unsigned long long n = 0; while( 1 ) { unsigned int i; for( i = 0; i < Batch; i++ ) timed2(client()); n += i; if( t.millis() > hlm ) break; } say(n, t.micros(), test2name); } } if( testThreaded() ) { const int nThreads = 8; //cout << "testThreaded nThreads:" << nThreads << endl; mongo::Timer t; const unsigned long long result = launchThreads(nThreads); say(result/nThreads, t.micros(), test2name+"-threaded"); } } bool stop; void thread(unsigned long long* counter) { #if defined(_WIN32) static int z; srand( ++z ^ (unsigned) time(0)); #endif Client::initThreadIfNotAlready("perftestthr"); OperationContextImpl txn; DBDirectClient c(&txn); const unsigned int Batch = batchSize(); prepThreaded(); while( 1 ) { unsigned int i = 0; for( i = 0; i < Batch; i++ ) timed2(&c); *counter += i; if( stop ) break; } cc().shutdown(); } unsigned long long launchThreads(int remaining) { stop = false; if (!remaining) { int hlm = howLong(); sleepmillis(hlm); stop = true; return 0; } unsigned long long counter = 0; boost::thread athread(stdx::bind(&B::thread, this, &counter)); unsigned long long child = launchThreads(remaining - 1); athread.join(); unsigned long long accum = child + counter; return accum; } }; unsigned dontOptimizeOutHopefully = 1; class NonDurTest : public B { public: virtual int howLongMillis() { return 3000; } virtual bool showDurStats() { return false; } }; class BSONIter : public NonDurTest { public: int n; bo b, sub; string name() { return "BSONIter"; } BSONIter() { n = 0; bo sub = bob().appendTimeT("t", time(0)).appendBool("abool", true).appendBinData("somebin", 3, BinDataGeneral, "abc").appendNull("anullone").obj(); b = BSON( "_id" << OID() << "x" << 3 << "yaaaaaa" << 3.00009 << "zz" << 1 << "q" << false << "obj" << sub << "zzzzzzz" << "a string a string" ); } void timed() { for( bo::iterator i = b.begin(); i.more(); ) if( i.next().fieldName() ) n++; for( bo::iterator i = sub.begin(); i.more(); ) if( i.next().fieldName() ) n++; } }; class BSONGetFields1 : public NonDurTest { public: int n; bo b, sub; string name() { return "BSONGetFields1By1"; } BSONGetFields1() { n = 0; bo sub = bob().appendTimeT("t", time(0)).appendBool("abool", true).appendBinData("somebin", 3, BinDataGeneral, "abc").appendNull("anullone").obj(); b = BSON( "_id" << OID() << "x" << 3 << "yaaaaaa" << 3.00009 << "zz" << 1 << "q" << false << "obj" << sub << "zzzzzzz" << "a string a string" ); } void timed() { if( b["x"].eoo() ) n++; if( b["q"].eoo() ) n++; if( b["zzz"].eoo() ) n++; } }; class BSONGetFields2 : public BSONGetFields1 { public: string name() { return "BSONGetFields"; } void timed() { static const char *names[] = { "x", "q", "zzz" }; BSONElement elements[3]; b.getFields(3, names, elements); if( elements[0].eoo() ) n++; if( elements[1].eoo() ) n++; if( elements[2].eoo() ) n++; } }; class KeyTest : public B { public: KeyV1Owned a,b,c; string name() { return "Key-woequal"; } virtual int howLongMillis() { return 3000; } KeyTest() : a(BSON("a"<<1<<"b"<<3.0<<"c"<<"qqq")), b(BSON("a"<<1<<"b"<<3.0<<"c"<<"qqq")), c(BSON("a"<<1<<"b"<<3.0<<"c"<<"qqqb")) {} virtual bool showDurStats() { return false; } void timed() { verify( a.woEqual(b) ); verify( !a.woEqual(c) ); } }; unsigned long long aaa; class Timer : public B { public: string name() { return "Timer"; } virtual int howLongMillis() { return 1000; } virtual bool showDurStats() { return false; } void timed() { mongo::Timer t; aaa += t.millis(); } }; class Sleep0Ms : public B { public: string name() { return "Sleep0Ms"; } virtual int howLongMillis() { return 400; } virtual bool showDurStats() { return false; } void timed() { sleepmillis(0); aaa++; } }; #if defined(__USE_XOPEN2K) class Yield : public B { public: string name() { return "Yield"; } virtual int howLongMillis() { return 400; } virtual bool showDurStats() { return false; } void timed() { pthread_yield(); aaa++; } }; #endif RWLock lk("testrw"); SimpleMutex m("simptst"); boost::mutex mboost; boost::timed_mutex mboost_timed; std::mutex mstd; std::timed_mutex mstd_timed; SpinLock s; boost::condition c; class NotifyOne : public B { public: string name() { return "notify_one"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } void timed() { c.notify_one(); } }; class boostmutexspeed : public B { public: string name() { return "boost::mutex"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } void timed() { boost::lock_guard lk(mboost); } }; class boosttimed_mutexspeed : public B { public: string name() { return "boost::timed_mutex"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } void timed() { boost::lock_guard lk(mboost_timed); } }; class simplemutexspeed : public B { public: string name() { return "simplemutex"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } void timed() { SimpleMutex::scoped_lock lk(m); } }; class stdmutexspeed : public B { public: string name() { return "std::mutex"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } void timed() { std::lock_guard lk(mstd); } }; class stdtimed_mutexspeed : public B { public: string name() { return "std::timed_mutex"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } void timed() { std::lock_guard lk(mstd_timed); } }; class spinlockspeed : public B { public: string name() { return "spinlock"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } void timed() { mongo::scoped_spinlock lk(s); } }; int cas; class casspeed : public B { public: string name() { return "compareandswap"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } void timed() { #ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 #define RUNCOMPARESWAP 1 __sync_bool_compare_and_swap(&cas, 0, 0); #endif } }; class rlock : public B { public: string name() { return "rlock"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } void timed() { lk.lock_shared(); lk.unlock_shared(); } }; class wlock : public B { public: string name() { return "wlock"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } void timed() { lk.lock(); lk.unlock(); } }; class locker_test : public B { public: boost::thread_specific_ptr resId; boost::thread_specific_ptr locker; boost::thread_specific_ptr id; boost::mutex lock; // The following members are intitialized in the constructor LockMode lockMode; LockMode glockMode; locker_test(LockMode m = MODE_X, LockMode gm = MODE_IX) : lockMode(m), glockMode(gm) { } virtual string name() { return (str::stream() << "locker_contested" << lockMode); } virtual bool showDurStats() { return false; } virtual bool testThreaded() { return true; } virtual void prep() { resId.reset(new ResourceId(RESOURCE_COLLECTION, std::string("TestDB.collection"))); locker.reset(new MMAPV1LockerImpl()); } virtual void prepThreaded() { resId.reset(new ResourceId(RESOURCE_COLLECTION, std::string("TestDB.collection"))); id.reset(new int); lock.lock(); lock.unlock(); locker.reset(new MMAPV1LockerImpl()); } void timed() { locker->lockGlobal(glockMode); locker->lock(*resId, lockMode); locker->unlockAll(); } void timed2(DBClientBase* c) { locker->lockGlobal(glockMode); locker->lock(*resId, lockMode); locker->unlockAll(); } }; class glockerIX : public locker_test { public: virtual string name() { return (str::stream() << "glocker" << glockMode); } void timed() { locker->lockGlobal(glockMode); locker->unlockAll(); } void timed2(DBClientBase* c) { locker->lockGlobal(glockMode); locker->unlockAll(); } }; class locker_test_uncontested : public locker_test { public: locker_test_uncontested(LockMode m = MODE_IX, LockMode gm = MODE_IX) : locker_test(m, gm) { } virtual string name() { return (str::stream() << "locker_uncontested" << lockMode); } virtual void prepThreaded() { id.reset(new int); lock.lock(); lock.unlock(); locker.reset(new LockerImpl); resId.reset(new ResourceId(RESOURCE_COLLECTION, str::stream() << "TestDB.collection" << *id)); } }; class glockerIS : public glockerIX { public: glockerIS() : glockerIX() { glockMode = MODE_IS; } }; class locker_contestedX : public locker_test { public: locker_contestedX() : locker_test(MODE_X, MODE_IX) { } }; class locker_contestedS : public locker_test { public: locker_contestedS() : locker_test(MODE_S, MODE_IS) { } }; class locker_uncontestedX : public locker_test_uncontested { public: locker_uncontestedX() : locker_test_uncontested(MODE_X, MODE_IX) { } }; class locker_uncontestedS : public locker_test_uncontested { public: locker_uncontestedS() : locker_test_uncontested(MODE_S, MODE_IS) { } }; class CTM : public B { public: CTM() : last(0), delts(0), n(0) { } string name() { return "curTimeMillis64"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } unsigned long long last; unsigned long long delts; unsigned n; void timed() { unsigned long long x = curTimeMillis64(); aaa += x; if( last ) { unsigned long long delt = x-last; if( delt ) { delts += delt; n++; } } last = x; } void post() { // we need to know if timing is highly ungranular - that could be relevant in some places if( n ) cout << " avg timer granularity: " << ((double)delts)/n << "ms " << endl; } }; class CTMicros : public B { public: CTMicros() : last(0), delts(0), n(0) { } string name() { return "curTimeMicros64"; } virtual int howLongMillis() { return 500; } virtual bool showDurStats() { return false; } unsigned long long last; unsigned long long delts; unsigned n; void timed() { unsigned long long x = curTimeMicros64(); aaa += x; if( last ) { unsigned long long delt = x-last; if( delt ) { delts += delt; n++; } } last = x; } void post() { // we need to know if timing is highly ungranular - that could be relevant in some places if( n ) cout << " avg timer granularity: " << ((double)delts)/n << "ms " << endl; } }; class Bldr : public B { public: int n; string name() { return "BufBuilder"; } Bldr() { } virtual int howLongMillis() { return 3000; } virtual bool showDurStats() { return false; } void timed() { BufBuilder b; b.appendNum(3); b.appendUChar(' '); b.appendStr("abcd"); n += b.len(); } }; class StkBldr : public B { public: virtual int howLongMillis() { return 3000; } int n; string name() { return "StackBufBuilder"; } virtual bool showDurStats() { return false; } void timed() { StackBufBuilder b; b.appendNum(3); b.appendUChar(' '); b.appendStr("abcd"); n += b.len(); } }; // if a test is this fast, it was optimized out class Dummy : public B { public: Dummy() { } virtual int howLongMillis() { return 3000; } string name() { return "dummy"; } void timed() { dontOptimizeOutHopefully++; } virtual bool showDurStats() { return false; } }; // test thread local speed #if defined(_WIN32) __declspec( thread ) int x; class TLS2 : public B { public: virtual int howLongMillis() { return 3000; } string name() { return "thread-local-storage2"; } void timed() { if( x ) dontOptimizeOutHopefully++; } virtual bool showDurStats() { return false; } }; #endif // test thread local speed class TLS : public B { public: virtual int howLongMillis() { return 3000; } string name() { return "thread-local-storage"; } void timed() { dontOptimizeOutHopefully++; } virtual bool showDurStats() { return false; } }; bool dummy1 = false; class TestException : public DBException { public: TestException() : DBException("testexception",3) { } }; struct Z { Z() { dontOptimizeOutHopefully--; } ~Z() { dontOptimizeOutHopefully++; } }; void thr1(int n) { if( dontOptimizeOutHopefully ) { throw TestException(); } mongo::unittest::log() << "hmmm" << endl; } void thr2(int n) { if( --n <= 0 ) { if( dontOptimizeOutHopefully ) { throw TestException(); } mongo::unittest::log() << "hmmm" << endl; } Z z; try { thr2(n-1); } catch(DBException&) { } } void thr3(int n) { if( --n <= 0 ) { if( dontOptimizeOutHopefully ) { throw TestException(); } mongo::unittest::log() << "hmmm" << endl; } try { Z z; thr3(n-1); } catch(DBException&) { } } void thr4(int n) { if( --n <= 0 ) { if( dontOptimizeOutHopefully ) { throw TestException(); } mongo::unittest::log() << "hmmm" << endl; } Z z; thr4(n-1); } template< void T (int) > class Throw : public B { public: virtual int howLongMillis() { return 2000; } string name() { return "throw"; } void timed() { try { T(10); dontOptimizeOutHopefully += 2; } catch(DBException& e) { e.getCode(); dontOptimizeOutHopefully++; } } virtual bool showDurStats() { return false; } }; class New128 : public B { public: virtual int howLongMillis() { return 2000; } string name() { return "new128"; } void timed() { char *p = new char[128]; if( dontOptimizeOutHopefully++ > 0 ) delete[] p; } virtual bool showDurStats() { return false; } }; class New8 : public B { public: virtual int howLongMillis() { return 2000; } string name() { return "new8"; } void timed() { char *p = new char[8]; if( dontOptimizeOutHopefully++ > 0 ) delete[] p; } virtual bool showDurStats() { return false; } }; class Compress : public B { public: const unsigned sz; void *p; Compress() : sz(1024*1024*100+3) { } virtual unsigned batchSize() { return 1; } string name() { return "compress"; } virtual bool showDurStats() { return false; } virtual int howLongMillis() { return 4000; } void prep() { p = mongoMalloc(sz); // this isn't a fair test as it is mostly rands but we just want a rough perf check static int last; for (unsigned i = 0; iinsert( ns(), o ); } void timed() { client()->insert( ns(), o ); } void post() { verify( client()->count(ns()) == 1 ); } }; class Insert1 : public B { const BSONObj x; OID oid; BSONObj query; public: virtual int howLongMillis() { return profiling ? 30000 : 5000; } Insert1() : x( BSON("x" << 99) ) { oid.init(); query = BSON("_id" << oid); i = 0; } string name() { return "insert-simple"; } unsigned i; void timed() { BSONObj o = BSON( "_id" << i++ << "x" << 99 ); client()->insert( ns(), o ); } virtual bool testThreaded() { if( profiling ) return false; return true; } string name2() { return "findOne_by_id"; } void timed2(DBClientBase* c) { Query q = QUERY( "_id" << (unsigned) (rand() % i) ); c->findOne(ns(), q); } void post() { #if !defined(_DEBUG) verify( client()->count(ns()) > 50 ); #endif } }; class InsertBig : public B { BSONObj x; virtual int howLongMillis() { if (sizeof(void*) == 4) { // See SERVER-12556 - Running this test for some time causes occasional failures // on Windows 32-bit, because the virtual address space is used up and remapping // starts to fail. Value of zero means that only one iteration of the test // will run. // return 0; } return 5000; } public: InsertBig() { char buf[200000]; BSONObjBuilder b; b.append("x", 99); b.appendBinData("bin", 200000, (BinDataType) 129, buf); x = b.obj(); } string name() { return "insert-big"; } void timed() { client()->insert( ns(), x ); } }; class InsertRandom : public B { public: virtual int howLongMillis() { return profiling ? 30000 : 5000; } string name() { return "random-inserts"; } void prep() { client()->insert( ns(), BSONObj() ); ASSERT_OK(dbtests::createIndex(txn(), ns(), BSON("x"<<1))); } void timed() { int x = rand(); BSONObj y = BSON("x" << x << "y" << rand() << "z" << 33); client()->insert(ns(), y); } }; /** upserts about 32k records and then keeps updating them 2 indexes */ class Update1 : public B { public: static int rand() { return std::rand() & 0x7fff; } virtual string name() { return "random-upserts"; } void prep() { client()->insert( ns(), BSONObj() ); ASSERT_OK(dbtests::createIndex(txn(), ns(), BSON("x"<<1))); } void timed() { int x = rand(); BSONObj q = BSON("x" << x); BSONObj y = BSON("x" << x << "y" << rand() << "z" << 33); client()->update(ns(), q, y, /*upsert*/true); } virtual bool testThreaded() { return true; } virtual string name2() { return name()+"-inc"; } virtual void timed2(DBClientBase* c) { static BSONObj I = BSON( "$inc" << BSON( "y" << 1 ) ); // test some $inc's int x = rand(); BSONObj q = BSON("x" << x); c->update(ns(), q, I); } }; template class MoreIndexes : public T { public: string name() { return T::name() + "-more-indexes"; } void prep() { T::prep(); ASSERT_OK(dbtests::createIndex(this->txn(), this->ns(), BSON("y"<<1))); ASSERT_OK(dbtests::createIndex(this->txn(), this->ns(), BSON("z"<<1))); } }; // Tests what the worst case is for the overhead of enabling a fail point. If 'fpInjected' // is false, then the fail point will be compiled out. If 'fpInjected' is true, then the // fail point will be compiled in. Since the conditioned block is more or less trivial, any // difference in performance is almost entirely attributable to the cost of checking // whether the failpoint is enabled. // // If fpEnabled is true, then the failpoint will be enabled, using the 'nTimes' model since // this looks to be the most expensive code path through the fail point enable detection // logic. // // It makes no sense to trigger the slow path if the fp is not injected, so that will fail // to compile. template class FailPointTest : public B { public: BOOST_STATIC_ASSERT(fpInjected || !fpEnabled); FailPointTest() : B() , _value(0) { if (fpEnabled) { _fp.setMode( FailPoint::nTimes, std::numeric_limits::max()); verify(_fp.shouldFail()); } else { verify(!_fp.shouldFail()); } } virtual string name() { return std::string("failpoint") + (fpInjected ? "-present" : "-absent") + (fpInjected ? (fpEnabled ? "-enabled" : "-disabled") : ""); } virtual int howLongMillis() { return 5000; } virtual bool showDurStats() { return false; } virtual void timed() { if (MONGO_unlikely(_value != 0) || (fpInjected && MONGO_FAIL_POINT(_fp))) { // We should only get here if the failpoint is enabled. verify(fpEnabled); } } virtual string name2() { // Will inhibit running 'timed2' as its own test, but will cause it to be run as a // threaded test. return name(); } virtual void timed2(DBClientBase*) { // We just want to re-run 'timed' when timed2 is invoked as a threaded test, so it // invoke 'timed' statically to avoid overhead of virtual function call. this->FailPointTest::timed(); } virtual bool testThreaded() { return true; } private: // The failpoint under test. FailPoint _fp; // _value should always be zero for this test to behave as expected, but we don't want // the compiler exploiting this fact to compile out our check, so mark it volatile. const volatile int _value; }; void t() { for( int i = 0; i < 20; i++ ) { sleepmillis(21); string fn = "/tmp/t1"; DurableMappedFile f; unsigned long long len = 1 * 1024 * 1024; verify( f.create(fn, len, /*sequential*/rand()%2==0) ); { char *p = (char *) f.getView(); verify(p); // write something to the private view as a test strcpy(p, "hello"); } if (storageGlobalParams.dur) { char *w = (char *) f.view_write(); strcpy(w + 6, "world"); } MongoFileFinder ff; ASSERT( ff.findByPath(fn) ); } } class StatusTestBase : public B { public: StatusTestBase() : _message("Some string data that should not fit in a short string optimization") { } virtual int howLongMillis() { return 2000; } virtual bool showDurStats() { return false; } protected: NOINLINE_DECL Status doThingOK() const { return Status::OK(); } NOINLINE_DECL Status doThingNotOK() const{ return Status( ErrorCodes::InternalError, _message, 42); } private: const std::string _message; }; class ReturnOKStatus : public StatusTestBase { public: string name() { return "return-ok-status"; } void timed() { doThingOK(); } }; class ReturnNotOKStatus : public StatusTestBase { public: string name() { return "return-not-ok-status"; } void timed() { doThingNotOK(); } }; class CopyOKStatus : public StatusTestBase { public: CopyOKStatus() : _status(doThingOK()) {} string name() { return "copy-ok-status"; } void timed() { const Status copy = _status; } private: const Status _status; }; class CopyNotOKStatus : public StatusTestBase { public: CopyNotOKStatus() : _status(doThingNotOK()) {} string name() { return "copy-not-ok-status"; } void timed() { const Status copy = _status; } private: const Status _status; }; class StatusMoveTestBase : public StatusTestBase { public: StatusMoveTestBase(bool ok) : StatusTestBase() , _a(ok ? doThingOK() : doThingNotOK()) , _b(_a.isOK() ? Status::OK() : Status(_a.code(), _a.reason().c_str(), _a.location())) { } void timed() { Status temp(std::move(_a)); _a = std::move(_b); _b = std::move(temp); } protected: Status _a; Status _b; }; class MoveOKStatus : public StatusMoveTestBase { public: MoveOKStatus() : StatusMoveTestBase(true) {} string name() { return "move-ok-status"; } }; class MoveNotOKStatus : public StatusMoveTestBase { public: MoveNotOKStatus() : StatusMoveTestBase(false) {} string name() { return "move-not-ok-status"; } }; class All : public Suite { public: All() : Suite( "perf" ) { } Result * run( const string& filter, int runsPerTest ) { boost::thread a(t); Result * res = Suite::run(filter, runsPerTest); a.join(); return res; } void setupTests() { pstatsConnect(); cout << "stats test rps------ time-- " << dur::stats.curr()->_CSVHeader() << endl; if( profiling ) { add< Insert1 >(); } else { add< Dummy >(); add< ChecksumTest >(); add< Compress >(); add< TLS >(); #if defined(_WIN32) add< TLS2 >(); #endif add< New8 >(); add< New128 >(); add< Throw< thr1 > >(); add< Throw< thr2 > >(); add< Throw< thr3 > >(); #if !defined(__clang__) || !defined(MONGO_OPTIMIZED_BUILD) // clang-3.2 (and earlier?) miscompiles this test when optimization is on (see // SERVER-9767 and SERVER-11183 for additional details, including a link to the // LLVM ticket and LLVM fix). // // Ideally, the test above would also say // || (__clang_major__ > 3) || ((__clang_major__ == 3) && (__clang_minor__ > 2)) // so that the test would still run on known good vesrions of clang; see // comments in SERVER-11183 for why that doesn't work. // // TODO: Remove this when we no longer need to support clang-3.2. We should // also consider requiring clang > 3.2 in our configure tests once XCode 5 is // ubiquitious. add< Throw< thr4 > >(); #endif add< Timer >(); add< Sleep0Ms >(); #if defined(__USE_XOPEN2K) add< Yield >(); #endif add< rlock >(); add< wlock >(); add< glockerIX > (); add< glockerIS > (); add< locker_contestedX >(); add< locker_uncontestedX >(); add< locker_contestedS >(); add< locker_uncontestedS >(); add< NotifyOne >(); add< simplemutexspeed >(); add< boostmutexspeed >(); add< boosttimed_mutexspeed >(); add< stdmutexspeed >(); add< stdtimed_mutexspeed >(); add< spinlockspeed >(); #ifdef RUNCOMPARESWAP add< casspeed >(); #endif add< CTM >(); add< CTMicros >(); add< KeyTest >(); add< Bldr >(); add< StkBldr >(); add< BSONIter >(); add< BSONGetFields1 >(); add< BSONGetFields2 >(); //add< TaskQueueTest >(); add< InsertDup >(); add< Insert1 >(); add< InsertRandom >(); add< MoreIndexes >(); add< Update1 >(); add< MoreIndexes >(); add< InsertBig >(); add< FailPointTest >(); add< FailPointTest >(); add< FailPointTest >(); add< ReturnOKStatus >(); add< ReturnNotOKStatus >(); add< CopyOKStatus >(); add< CopyNotOKStatus >(); add< MoveOKStatus >(); add< MoveNotOKStatus >(); } } } myall; }