Files
mongo/dbtests/perftests.cpp

381 lines
12 KiB
C++
Raw Normal View History

/** @file perftests.cpp.cpp : unit tests relating to performance
2011-01-04 00:40:41 -05:00
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 <http://www.gnu.org/licenses/>.
*/
#include "pch.h"
#include "../db/query.h"
#include "../db/db.h"
#include "../db/instance.h"
#include "../db/json.h"
#include "../db/lasterror.h"
#include "../db/update.h"
2010-12-22 09:44:32 -05:00
#include "../db/taskqueue.h"
#include "../util/timer.h"
#include "dbtests.h"
2010-12-26 11:07:06 -05:00
#include "../db/dur_stats.h"
#include "../util/checksum.h"
namespace PerfTests {
typedef DBDirectClient DBClientType;
//typedef DBClientConnection DBClientType;
class ClientBase {
public:
// NOTE: Not bothering to backup the old error record.
ClientBase() {
//_client.connect("localhost");
mongo::lastError.reset( new LastError() );
}
virtual ~ClientBase() {
2010-12-21 14:20:05 -05:00
//mongo::lastError.release();
}
protected:
static void insert( const char *ns, BSONObj o ) {
2010-12-21 09:49:29 -05:00
_client.insert( ns, o );
}
static void update( const char *ns, BSONObj q, BSONObj o, bool upsert = 0 ) {
2010-12-21 09:49:29 -05:00
_client.update( ns, Query( q ), o, upsert );
}
static bool error() {
2010-12-21 09:49:29 -05:00
return !_client.getPrevError().getField( "err" ).isNull();
}
DBClientBase &client() const { return _client; }
private:
static DBClientType _client;
};
DBClientType ClientBase::_client;
class Checksum {
public:
void run() {
{
// the checksum code assumes 'standard' rollover on addition overflows. let's check that:
unsigned long long x = 0xffffffffffffffffUL;
ASSERT( x+2 == 1 );
}
unsigned sz = 1024 * 1024 * 100 + 3;
void *p = malloc(sz);
mongo::Checksum last;
for( int i = 0; i < 4; i++ ) {
Timer t;
mongo::Checksum c;
c.gen(p, sz);
cout << "checksum " << t.millis() << "ms" << endl;
ASSERT( i == 0 || c == last );
last = c;
}
{
mongo::Checksum c;
c.gen(p, sz-1);
ASSERT( c != last );
((char *&)p)[0]++; // check same data, different order, doesn't give same checksum
((char *&)p)[1]--;
c.gen(p, sz);
ASSERT( c != last );
((char *&)p)[1]++; // check same data, different order, doesn't give same checksum (different longwords case)
((char *&)p)[8]--;
c.gen(p, sz);
ASSERT( c != last );
}
free(p);
}
};
2010-12-21 12:06:53 -05:00
// todo: use a couple threads. not a very good test yet.
2011-01-04 13:08:57 -05:00
class TaskQueueTest {
2010-12-21 12:06:53 -05:00
static int tot;
2011-01-04 00:40:41 -05:00
struct V {
2010-12-21 12:06:53 -05:00
int val;
static void go(const V &v) { tot += v.val; }
};
public:
2011-01-04 00:40:41 -05:00
void run() {
2010-12-21 12:06:53 -05:00
tot = 0;
2010-12-22 09:44:32 -05:00
TaskQueue<V> d;
2010-12-21 12:06:53 -05:00
int x = 0;
for( int i = 0; i < 100; i++ ) {
if( i % 30 == 0 )
d.invoke();
x += i;
writelock lk;
V v;
v.val = i;
d.defer(v);
}
d.invoke();
assert( x == tot );
}
};
2011-01-04 13:08:57 -05:00
int TaskQueueTest::tot;
2010-12-21 12:06:53 -05:00
2011-01-04 00:40:41 -05:00
class CappedTest : public ClientBase {
2010-12-22 12:29:30 -05:00
};
2011-01-04 00:40:41 -05:00
class B : public ClientBase {
2010-12-21 09:49:29 -05:00
string _ns;
protected:
2010-12-21 09:49:29 -05:00
const char *ns() { return _ns.c_str(); }
virtual void prep() = 0;
2010-12-21 14:20:05 -05:00
virtual void timed() = 0;
2010-12-21 14:20:05 -05:00
// optional 2nd test phase to be timed separately
// return name of it
virtual const char * timed2() { return 0; }
virtual void post() { }
virtual string name() = 0;
virtual unsigned long long expectation() = 0;
virtual int howLongMillis() { return 5000; } // how long to run test
public:
2011-01-04 00:40:41 -05:00
void say(unsigned long long n, int ms, string s) {
//cout << setw(36) << left << s << ' ' << right << setw(7) << n*1000/ms << "/sec " << setw(4) << ms << "ms" << endl;
cout << "stats\t" << s << '\t' << n*1000/ms << "\t" << ms << "ms\t"
<< dur::stats.curr->_asCSV() << endl;
2010-12-26 11:07:06 -05:00
}
2011-01-04 00:40:41 -05:00
void run() {
2010-12-21 09:49:29 -05:00
_ns = string("perftest.") + name();
client().dropCollection(ns());
prep();
int hlm = howLongMillis();
2010-12-27 11:29:16 -05:00
dur::stats._intervalMicros = 0; // no auto rotate
2010-12-27 11:32:37 -05:00
dur::stats.curr->reset();
Timer t;
unsigned long long n = 0;
const unsigned Batch = 50;
2011-01-04 00:40:41 -05:00
do {
unsigned i;
for( i = 0; i < Batch; i++ )
timed();
n += i;
2011-01-04 00:40:41 -05:00
}
while( t.millis() < hlm );
2010-12-22 23:53:38 -05:00
client().getLastError(); // block until all ops are finished
int ms = t.millis();
2010-12-26 11:07:06 -05:00
say(n, ms, name());
2011-01-04 00:40:41 -05:00
if( n < expectation() ) {
2010-12-26 12:51:52 -05:00
cout << "\ntest " << name() << " seems slow n:" << n << " ops/sec but expect greater than:" << expectation() << endl;
cout << endl;
}
2010-12-21 14:20:05 -05:00
{
const char *test2name = timed2();
if( test2name ) {
2010-12-27 11:32:37 -05:00
dur::stats.curr->reset();
2010-12-21 14:20:05 -05:00
Timer t;
unsigned long long n = 0;
2011-01-04 00:40:41 -05:00
while( 1 ) {
2010-12-21 14:20:05 -05:00
unsigned i;
for( i = 0; i < Batch; i++ )
2010-12-21 14:20:05 -05:00
timed2();
n += i;
2011-01-04 00:40:41 -05:00
if( t.millis() > hlm )
2010-12-21 14:20:05 -05:00
break;
}
int ms = t.millis();
2010-12-26 11:07:06 -05:00
say(n, ms, test2name);
2010-12-21 14:20:05 -05:00
}
}
}
};
2011-01-04 00:40:41 -05:00
class InsertDup : public B {
const BSONObj o;
public:
2010-12-20 16:55:16 -05:00
InsertDup() : o( BSON("_id" << 1) ) { } // dup keys
2011-01-04 00:40:41 -05:00
string name() {
return "insert duplicate _ids";
}
2011-01-04 00:40:41 -05:00
void prep() {
client().insert( ns(), o );
}
void timed() {
client().insert( ns(), o );
}
void post() {
assert( client().count(ns()) == 1 );
}
unsigned long long expectation() { return 1000; }
};
2011-01-04 00:40:41 -05:00
class Insert1 : public InsertDup {
2010-12-20 17:03:10 -05:00
const BSONObj x;
public:
2010-12-20 17:03:10 -05:00
Insert1() : x( BSON("x" << 99) ) { }
string name() { return "insert simple"; }
void timed() {
2010-12-20 17:03:10 -05:00
client().insert( ns(), x );
2010-12-20 16:55:16 -05:00
}
void post() {
2010-12-20 17:03:10 -05:00
assert( client().count(ns()) > 100 );
}
unsigned long long expectation() { return 1000; }
};
2010-12-21 17:30:46 -05:00
2011-01-04 00:40:41 -05:00
class InsertBig : public InsertDup {
2010-12-26 10:37:37 -05:00
BSONObj x;
2011-01-04 00:40:41 -05:00
virtual int howLongMillis() {
if( sizeof(void*) == 4 )
return 1000; // could exceed mmapping if run too long, as this function adds a lot fasta
return 5000;
}
2010-12-26 10:37:37 -05:00
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 );
}
unsigned long long expectation() { return 20; }
};
2011-01-04 00:40:41 -05:00
class InsertRandom : public B {
2010-12-21 17:30:46 -05:00
public:
string name() { return "random inserts"; }
2011-01-04 00:40:41 -05:00
void prep() {
2010-12-21 17:30:46 -05:00
client().insert( ns(), BSONObj() );
client().ensureIndex(ns(), BSON("x"<<1));
}
void timed() {
int x = rand();
BSONObj y = BSON("x" << x << "y" << rand() << "z" << 33);
client().insert(ns(), y);
}
void post() {
}
unsigned long long expectation() { return 1000; }
};
2011-01-04 00:40:41 -05:00
/** upserts about 32k records and then keeps updating them
2010-12-21 09:49:29 -05:00
2 indexes
*/
2011-01-04 00:40:41 -05:00
class Update1 : public B {
2010-12-21 02:52:18 -05:00
public:
2011-01-04 00:40:41 -05:00
static int rand() {
2010-12-22 09:54:17 -05:00
return std::rand() & 0x7fff;
}
virtual string name() { return "random upserts"; }
2011-01-04 00:40:41 -05:00
void prep() {
2010-12-21 02:52:18 -05:00
client().insert( ns(), BSONObj() );
client().ensureIndex(ns(), BSON("x"<<1));
}
void timed() {
int x = rand();
BSONObj q = BSON("x" << x);
2010-12-21 15:37:52 -05:00
BSONObj y = BSON("x" << x << "y" << rand() << "z" << 33);
2010-12-21 02:52:18 -05:00
client().update(ns(), q, y, /*upsert*/true);
}
2010-12-21 14:20:05 -05:00
const char * timed2() {
static BSONObj I = BSON( "$inc" << BSON( "y" << 1 ) );
// test some $inc's
int x = rand();
BSONObj q = BSON("x" << x);
client().update(ns(), q, I);
static string s = name()+" inc";
return s.c_str();
2010-12-21 14:20:05 -05:00
}
2010-12-21 02:52:18 -05:00
void post() {
}
unsigned long long expectation() { return 1000; }
};
2011-01-04 00:40:41 -05:00
2010-12-21 17:30:46 -05:00
template <typename T>
2011-01-04 00:40:41 -05:00
class MoreIndexes : public T {
2010-12-21 09:49:29 -05:00
public:
2010-12-21 17:30:46 -05:00
string name() { return T::name() + " with more indexes"; }
2011-01-04 00:40:41 -05:00
void prep() {
2010-12-21 17:30:46 -05:00
T::prep();
this->client().ensureIndex(this->ns(), BSON("y"<<1));
this->client().ensureIndex(this->ns(), BSON("z"<<1));
2010-12-21 09:49:29 -05:00
}
};
2011-01-06 09:16:00 -05:00
void t() {
for( int i = 0; i < 20; i++ ) {
sleepmillis(21);
string fn = "/tmp/t1";
MongoMMF f;
unsigned long long len = 1 * 1024 * 1024;
assert( f.create(fn, len, /*sequential*/rand()%2==0) );
2011-01-08 13:22:45 -05:00
{
2011-01-06 09:16:00 -05:00
char *p = (char *) f.getView();
assert(p);
// write something to the private view as a test
strcpy(p, "hello");
}
if( cmdLine.dur ) {
char *w = (char *) f.view_write();
strcpy(w + 6, "world");
}
MongoFileFinder ff;
ASSERT( ff.findByPath(fn) );
}
}
class All : public Suite {
public:
2011-01-06 09:16:00 -05:00
All() : Suite( "perf" )
{
}
2011-01-06 09:16:00 -05:00
~All() {
}
Result * run( const string& filter ) {
boost::thread a(t);
Result * res = Suite::run(filter);
a.join();
return res;
}
2011-01-04 00:40:41 -05:00
void setupTests() {
add< Checksum >();
2011-01-04 13:08:57 -05:00
add< TaskQueueTest >();
cout << "stats\t"
<< "test\trps\ttime\t"
<< dur::stats.curr->_CSVHeader() << endl;
add< InsertDup >();
2010-12-20 17:03:10 -05:00
add< Insert1 >();
2010-12-21 17:30:46 -05:00
add< InsertRandom >();
add< MoreIndexes<InsertRandom> >();
2010-12-21 02:52:18 -05:00
add< Update1 >();
2010-12-21 17:30:46 -05:00
add< MoreIndexes<Update1> >();
2010-12-26 10:37:37 -05:00
add< InsertBig >();
}
} myall;
}