|
reSIProcate/rutil
9694
|
00001 00006 #include <cstdlib> 00007 #include <cmath> // for sqrt 00008 #include <iostream> 00009 #include <vector> 00010 #include <memory> // for auto_ptr 00011 #include <set> 00012 00013 // #include "rutil/Data.hxx" 00014 #include "rutil/Random.hxx" 00015 #include "rutil/Time.hxx" // for ResipClock 00016 #include "rutil/Lock.hxx" 00017 00018 #define RANDINT_PER_CYCLE (1000000) 00019 #define RANDSEQ_PER_CYCLE (1000) 00020 00021 using namespace std; 00022 using namespace resip; 00023 00024 class Barrier { 00025 public: 00026 Barrier(int numThreads) 00027 : mCurId(0), mHaveCnt(0), mWantCnt(numThreads) { }; 00028 void sync(int id, bool isMaster=false); 00029 protected: 00030 Condition mCond; 00031 Mutex mMutex; 00032 volatile int mCurId; 00033 volatile int mHaveCnt; 00034 int mWantCnt; 00035 00036 public: 00037 static volatile int sPreWaitCnt; 00038 static volatile int sPostWaitCnt; 00039 }; 00040 00041 volatile int Barrier::sPreWaitCnt = 0; 00042 volatile int Barrier::sPostWaitCnt = 0; 00043 00044 void 00045 Barrier::sync(int id, bool isMaster) 00046 { 00047 Lock datalock(mMutex); 00048 if ( isMaster ) 00049 { 00050 mHaveCnt = 0; 00051 mCurId = id; 00052 mCond.broadcast(); 00053 } 00054 else 00055 { 00056 while ( mCurId != id ) 00057 { 00058 ++sPreWaitCnt; 00059 mCond.wait(mMutex); 00060 } 00061 ++mHaveCnt; 00062 mCond.broadcast(); 00063 } 00064 while ( mCurId==id && mHaveCnt < mWantCnt ) 00065 { 00066 ++sPostWaitCnt; 00067 mCond.wait(mMutex); 00068 } 00069 } 00070 00071 class TestDummyThread : public ThreadIf 00072 { 00073 public: 00074 TestDummyThread() { }; 00075 ~TestDummyThread() { }; 00076 00077 virtual void thread() { } 00078 }; 00079 00080 typedef std::set<Data> DataSet; 00081 00082 class TestRandomThread : public ThreadIf 00083 { 00084 public: 00085 TestRandomThread(int runs, Barrier& barrier, int storeBytes) 00086 : mNumRuns(runs), mBarrier(barrier), mStoreBytes(storeBytes), 00087 mDupCnt(0) 00088 { }; 00089 ~TestRandomThread() { }; 00090 00091 virtual void thread(); 00092 static void makeRandoms(int numCycles); 00093 void makeAndStoreRandoms(int numCycles); 00094 const DataSet& getRandoms() const { return mRandoms; }; 00095 int getDupCnt() const { return mDupCnt; } 00096 00097 protected: 00098 int mNumRuns; 00099 Barrier& mBarrier; 00100 int mStoreBytes; 00101 DataSet mRandoms; 00102 int mDupCnt; 00103 }; 00104 00105 00106 void 00107 TestRandomThread::makeRandoms(int numCycles) 00108 { 00109 int idx, cidx; 00110 for (cidx=0; cidx < numCycles; cidx++) 00111 { 00112 for (idx = 0; idx < RANDINT_PER_CYCLE; idx++) 00113 { 00114 int val = Random::getRandom(); 00115 (void)val; 00116 } 00117 } 00118 } 00119 00120 void 00121 TestRandomThread::makeAndStoreRandoms(int numCycles) 00122 { 00123 int idx, cidx; 00124 for (cidx=0; cidx < numCycles; cidx++) 00125 { 00126 for (idx = 0; idx < RANDSEQ_PER_CYCLE; idx++) 00127 { 00128 Data foo = Random::getRandomHex(mStoreBytes); 00129 if (mRandoms.insert(foo).second == false ) 00130 { 00131 ++mDupCnt; 00132 // std::cerr << "RandomHex produced intra-thread duplicate after " 00133 // << idx << " calls."<<std::endl; 00134 } 00135 } 00136 } 00137 } 00138 00139 00140 void 00141 TestRandomThread::thread() 00142 { 00143 // do once to get over initialization 00144 Random::getRandom(); 00145 // std::cerr << "Thread initialized." << std::endl; 00146 00147 mBarrier.sync(1); 00148 00149 if (mStoreBytes == 0 ) 00150 { 00151 makeRandoms(mNumRuns); 00152 } 00153 else 00154 { 00155 makeAndStoreRandoms(mNumRuns); 00156 } 00157 00158 mBarrier.sync(2); 00159 } 00160 00161 static UInt64 00162 doSingleTest(int numCycles) 00163 { 00164 UInt64 startUs = ResipClock::getTimeMicroSec(); 00165 TestRandomThread::makeRandoms(numCycles); 00166 UInt64 doneUs = ResipClock::getTimeMicroSec(); 00167 return doneUs - startUs; 00168 } 00169 00170 static int 00171 mergeCheckRandoms(DataSet& all, const DataSet& more) 00172 { 00173 int dupCnt = 0; 00174 DataSet::const_iterator it = more.begin(); 00175 // DataSet::iterator at = all.begin(); 00176 for ( ; it != more.end(); ++it) 00177 { 00178 // STL is unbelieable: this form isn't supported: 00179 // std::pair<DataSet::iterator,bool> res = all.insert( at, *it); 00180 // So we have to do this NlgN style: 00181 std::pair<DataSet::iterator,bool> res = all.insert( *it); 00182 // at = res->first; 00183 if ( res.second == false ) 00184 { 00185 ++dupCnt; 00186 // std::cerr << "Inter-thread duplicate found." << std::endl; 00187 } 00188 } 00189 return dupCnt; 00190 } 00191 00192 static UInt64 00193 doThreadedTest(int numCycles, int numThreads, int storeBytes) 00194 { 00195 std::vector<TestRandomThread*> threadList; 00196 Barrier bar(numThreads); 00197 int pidx; 00198 for (pidx=0; pidx < numThreads; pidx++) 00199 { 00200 TestRandomThread* rth = new TestRandomThread(numCycles,bar, storeBytes); 00201 rth->run(); 00202 threadList.push_back(rth); 00203 } 00204 // std::cerr << "Threads started." << std::endl; 00205 00206 00207 bar.sync(1, true); 00208 00209 UInt64 startUs = ResipClock::getTimeMicroSec(); 00210 // TestRandomThread::makeRandoms(numCycles); 00211 00212 bar.sync(2, true); 00213 00214 UInt64 doneUs = ResipClock::getTimeMicroSec(); 00215 // std::cerr << "Threads finished." 00216 // << " (barrier pre="<<Barrier::sPreWaitCnt 00217 // << " post="<<Barrier::sPostWaitCnt 00218 // <<")" << std::endl; 00219 00220 DataSet allRandoms; 00221 int intraDupCnt = 0; 00222 int interDupCnt = 0; 00223 for (pidx=0; pidx < numThreads; pidx++) 00224 { 00225 TestRandomThread* rth = threadList[pidx]; 00226 if ( storeBytes > 0 ) 00227 { 00228 intraDupCnt += rth->getDupCnt(); 00229 interDupCnt += mergeCheckRandoms(allRandoms, rth->getRandoms()); 00230 } 00231 rth->join(); 00232 delete rth; 00233 } 00234 if ( storeBytes > 0 ) 00235 { 00236 std::cout << "Found "<<interDupCnt<< " inter-thread and " 00237 <<intraDupCnt<<" intra-thread duplicates." << std::endl; 00238 } 00239 return doneUs - startUs; 00240 } 00241 00242 static void 00243 doVariationTest(int numCycles, int numThreads, int numPass, int storeBytes) 00244 { 00245 UInt64 msMin = 0, msMax = 0; 00246 UInt64 msSum = 0; 00247 UInt64 msSumSq = 0; 00248 int passIdx=0; 00249 for (passIdx=0; passIdx < numPass; passIdx++) 00250 { 00251 UInt64 usTot = numThreads<=0 00252 ? doSingleTest(numCycles) 00253 : doThreadedTest(numCycles, numThreads, storeBytes); 00254 UInt64 usPerCycle = usTot/numCycles; 00255 #if 0 00256 std::cerr << numCycles << " cycles/thread (1M plain 32-bit ints)" 00257 << " with " << numThreads << " threads" 00258 << " took " << usTot<<"us (" << usPerCycle << "us/cycle)" 00259 << std::endl; 00260 #endif 00261 UInt64 msPerCycle = usPerCycle/1000; 00262 msSum += msPerCycle; 00263 msSumSq += msPerCycle*msPerCycle; 00264 if ( msPerCycle < msMin || passIdx==0 ) 00265 msMin = msPerCycle; 00266 if ( msPerCycle > msMax || passIdx==0 ) 00267 msMax = msPerCycle; 00268 } 00269 double msAvg = msSum/(numPass+0.0); 00270 double msVar = msSumSq/(numPass+0.0) - msAvg*msAvg; 00271 double msStd = sqrt(msVar); 00272 double msStdPct = msStd/msAvg*100; 00273 fprintf(stderr,"RESULT:plain:%s:cycles=%d,threads=%d,passes=%d:min=%d,avg=%d,max=%d,std=%.1f(%.1f%%)[ms/cycle]\n", 00274 Random::getImplName(), 00275 numCycles, numThreads, numPass, 00276 (int)msMin,(int)msAvg,(int)msMax, msStd, msStdPct); 00277 } 00278 00279 00280 int main(int argc, char** argv) 00281 { 00282 bool doSweep = true; 00283 int numCycles = 2; 00284 int numThreads = 3; 00285 int numPass = 10; 00286 int storeBytes = 0; 00287 00288 { 00289 doSweep = false; 00290 if(argc >= 2) 00291 numCycles = atoi(argv[1]); 00292 if(argc >= 3) 00293 numThreads = atoi(argv[2]); 00294 if(argc >= 4) 00295 numPass = atoi(argv[3]); 00296 if(argc >= 5) 00297 storeBytes = atoi(argv[4]); 00298 } 00299 00300 if (numCycles <= 0 || numThreads < -1 || numPass < 1) 00301 { 00302 std::cerr 00303 << "usage: testRandomThread [numCycles numThreads numPasses storeBytes]" << std::endl 00304 << "numCycles>0 is number of cycles to run in each thread." << std::endl 00305 << " each cycle is one million random 32bit integers" << std::endl 00306 << " each cycle is one thousand random sequences when storing (see below) " << std::endl 00307 << "numThreads>=-1 is number of threads to run " << std::endl 00308 << " -1 ==> work in main thread only" << std::endl 00309 << " 0 ==> work in main thread, plus dummy thread doing nothing" << std::endl 00310 << " 1 ==> single working thread, plus dummy & main doing nothing" << std::endl 00311 << " N ==> this many working threads, plus dummy & main doing nothing" << std::endl 00312 << "numPasses>=1 is number of time to repeat test (for min/max/stdev) " << std::endl 00313 << "Reported metric (min/avg/max/stdev) are milliseconds per cycle for all threads to complete." << std::endl 00314 << "storeBytes>=0 will store random sequences of this many bytes" << std::endl 00315 << " and check for duplicates. Mainly to check for thread safety." << std::endl 00316 << " Timing numbers in this case are not so useful." << std::endl 00317 << " 0 ==> [default] Don't store, just time." << std::endl 00318 ; 00319 exit(-1); 00320 } 00321 00322 std::auto_ptr<TestDummyThread> dummyThread; 00323 if ( numThreads >= 0 ) 00324 { 00325 dummyThread.reset(new TestDummyThread); 00326 dummyThread->run(); 00327 } 00328 00329 std::cerr << "Starting..." << std::endl; 00330 00331 { 00332 doVariationTest(numCycles, numThreads, numPass, storeBytes); 00333 } 00334 00335 std::cerr << "Success." << std::endl; 00336 return 0; 00337 } 00338 00339 00340 /* ==================================================================== 00341 * The Vovida Software License, Version 1.0 00342 * 00343 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. 00344 * 00345 * Redistribution and use in source and binary forms, with or without 00346 * modification, are permitted provided that the following conditions 00347 * are met: 00348 * 00349 * 1. Redistributions of source code must retain the above copyright 00350 * notice, this list of conditions and the following disclaimer. 00351 * 00352 * 2. Redistributions in binary form must reproduce the above copyright 00353 * notice, this list of conditions and the following disclaimer in 00354 * the documentation and/or other materials provided with the 00355 * distribution. 00356 * 00357 * 3. The names "VOCAL", "Vovida Open Communication Application Library", 00358 * and "Vovida Open Communication Application Library (VOCAL)" must 00359 * not be used to endorse or promote products derived from this 00360 * software without prior written permission. For written 00361 * permission, please contact vocal@vovida.org. 00362 * 00363 * 4. Products derived from this software may not be called "VOCAL", nor 00364 * may "VOCAL" appear in their name, without prior written 00365 * permission of Vovida Networks, Inc. 00366 * 00367 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED 00368 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00369 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND 00370 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA 00371 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES 00372 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, 00373 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00374 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00375 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 00376 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00377 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 00378 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 00379 * DAMAGE. 00380 * 00381 * ==================================================================== 00382 * 00383 * This software consists of voluntary contributions made by Vovida 00384 * Networks, Inc. and many individuals on behalf of Vovida Networks, 00385 * Inc. For more information on Vovida Networks, Inc., please see 00386 * <http://www.vovida.org/>. 00387 * 00388 * vi: set shiftwidth=3 expandtab: 00389 */
1.7.5.1