/[resiprocate]/main/rutil/test/testRandomThread.cxx
ViewVC logotype

Contents of /main/rutil/test/testRandomThread.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 9568 - (show annotations) (download)
Fri Apr 20 13:56:34 2012 UTC (7 years, 6 months ago) by dpocock
File size: 11335 byte(s)
rutil: make testRandomThread use std:: for sqrt, atoi and friends
1 /**
2 Helper to test the speed of of the random generator. Used
3 to optimize the different flavors.
4
5 **/
6 #include <cstdlib>
7 #include <cmath> // for sqrt
8 #include <iostream>
9 #include <vector>
10 #include <memory> // for auto_ptr
11 #include <set>
12
13 // #include "rutil/Data.hxx"
14 #include "rutil/Random.hxx"
15 #include "rutil/Time.hxx" // for ResipClock
16 #include "rutil/Lock.hxx"
17
18 #define RANDINT_PER_CYCLE (1000000)
19 #define RANDSEQ_PER_CYCLE (1000)
20
21 using namespace std;
22 using namespace resip;
23
24 class Barrier {
25 public:
26 Barrier(int numThreads)
27 : mCurId(0), mHaveCnt(0), mWantCnt(numThreads) { };
28 void sync(int id, bool isMaster=false);
29 protected:
30 Condition mCond;
31 Mutex mMutex;
32 volatile int mCurId;
33 volatile int mHaveCnt;
34 int mWantCnt;
35
36 public:
37 static volatile int sPreWaitCnt;
38 static volatile int sPostWaitCnt;
39 };
40
41 volatile int Barrier::sPreWaitCnt = 0;
42 volatile int Barrier::sPostWaitCnt = 0;
43
44 void
45 Barrier::sync(int id, bool isMaster)
46 {
47 Lock datalock(mMutex);
48 if ( isMaster )
49 {
50 mHaveCnt = 0;
51 mCurId = id;
52 mCond.broadcast();
53 }
54 else
55 {
56 while ( mCurId != id )
57 {
58 ++sPreWaitCnt;
59 mCond.wait(mMutex);
60 }
61 ++mHaveCnt;
62 mCond.broadcast();
63 }
64 while ( mCurId==id && mHaveCnt < mWantCnt )
65 {
66 ++sPostWaitCnt;
67 mCond.wait(mMutex);
68 }
69 }
70
71 class TestDummyThread : public ThreadIf
72 {
73 public:
74 TestDummyThread() { };
75 ~TestDummyThread() { };
76
77 virtual void thread() { }
78 };
79
80 typedef std::set<Data> DataSet;
81
82 class TestRandomThread : public ThreadIf
83 {
84 public:
85 TestRandomThread(int runs, Barrier& barrier, int storeBytes)
86 : mNumRuns(runs), mBarrier(barrier), mStoreBytes(storeBytes),
87 mDupCnt(0)
88 { };
89 ~TestRandomThread() { };
90
91 virtual void thread();
92 static void makeRandoms(int numCycles);
93 void makeAndStoreRandoms(int numCycles);
94 const DataSet& getRandoms() const { return mRandoms; };
95 int getDupCnt() const { return mDupCnt; }
96
97 protected:
98 int mNumRuns;
99 Barrier& mBarrier;
100 int mStoreBytes;
101 DataSet mRandoms;
102 int mDupCnt;
103 };
104
105
106 void
107 TestRandomThread::makeRandoms(int numCycles)
108 {
109 int idx, cidx;
110 for (cidx=0; cidx < numCycles; cidx++)
111 {
112 for (idx = 0; idx < RANDINT_PER_CYCLE; idx++)
113 {
114 int val = Random::getRandom();
115 (void)val;
116 }
117 }
118 }
119
120 void
121 TestRandomThread::makeAndStoreRandoms(int numCycles)
122 {
123 int idx, cidx;
124 for (cidx=0; cidx < numCycles; cidx++)
125 {
126 for (idx = 0; idx < RANDSEQ_PER_CYCLE; idx++)
127 {
128 Data foo = Random::getRandomHex(mStoreBytes);
129 if (mRandoms.insert(foo).second == false )
130 {
131 ++mDupCnt;
132 // std::cerr << "RandomHex produced intra-thread duplicate after "
133 // << idx << " calls."<<std::endl;
134 }
135 }
136 }
137 }
138
139
140 void
141 TestRandomThread::thread()
142 {
143 // do once to get over initialization
144 Random::getRandom();
145 // std::cerr << "Thread initialized." << std::endl;
146
147 mBarrier.sync(1);
148
149 if (mStoreBytes == 0 )
150 {
151 makeRandoms(mNumRuns);
152 }
153 else
154 {
155 makeAndStoreRandoms(mNumRuns);
156 }
157
158 mBarrier.sync(2);
159 }
160
161 static UInt64
162 doSingleTest(int numCycles)
163 {
164 UInt64 startUs = ResipClock::getTimeMicroSec();
165 TestRandomThread::makeRandoms(numCycles);
166 UInt64 doneUs = ResipClock::getTimeMicroSec();
167 return doneUs - startUs;
168 }
169
170 static int
171 mergeCheckRandoms(DataSet& all, const DataSet& more)
172 {
173 int dupCnt = 0;
174 DataSet::const_iterator it = more.begin();
175 // DataSet::iterator at = all.begin();
176 for ( ; it != more.end(); ++it)
177 {
178 // STL is unbelieable: this form isn't supported:
179 // std::pair<DataSet::iterator,bool> res = all.insert( at, *it);
180 // So we have to do this NlgN style:
181 std::pair<DataSet::iterator,bool> res = all.insert( *it);
182 // at = res->first;
183 if ( res.second == false )
184 {
185 ++dupCnt;
186 // std::cerr << "Inter-thread duplicate found." << std::endl;
187 }
188 }
189 return dupCnt;
190 }
191
192 static UInt64
193 doThreadedTest(int numCycles, int numThreads, int storeBytes)
194 {
195 std::vector<TestRandomThread*> threadList;
196 Barrier bar(numThreads);
197 int pidx;
198 for (pidx=0; pidx < numThreads; pidx++)
199 {
200 TestRandomThread* rth = new TestRandomThread(numCycles,bar, storeBytes);
201 rth->run();
202 threadList.push_back(rth);
203 }
204 // std::cerr << "Threads started." << std::endl;
205
206
207 bar.sync(1, true);
208
209 UInt64 startUs = ResipClock::getTimeMicroSec();
210 // TestRandomThread::makeRandoms(numCycles);
211
212 bar.sync(2, true);
213
214 UInt64 doneUs = ResipClock::getTimeMicroSec();
215 // std::cerr << "Threads finished."
216 // << " (barrier pre="<<Barrier::sPreWaitCnt
217 // << " post="<<Barrier::sPostWaitCnt
218 // <<")" << std::endl;
219
220 DataSet allRandoms;
221 int intraDupCnt = 0;
222 int interDupCnt = 0;
223 for (pidx=0; pidx < numThreads; pidx++)
224 {
225 TestRandomThread* rth = threadList[pidx];
226 if ( storeBytes > 0 )
227 {
228 intraDupCnt += rth->getDupCnt();
229 interDupCnt += mergeCheckRandoms(allRandoms, rth->getRandoms());
230 }
231 rth->join();
232 delete rth;
233 }
234 if ( storeBytes > 0 )
235 {
236 std::cout << "Found "<<interDupCnt<< " inter-thread and "
237 <<intraDupCnt<<" intra-thread duplicates." << std::endl;
238 }
239 return doneUs - startUs;
240 }
241
242 static void
243 doVariationTest(int numCycles, int numThreads, int numPass, int storeBytes)
244 {
245 UInt64 msMin = 0, msMax = 0;
246 UInt64 msSum = 0;
247 UInt64 msSumSq = 0;
248 int passIdx=0;
249 for (passIdx=0; passIdx < numPass; passIdx++)
250 {
251 UInt64 usTot = numThreads<=0
252 ? doSingleTest(numCycles)
253 : doThreadedTest(numCycles, numThreads, storeBytes);
254 UInt64 usPerCycle = usTot/numCycles;
255 #if 0
256 std::cerr << numCycles << " cycles/thread (1M plain 32-bit ints)"
257 << " with " << numThreads << " threads"
258 << " took " << usTot<<"us (" << usPerCycle << "us/cycle)"
259 << std::endl;
260 #endif
261 UInt64 msPerCycle = usPerCycle/1000;
262 msSum += msPerCycle;
263 msSumSq += msPerCycle*msPerCycle;
264 if ( msPerCycle < msMin || passIdx==0 )
265 msMin = msPerCycle;
266 if ( msPerCycle > msMax || passIdx==0 )
267 msMax = msPerCycle;
268 }
269 double msAvg = msSum/(numPass+0.0);
270 double msVar = msSumSq/(numPass+0.0) - msAvg*msAvg;
271 double msStd = sqrt(msVar);
272 double msStdPct = msStd/msAvg*100;
273 fprintf(stderr,"RESULT:plain:%s:cycles=%d,threads=%d,passes=%d:min=%d,avg=%d,max=%d,std=%.1f(%.1f%%)[ms/cycle]\n",
274 Random::getImplName(),
275 numCycles, numThreads, numPass,
276 (int)msMin,(int)msAvg,(int)msMax, msStd, msStdPct);
277 }
278
279
280 int main(int argc, char** argv)
281 {
282 bool doSweep = true;
283 int numCycles = 2;
284 int numThreads = 3;
285 int numPass = 10;
286 int storeBytes = 0;
287
288 {
289 doSweep = false;
290 if(argc >= 2)
291 numCycles = atoi(argv[1]);
292 if(argc >= 3)
293 numThreads = atoi(argv[2]);
294 if(argc >= 4)
295 numPass = atoi(argv[3]);
296 if(argc >= 5)
297 storeBytes = atoi(argv[4]);
298 }
299
300 if (numCycles <= 0 || numThreads < -1 || numPass < 1)
301 {
302 std::cerr
303 << "usage: testRandomThread [numCycles numThreads numPasses storeBytes]" << std::endl
304 << "numCycles>0 is number of cycles to run in each thread." << std::endl
305 << " each cycle is one million random 32bit integers" << std::endl
306 << " each cycle is one thousand random sequences when storing (see below) " << std::endl
307 << "numThreads>=-1 is number of threads to run " << std::endl
308 << " -1 ==> work in main thread only" << std::endl
309 << " 0 ==> work in main thread, plus dummy thread doing nothing" << std::endl
310 << " 1 ==> single working thread, plus dummy & main doing nothing" << std::endl
311 << " N ==> this many working threads, plus dummy & main doing nothing" << std::endl
312 << "numPasses>=1 is number of time to repeat test (for min/max/stdev) " << std::endl
313 << "Reported metric (min/avg/max/stdev) are milliseconds per cycle for all threads to complete." << std::endl
314 << "storeBytes>=0 will store random sequences of this many bytes" << std::endl
315 << " and check for duplicates. Mainly to check for thread safety." << std::endl
316 << " Timing numbers in this case are not so useful." << std::endl
317 << " 0 ==> [default] Don't store, just time." << std::endl
318 ;
319 exit(-1);
320 }
321
322 std::auto_ptr<TestDummyThread> dummyThread;
323 if ( numThreads >= 0 )
324 {
325 dummyThread.reset(new TestDummyThread);
326 dummyThread->run();
327 }
328
329 std::cerr << "Starting..." << std::endl;
330
331 {
332 doVariationTest(numCycles, numThreads, numPass, storeBytes);
333 }
334
335 std::cerr << "Success." << std::endl;
336 return 0;
337 }
338
339
340 /* ====================================================================
341 * The Vovida Software License, Version 1.0
342 *
343 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
344 *
345 * Redistribution and use in source and binary forms, with or without
346 * modification, are permitted provided that the following conditions
347 * are met:
348 *
349 * 1. Redistributions of source code must retain the above copyright
350 * notice, this list of conditions and the following disclaimer.
351 *
352 * 2. Redistributions in binary form must reproduce the above copyright
353 * notice, this list of conditions and the following disclaimer in
354 * the documentation and/or other materials provided with the
355 * distribution.
356 *
357 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
358 * and "Vovida Open Communication Application Library (VOCAL)" must
359 * not be used to endorse or promote products derived from this
360 * software without prior written permission. For written
361 * permission, please contact vocal@vovida.org.
362 *
363 * 4. Products derived from this software may not be called "VOCAL", nor
364 * may "VOCAL" appear in their name, without prior written
365 * permission of Vovida Networks, Inc.
366 *
367 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
368 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
369 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
370 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
371 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
372 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
373 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
374 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
375 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
376 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
377 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
378 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
379 * DAMAGE.
380 *
381 * ====================================================================
382 *
383 * This software consists of voluntary contributions made by Vovida
384 * Networks, Inc. and many individuals on behalf of Vovida Networks,
385 * Inc. For more information on Vovida Networks, Inc., please see
386 * <http://www.vovida.org/>.
387 *
388 * vi: set shiftwidth=3 expandtab:
389 */

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27