reSIProcate/rutil  9694
testRandomThread.cxx
Go to the documentation of this file.
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  */