00001
00002
00003
00004
00005
00006 #ifndef __RW_LOCK_TESTER_H_Included__
00007 #define __RW_LOCK_TESTER_H_Included__
00008
00009 #include <iostream>
00010 #include <iomanip>
00011 #include <vector>
00012 #include <string>
00013 #include <stdio.h>
00014 #include <errno.h>
00015 #include <time.h>
00016 #include "atomic_util.h"
00017 #include "system_abstraction.h"
00018
00019 using namespace std;
00020
00021 #ifdef WIN32
00022 const unsigned long kValidationThreadsNumber = 40;
00023 const unsigned long kValidationLoopsNumber = 10000;
00024 const unsigned long kPerformanceLoopsNumber = 6400000;
00025 typedef ULONG64 MILLISECONDS;
00026 typedef ULONG64 CLOCK_T;
00027 #else // #ifdef WIN32
00028 const unsigned long kValidationThreadsNumber = 40;
00029 const unsigned long kValidationLoopsNumber = 10000;
00030 const unsigned long kPerformanceLoopsNumber = 64000000;
00031
00032 typedef uint64_t MILLISECONDS;
00033 typedef uint64_t CLOCK_T;
00034 #endif // #ifdef WIN32
00035
00036 const unsigned long SLEEP_MSEC = 10;
00037
00038 const CLOCK_T CLOCKS_IN_MILLISEC = CLOCKS_PER_SEC / CLOCK_T(1000);
00039 const int kLoopYields = 1000;
00040
00041 class SyncOStream
00042 {
00043 public:
00044 SyncOStream(std::ostream * ostr = &cout) : out(ostr) {}
00045
00046 ~SyncOStream() {}
00047
00048 void lock(void) { l.lock(); }
00049 void unlock(void) { l.unlock(); }
00050 std::ostream *out;
00051 private:
00052 SystemLock l;
00053 };
00054
00055 template<class T> SyncOStream & operator<<(SyncOStream &strm, T val)
00056 {
00057 if(strm.out)
00058 {
00059 (*strm.out) << val;
00060 }
00061 return strm;
00062 }
00063
00064 class RWLockLoopTest
00065 {
00066 public:
00067 enum RWLockLoopTestMode
00068 {
00069 RWLockLoopTestRWMode = 0,
00070 RWLockLoopTestWRMode = 1
00071 };
00072
00073 RWLockLoopTest(SyncOStream &so, int loops=kPerformanceLoopsNumber) :
00074 out(so), read_thread_time(0), write_thread_time(0),
00075 total_read_thread_time(0), total_write_thread_time(0), run_count(0) {}
00076
00077 virtual ~RWLockLoopTest() {}
00078
00079 volatile void run(int loops, int rwratio=1);
00080 volatile void run_rw(int loops, int rwratio=1);
00081 volatile void run_wr(int loops, int rwratio=1);
00082
00083 volatile void run_r(int loops, int dummy=1);
00084 volatile void run_w(int loops, int dummy=1);
00085
00086 virtual volatile void run_mixed(int loops, int rwratio=1) = 0;
00087
00088 virtual volatile MILLISECONDS read_test_loop(int loops) = 0;
00089 virtual volatile MILLISECONDS write_test_loop(int loops) = 0;
00090
00091 virtual void enter_read(void) = 0;
00092 virtual void leave_read(void) = 0;
00093 virtual void enter_write(void) = 0;
00094 virtual void leave_write(void) = 0;
00095
00096 void cslock(void) { sys_lock.lock(); }
00097 void csunlock(void) { sys_lock.unlock(); }
00098
00099 virtual const char *name() const =0;
00100 MILLISECONDS get_read_thread_time(void) const { return read_thread_time; }
00101 MILLISECONDS get_write_thread_time(void) const { return write_thread_time; }
00102 MILLISECONDS get_total_read_thread_time(void) const { return total_read_thread_time; }
00103 MILLISECONDS get_total_write_thread_time(void) const { return total_write_thread_time; }
00104 MILLISECONDS get_total_test_time(void) const { return total_test_time; }
00105
00106 void mark_test_start();
00107 void mark_test_end();
00108
00109 void clear_thread_times(void) { read_thread_time = write_thread_time = 0; run_count = 0;}
00110 void clear_total_thread_time(void) { total_read_thread_time = total_write_thread_time = 0; }
00111
00112 void print_thread_times(void);
00113 void print_mixed_total(void);
00114
00115 protected:
00116 SyncOStream &out;
00117 MILLISECONDS read_thread_time;
00118 MILLISECONDS write_thread_time;
00119 MILLISECONDS total_read_thread_time;
00120 MILLISECONDS total_write_thread_time;
00121 MILLISECONDS total_test_time;
00122 SystemLock sys_lock;
00123 volatile int run_count;
00124 };
00125
00126 typedef volatile void (RWLockLoopTest::*RWLockLoopTestRunFunction)(int loops, int rwratio);
00127
00128 template<class RWL> class RWLockLoopTestImpl : public RWLockLoopTest
00129 {
00130 public:
00131 RWLockLoopTestImpl(SyncOStream &so, int loops=kPerformanceLoopsNumber, bool vfy=true) :
00132 RWLockLoopTest(so, loops), current_readers(0), current_writers(0), verify(vfy) {}
00133
00134 volatile MILLISECONDS read_test_loop(int loops);
00135 volatile MILLISECONDS write_test_loop(int loops);
00136
00137 volatile void run_mixed(int loops, int rwratio=1);
00138
00139 virtual void enter_read(void) {lock.enter_read();}
00140 virtual void leave_read(void) {lock.leave_read();}
00141 virtual void enter_write(void) {lock.enter_write();}
00142 virtual void leave_write(void) {lock.leave_write();}
00143
00144 const char *name() const { return lock.name(); }
00145
00146 private:
00147
00148
00149 RWL lock;
00150 volatile int current_readers;
00151 volatile int current_writers;
00152 const bool verify;
00153 };
00154
00155 class RWLockTester
00156 {
00157 public:
00158 RWLockTester(int loops = 1000000, int threadNum = 1, SyncOStream &os = stdSyncOStream) :
00159 repeat(loops), threads(threadNum), out(os),
00160 current_threads(0), current_active_threads(0),
00161 current_read_threads(0), current_write_threads(0)
00162 {
00163 assert(CLOCKS_PER_SEC >= CLOCK_T(1000));
00164 assert(CLOCKS_PER_SEC % CLOCK_T(1000) == 0);
00165 }
00166
00167 ~RWLockTester()
00168 {
00169 std::vector<RWLockLoopTest *>::iterator i;
00170 for(i=lock_tests.begin(); i!=lock_tests.end(); i++)
00171 {
00172 delete *i;
00173 }
00174 }
00175
00176 void add_lock_test(RWLockLoopTest *test) { lock_tests.push_back(test); }
00177 void validate(void);
00178 void run_test( int thread_num=1,
00179 int loops=kPerformanceLoopsNumber,
00180 RWLockLoopTestRunFunction tf=&RWLockLoopTest::run,
00181 int rwratio=1 );
00182
00183 void set_loops(int loops) { if(loops>0) repeat = loops; }
00184 void set_threads(int tcount) { if(tcount>0) threads = tcount; }
00185 int get_loops(int loops) { return repeat; }
00186 int get_threads(int tcount) { return threads; }
00187 SyncOStream & get_ostream(void) { return out; }
00188 void print_summary(void);
00189 void clear_summary(void);
00190
00191 std::vector<RWLockLoopTest *>::const_iterator begin() const { return lock_tests.begin(); }
00192 std::vector<RWLockLoopTest *>::const_iterator end() const { return lock_tests.end(); }
00193 const RWLockLoopTest * operator[](size_t indx) const { return lock_tests[indx]; }
00194 size_t size() const { return lock_tests.size(); }
00195
00196 private:
00197 static void * thread_function(void *);
00198 static void * thread_function_read(void *);
00199 static void * thread_function_write(void *);
00200
00201 void set_tests(int threads_num, int loops, int rwratio, RWLockLoopTestRunFunction testf)
00202 {
00203 threads = threads_num;
00204 repeat = loops/threads;
00205 rw_ratio = rwratio;
00206 testfptr = testf;
00207 }
00208
00209 void clearStartFlag(void) { startFlag = 0; }
00210 void signalStart(void) { startFlag = 1; }
00211 void waitForStart(void) { while(startFlag == 0) thread_yield(); }
00212
00213 volatile int startFlag;
00214 int repeat;
00215 int threads;
00216 int rw_ratio;
00217 RWLockLoopTestRunFunction testfptr;
00218 RWLockLoopTest * curr;
00219
00220 std::vector<RWLockLoopTest *> lock_tests;
00221
00222 volatile int current_threads;
00223 volatile int current_active_threads;
00224 volatile int current_read_threads;
00225 volatile int current_write_threads;
00226 volatile int current_rw_threads;
00227 volatile int current_wr_threads;
00228 SyncOStream &out;
00229
00230 static SyncOStream stdSyncOStream;
00231 };
00232
00233
00234 struct RWLockGraphData
00235 {
00236 std::string name;
00237 std::vector<MILLISECONDS> read;
00238 std::vector<MILLISECONDS> write;
00239 std::vector<MILLISECONDS> total;
00240 void clear() { name = ""; read.clear(); write.clear(); total.clear();}
00241 void resize(size_t new_sz, MILLISECONDS initval=0)
00242 {
00243 read.resize(new_sz, initval);
00244 write.resize(new_sz, initval);
00245 total.resize(new_sz, initval);
00246 }
00247 };
00248
00249 class GraphData
00250 {
00251 std::vector<int> threads;
00252 std::vector<RWLockGraphData> lock_info;
00253 public:
00254 GraphData() {}
00255 void init(const RWLockTester &tester, const int *th_counts, size_t thread_count_len);
00256 int add_results(const RWLockTester &tester, size_t thread_count_num);
00257
00258 template <class F> int print(F f, const char *file, const char *title=NULL) const;
00259
00260 inline static MILLISECONDS get_read(const RWLockGraphData &d, size_t indx) { return d.read[indx]; }
00261 inline static MILLISECONDS get_write(const RWLockGraphData &d, size_t indx) { return d.write[indx]; }
00262 inline static MILLISECONDS get_sum(const RWLockGraphData &d, size_t indx) { return d.read[indx] + d.write[indx]; }
00263 inline static MILLISECONDS get_total(const RWLockGraphData &d, size_t indx) { return d.total[indx]; }
00264
00265 int print_read(const char *file, const char *title=NULL) const;
00266 int print_write(const char *file, const char *title=NULL) const;
00267 int print_summary(const char *file, const char *title=NULL) const;
00268 int print_total_time(const char *file, const char *title=NULL) const;
00269 };
00270
00271 template<class RWL> volatile inline MILLISECONDS RWLockLoopTestImpl<RWL>::read_test_loop(int loops)
00272 {
00273 int yield_point = loops / kLoopYields;
00274 CLOCK_T start = CLOCK_T(clock());
00275 for(int i=0; i<loops; i++)
00276 {
00277 lock.enter_read();
00278
00279 int tmp = current_writers;
00280
00281 atomic_inc(¤t_readers);
00282
00283 if((i % yield_point) == 0) thread_yield();
00284
00285
00286
00287 if(verify && tmp != 0)
00288 {
00289 out.lock();
00290 out << "\n\nRW lock " << name() << " current_writers != 0 : " << tmp << "\n";
00291 out.unlock();
00292 exit(-1);
00293 }
00294
00295 atomic_dec(¤t_readers);
00296
00297 lock.leave_read();
00298 }
00299 CLOCK_T finish = CLOCK_T(clock());
00300 if(finish < start)
00301 {
00302 cerr << "!!! clock round up: start=" << start << "; end=" << finish << "; loops=" << loops << endl;
00303 assert(!"clock round up");
00304 }
00305 return (finish - start)/CLOCKS_IN_MILLISEC;
00306 }
00307
00308 template<class RWL> volatile inline MILLISECONDS RWLockLoopTestImpl<RWL>::write_test_loop(int loops)
00309 {
00310 int yield_point = loops / kLoopYields;
00311 CLOCK_T start = CLOCK_T(clock());
00312 for(int i=0; i<loops; i++)
00313 {
00314 lock.enter_write();
00315
00316 int tmp = current_readers;
00317
00318 if((i % yield_point) == 0) thread_yield();
00319
00320 if(verify && tmp != 0)
00321 {
00322 out.lock();
00323 out << "\n\nRW lock " << name() << " current_readers != 0 : " << tmp << "\n";
00324 out.unlock();
00325 exit(-12);
00326 }
00327
00328 atomic_inc(¤t_writers);
00329
00330
00331
00332
00333 if(verify && current_writers != 1)
00334 {
00335 out.lock();
00336 out << "\n\nRW lock " << name() << " current_writers != 1 : " << current_writers << "\n";
00337 out.unlock();
00338 exit(-2);
00339 }
00340
00341 atomic_dec(¤t_writers);
00342
00343 lock.leave_write();
00344 }
00345 CLOCK_T finish = CLOCK_T(clock());
00346 if(finish < start)
00347 {
00348 cerr << "!!! clock round up: start=" << start << "; end=" << finish << "; loops=" << loops << endl;
00349 assert(!"clock round up");
00350 }
00351 return (finish - start)/CLOCKS_IN_MILLISEC;
00352 }
00353
00354 template<class RWL> volatile inline void RWLockLoopTestImpl<RWL>::run_mixed(int loops, int rwratio)
00355 {
00356 MILLISECONDS thread_time;
00357 int yield_point = loops / kLoopYields;
00358 CLOCK_T start = CLOCK_T(clock());
00359 for(int i=0; i<loops; i++)
00360 {
00361 for(int j=0; j<rwratio; j++)
00362 {
00363 lock.enter_read();
00364
00365 int tmp = current_writers;
00366 atomic_inc(¤t_readers);
00367 if(verify && tmp != 0)
00368 {
00369 out.lock();
00370 out << "\n\nRW lock " << name() << " current_writers != 0 : " << tmp << "\n";
00371 out.unlock();
00372 exit(-1);
00373 }
00374
00375
00376 atomic_dec(¤t_readers);
00377
00378 lock.leave_read();
00379 }
00380 lock.enter_write();
00381 int tmp = current_readers;
00382 if(verify && tmp != 0)
00383 {
00384 out.lock();
00385 out << "\n\nRW lock " << name() << " current_readers != 0 : " << tmp << "\n";
00386 out.unlock();
00387 exit(-12);
00388 }
00389 atomic_inc(¤t_writers);
00390 if(verify && current_writers != 1)
00391 {
00392 out.lock();
00393 out << "\n\nRW lock " << name() << " current_writers != 1 : " << current_writers << "\n";
00394 out.unlock();
00395 exit(-2);
00396 }
00397
00398 if((i % yield_point) == 0) thread_yield();
00399
00400
00401 atomic_dec(¤t_writers);
00402 lock.leave_write();
00403 }
00404 CLOCK_T finish = CLOCK_T(clock());
00405
00406 if(finish < start)
00407 {
00408 cerr << "!!! clock round up: start=" << start << "; end=" << finish << "; loops=" << loops << endl;
00409 assert(!"clock round up");
00410 }
00411
00412 thread_time = (finish - start)/CLOCKS_IN_MILLISEC;
00413
00414 cslock();
00415 read_thread_time += thread_time;
00416 total_read_thread_time += thread_time;
00417 csunlock();
00418 }
00419
00420 template <class F> inline int GraphData::print(F binf, const char *file, const char *title) const
00421 {
00422 FILE *f = fopen(file, "w");
00423 if(f == NULL) return errno;
00424 if(title)
00425 fprintf(f, "# %s\n", title);
00426 fprintf(f, "%32.32s \t ", "threads:");
00427 for(std::vector<int>::const_iterator i=threads.begin(); i!=threads.end(); i++)
00428 {
00429 fprintf(f, "%16d \t ", *i);
00430 }
00431 fprintf(f, "\n");
00432 for(std::vector<RWLockGraphData>::const_iterator curr=lock_info.begin(); curr!=lock_info.end(); curr++)
00433 {
00434 fprintf(f, "\"%-30.30s\" \t ", curr->name.c_str());
00435 for(size_t ii=0; ii<threads.size(); ii++)
00436 fprintf(f, "%16llu \t ", (unsigned long long)binf(*curr, ii));
00437 fprintf(f, "\n");
00438 }
00439
00440 fclose(f);
00441 return 0;
00442 }
00443 inline int GraphData::print_read(const char *file, const char *title) const
00444 { return print(get_read, file, title); }
00445
00446 inline int GraphData::print_write(const char *file, const char *title) const
00447 { return print(get_write, file, title); }
00448
00449 inline int GraphData::print_summary(const char *file, const char *title) const
00450 { return print(get_sum, file, title); }
00451
00452 inline int GraphData::print_total_time(const char *file, const char *title) const
00453 { return print(get_total, file, title); }
00454
00455 #endif // __RW_LOCK_TESTER_H_Included__
00456