#include #include #include #include #include #include "MemCache.hh" using namespace std; using namespace Answers; using namespace boost; using namespace boost::random; boost::mt19937 rng; boost::uniform_real<> uni_dist(0,1); boost::variate_generator > rand_real(rng, uni_dist); struct CacheTestSet { unsigned long num_of_keys; string value; time_t stop_time; unsigned long average_sleep_useconds; unsigned long count_reads, count_writes; unsigned long total_waiting_read_time_useconds, total_waiting_write_time_useconds; mutex m; }; int a = 0; class CacheTester { public: CacheTester( shared_ptr test_set ) : test_set( test_set ), cache() {} void run() { while ( !test_end() ) { process_key(); update(); if ( test_end() ) break; sleep(); } } private: shared_ptr test_set; MemCache cache; boost::thread *thrd; unsigned long read_time, write_time; void process_key() { string key = lexical_cast( (long) ( rand_real() * test_set->num_of_keys ) ); string value; struct timeval tv_begin, tv_end; gettimeofday( &tv_begin, 0 ); cache.get( key, value ); gettimeofday( &tv_end, 0 ); read_time = (tv_end.tv_sec - tv_begin.tv_sec) * 1000000L + tv_end.tv_usec - tv_begin.tv_usec; if ( rand_real() < 0.1 ) { gettimeofday( &tv_begin, 0 ); cache.set( key, test_set->value, 0, 0 ); gettimeofday( &tv_end, 0 ); write_time = (tv_end.tv_sec - tv_begin.tv_sec) * 1000000L + tv_end.tv_usec - tv_begin.tv_usec; } else { write_time = 0; } } bool test_end() { return ( time(0) >= test_set->stop_time ); } void sleep() { if ( test_set->average_sleep_useconds <= 0 ) return; unsigned long sl = (unsigned long) ( rand_real() * 2 * test_set->average_sleep_useconds ); usleep( sl ); } void update() { mutex::scoped_lock( test_set->m ); if ( read_time > 0 ) { test_set->count_reads ++; test_set->total_waiting_read_time_useconds += read_time; } if ( write_time > 0 ) { test_set->count_writes ++; test_set->total_waiting_write_time_useconds += write_time; } } }; class Runner { public: Runner( CacheTester *ct ) : ct(ct) {} Runner() : ct(0) {} void operator()() { ct->run(); } void set( CacheTester *_ct ) { ct = _ct; } private: CacheTester *ct; }; // // Start up threads int main( int argc, char *argv[] ) { try { // help if ( argc != 7 || argv[1] == "-h" || argv[1] == "--help" ) { cout << argv[0] << " <# of simultaneous threads>" << " <# of seconds running>" << " " << " <# of different keys to use>" << " " << " " << endl; return 1; } // get arguments int seconds_to_run = lexical_cast( argv[2] ); shared_ptr test_set( new CacheTestSet ); int num_threads = lexical_cast( argv[1] ); test_set->stop_time = time(0) + seconds_to_run; test_set->average_sleep_useconds = lexical_cast( argv[3] ); test_set->num_of_keys = lexical_cast( argv[4] ); test_set->value = string( lexical_cast( argv[5] ), 'a' ); cout << "Value being used (" << lexical_cast( argv[5] ) << ") : " << test_set->value << endl; int prefill = lexical_cast( argv[6] ); // prefill if requested if ( prefill ) { MemCache cache; struct timeval tv_begin, tv_end; struct timespec pause = { 0, 1000 }; gettimeofday( &tv_begin, 0 ); for ( unsigned i = 0; i < test_set->num_of_keys; i++ ) { string key = lexical_cast( (long) ( rand_real() * test_set->num_of_keys ) ); cache.set( key, test_set->value, 0, 0 ); // nanosleep( &pause, 0 ); } gettimeofday( &tv_end, 0 ); unsigned long write_time = (tv_end.tv_sec - tv_begin.tv_sec) * 1000000L + tv_end.tv_usec - tv_begin.tv_usec; cout << "Cache prefilled, " << (double) test_set->num_of_keys / (double) write_time * 1e6 << " writes per second" << endl; gettimeofday( &tv_begin, 0 ); unsigned failed = 0; for ( unsigned i = 0; i < test_set->num_of_keys; i++ ) { string key = lexical_cast( (long) ( rand_real() * test_set->num_of_keys ) ); string v; if ( ! cache.get( key,v ) ) failed ++; else if ( v != test_set->value ) cerr << "returned wrong value!!!" << endl; // nanosleep( &pause, 0 ); } gettimeofday( &tv_end, 0 ); unsigned long read_time = (tv_end.tv_sec - tv_begin.tv_sec) * 1000000L + tv_end.tv_usec - tv_begin.tv_usec; cout << "Cache prefill checked, " << (double) test_set->num_of_keys / (double) read_time * 1e6 << " reads per second, " << failed << " failed" << endl; return 0; } CacheTester *testers[num_threads]; int i; for ( i = 0 ; i < num_threads; i ++ ) testers[i] = new CacheTester( test_set ); Runner runners[ num_threads ]; for ( i = 0 ; i < num_threads; i ++ ) runners[i].set( testers[i] ); thread *threads[ num_threads ]; try { for ( i = 0 ; i < num_threads; i ++ ) threads[i] = new thread( runners[i] ); } catch ( ... ) { cerr << "Threads created: " << i << endl; throw; } for ( i = 0 ; i < num_threads; i ++ ) { threads[i]->join(); delete threads[i]; threads[i] = 0; } for ( i = 0 ; i < num_threads; i ++ ) { delete testers[i]; testers[i] = 0; } // print statistics cout << "Rate of read requests: " << (double)test_set->count_reads / (double)seconds_to_run << " reqs/s, " << test_set->count_reads << " total" << endl; cout << "Rate of write requests: " << (double)test_set->count_writes / (double)seconds_to_run << " reqs/s," << test_set->count_writes << " total" << endl; cout << "Total time passed waiting for requests (useconds): " << test_set->total_waiting_read_time_useconds << endl; cout << "Average reading time (useconds/request): " << (double) test_set->total_waiting_read_time_useconds / (double)test_set->count_reads << endl; cout << "Average writing time (useconds/request): " << (double) test_set->total_waiting_write_time_useconds / (double)test_set->count_writes << endl; return 0; } catch (exception &e) { cerr << "Exception: " << e.what() << endl; return 1; } }