1 /*
2 This file is part of Valgrind, a dynamic binary instrumentation
3 framework.
4
5 Copyright (C) 2008-2008 Google Inc
6 opensource@google.com
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 02111-1307, USA.
22
23 The GNU General Public License is contained in the file COPYING.
24 */
25
26 // Author: Timur Iskhodzhanov <opensource@google.com>
27 //
28 // This file contains a set of benchmarks for data race detection tools.
29
30 #include <vector>
31 #include <string>
32 #include <map>
33 #include <set>
34 #include <algorithm>
35 #include <cstring> // strlen(), index(), rindex()
36 #include <ctime>
37 #include <math.h>
38
39 #include "thread_wrappers.h"
40 #include "linear_solver.h"
41
42 class Mutex64: public Mutex {
43 // force sizeof(Mutex64) >= 64
44 private:
45 char ___[(sizeof(Mutex) > 64) ? (0) : (64 - sizeof(Mutex))];
46 };
47
48 enum StatType {
49 ZZZERO,
50 N_THREADS,
51 N_CV,
52 N_CV_SIGNALS,
53 N_CV_WAITS,
54 N_MUTEXES,
55 N_MUTEX_LOCK_UNLOCK,
56 N_MEM_ACCESSES_K, // kiloaccesses
57 /*N_RACEY_ACCESSES*/
58 };
59
60 class Test{
61 typedef void (*void_func_void_t)(void);
62
63 /* may return false to indicate smth like "Wait didn't succeed" */
64 typedef bool (*bool_func_void_t)(void);
65 //typedef TestStats (*TestStats_func_void_t)(void);
66
67 //TestStats_func_void_t GetStats_;
68 void_func_void_t Run_v_;
69 bool_func_void_t Run_b_;
70 public:
Test()71 Test() : Run_v_(0), Run_b_(0) {}
Test(int id,void_func_void_t _Run)72 Test(int id, void_func_void_t _Run) : Run_v_(_Run), Run_b_(0) {
73 CHECK(Run_v_ != NULL);
74 }
Test(int id,bool_func_void_t _Run)75 Test(int id, bool_func_void_t _Run) : Run_v_(0), Run_b_(_Run) {
76 CHECK(Run_b_ != NULL);
77 }
Run()78 bool Run() {
79 if (Run_v_ == NULL) {
80 CHECK(Run_b_ != NULL);
81 return (*Run_b_)();
82 } else {
83 Run_v_();
84 return true;
85 }
86 }
87 };
88
89 typedef std::map<int, Test> MapOfTests;
90 MapOfTests the_map_of_tests;
91
92 class GoalStats {
93 public:
94 typedef std::vector<StatType> TypesVector;
95 private:
96 //TODO: think of better names
97 std::map<StatType, int> goal;
98 TypesVector types;
99 Vector * stats;
100 typedef std::set<void (*)()> void_f_void_set;
101 void_f_void_set param_registerers, param_appliers;
102 std::vector<double*> parameters;
103 Matrix * cost_m;
104 bool calculated;
105
v_find(const std::vector<T> & vec,const T & val)106 template<typename T> static int v_find(const std::vector<T> & vec, const T & val) {
107 for (int i = 0; i < vec.size(); i++)
108 if (vec[i] == val)
109 return i;
110 return -1;
111 }
112 public:
GoalStats()113 GoalStats(): stats(NULL), cost_m(NULL), calculated(false) {}
~GoalStats()114 ~GoalStats() { delete stats; delete cost_m; }
115
116 // Note that this function is called before main()
AddPattern(void (* register_func)(),void (* paramapply_func)())117 void AddPattern(void (*register_func)(), void (*paramapply_func)()) {
118 param_registerers.insert(register_func);
119 param_appliers.insert(paramapply_func);
120 }
121
RegisterPatterns()122 void RegisterPatterns() {
123 for(void_f_void_set::iterator i = param_registerers.begin();
124 i != param_registerers.end(); i++)
125 {
126 (*(*i))(); // call each param_registerer
127 }
128 }
129
AddGoal(StatType type,int value)130 void AddGoal(StatType type, int value) {
131 CHECK(stats == NULL);
132 CHECK(goal.find(type) == goal.end());
133 goal[type] = value;
134 types.push_back(type);
135 }
136
CompileStatsIntoVector()137 void CompileStatsIntoVector() {
138 CHECK(stats == NULL);
139 CHECK(types.size() == goal.size());
140 stats = new Vector(types.size());
141 for (int i = 0; i < types.size(); i++)
142 (*stats)[i] = goal[types[i]];
143 cost_m = new Matrix(types.size(), 0);
144 }
145
GetStatsVector()146 const Vector & GetStatsVector() {
147 return *stats;
148 }
149
GetTypes()150 TypesVector GetTypes() {
151 CHECK(stats != NULL);
152 return types;
153 }
154
RegisterParameter(double * param)155 void RegisterParameter(double * param) {
156 CHECK(stats != NULL);
157 // int param_id = parameters.size();
158 parameters.push_back(param);
159 cost_m->IncN();
160 }
161
SetParameterStat(StatType stat_type,double * param,double cost)162 void SetParameterStat(StatType stat_type, double * param, double cost) {
163 int type_id = v_find(types, stat_type);
164 if (type_id == -1)
165 return; // this stat type isn't required - ignore
166
167 int param_id = v_find(parameters, param);
168 CHECK(param_id != -1);
169
170 cost_m->At(type_id, param_id) = cost;
171 }
172
CalculateAndApplyParameters()173 void CalculateAndApplyParameters() {
174 CHECK(calculated == false);
175 printf("Cost matrix:\n%s\n", cost_m->ToString().c_str());
176 printf("Stats vector:\n%s\n", stats->ToString().c_str());
177 int iterations = 0;
178 Vector params = EstimateParameters(*cost_m, *stats, 0.0005, &iterations);
179 CHECK(params.GetSize() == parameters.size());
180 /*params[0] = 1000;
181 params[1] = 3600;
182 params[2] = 80;
183 params[3] = 0;
184 params[4] = 19530;
185 params[5] = 1720;*/
186 printf("Parameters (estimated in %d steps) :\n", iterations);
187 for (int i = 0; i < parameters.size(); i++) {
188 printf("param[%i] = %lf\n", i, params[i]);
189 *(parameters[i]) = params[i];
190 }
191 printf("Est. stats: %s\n", cost_m->MultiplyRight(params).ToString().c_str());
192
193 for (void_f_void_set::iterator i = param_appliers.begin();
194 i != param_appliers.end(); i++)
195 {
196 (*(*i))();
197 }
198 fflush(stdout);
199
200 calculated = true;
201 }
202 } goals;
203
204 template <typename RetVal>
205 struct TestAdder {
TestAdderTestAdder206 TestAdder(int id, RetVal (*_Run)(),
207 void (*paramreg)(void),
208 void (*paramapply)(void))
209 {
210 CHECK(the_map_of_tests.count(id) == 0);
211 the_map_of_tests[id] = Test(id, _Run);
212 goals.AddPattern(paramreg, paramapply);
213 }
214 };
215
216 #define REGISTER_PATTERN(id) TestAdder<void> add_test##id(id, Pattern##id, \
217 ParametersRegistration##id, ApplyParameters##id)
218 #define REGISTER_PATTERN_PROB(id) TestAdder<bool> add_test##id(id, Pattern##id, \
219 ParametersRegistration##id, ApplyParameters##id)
220
221 ThreadPool * mainThreadPool;
222 std::map<int, double> map_of_counts; // test -> average run count
223
round(double lf)224 inline double round(double lf) {
225 return floor(lf + 0.5);
226 }
227
228 // Accessing memory locations holding one lock {{{1
229 namespace one_lock {
230 struct Params {
231 double num_contexts;
232 int NUM_CONTEXTS;
233
234 double num_iterations_times_runcount;
235 int NUM_ITERATIONS;
236
237 //double data_size_times_;
238 static const int DATA_SIZE = 128;
239 static const int REDO_CNT = 2;
240 } params;
241
242 struct TestContext {
243 Mutex64 MU;
244 int * data;
TestContextone_lock::TestContext245 TestContext() {
246 data = new int[params.DATA_SIZE];
247 }
248 } *contexts;
249
250 // Write accesses
Pattern101()251 void Pattern101() {
252 int id = rand() % params.NUM_CONTEXTS;
253 TestContext * context = &contexts[id];
254 for (int i = 0; i < params.NUM_ITERATIONS; i++) {
255 context->MU.Lock();
256 for (int j = 0; j < params.DATA_SIZE; j++) {
257 for (int k = 0; k < params.REDO_CNT; k++)
258 context->data[j] = 77; // write
259 }
260 context->MU.Unlock();
261 }
262 }
ParametersRegistration101()263 void ParametersRegistration101() {
264 map_of_counts[101] = 100;
265 //goals.RegisterParameter(&map_of_counts[101]);
266
267 goals.RegisterParameter(¶ms.num_iterations_times_runcount);
268 goals.SetParameterStat(N_MUTEX_LOCK_UNLOCK, ¶ms.num_iterations_times_runcount, 3 /*don't ask why*/);
269 goals.SetParameterStat(N_MEM_ACCESSES_K, ¶ms.num_iterations_times_runcount,
270 params.DATA_SIZE * params.REDO_CNT * 2.0 / 1000.0);
271
272 goals.RegisterParameter(¶ms.num_contexts);
273 goals.SetParameterStat(N_MUTEXES, ¶ms.num_contexts, 1);
274 }
ApplyParameters101()275 void ApplyParameters101() {
276 if (map_of_counts[101] < 1.0)
277 map_of_counts[101] = 1.0;
278 params.NUM_CONTEXTS = round(params.num_contexts);
279 if (params.NUM_CONTEXTS <= 0)
280 params.NUM_CONTEXTS = 1;
281 params.NUM_ITERATIONS = round(params.num_iterations_times_runcount / map_of_counts[101]);
282
283 contexts = new TestContext[params.NUM_CONTEXTS];
284 }
285 REGISTER_PATTERN(101);
286
287 /* other tests...
288 // Read accesses
289 void Pattern102() {
290 int id = rand() % NUM_CONTEXTS;
291 TestContext * context = &contexts[id];
292 for (int i = 0; i < NUM_ITERATIONS; i++) {
293 int temp = 0;
294 context->MU.Lock();
295 for (int j = 0; j < DATA_SIZE; j++) {
296 for (int k = 0; k < 10; k++)
297 temp += context->data[j]; // read
298 }
299 context->MU.Unlock();
300 }
301 }
302 REGISTER_PATTERN(102);
303
304
305 int atomic_integers[NUM_CONTEXTS] = {0};
306 // Atomic increment
307 void Pattern103() {
308 int id = rand() % NUM_CONTEXTS;
309 for (int i = 0; i < NUM_ITERATIONS; i++)
310 __sync_add_and_fetch(&atomic_integers[id], 1);
311 }
312 REGISTER_PATTERN(103);
313 */
314 } // namespace one_lock
315 /* other namespaces...
316 // Accessing memory locations holding random LockSets {{{1
317 namespace multiple_locks {
318 // TODO: make these constants as parameters
319 const int NUM_CONTEXTS = 1024;
320 const int DATA_SIZE = 4096;
321 const int NUM_ITERATIONS = 1;
322 const int LOCKSET_SIZE = 2;
323
324 struct TestContext {
325 Mutex64 MU;
326 int data[DATA_SIZE];
327 } contexts[NUM_CONTEXTS];
328
329 // Access random context holding a random LS including context->MU
330 void Pattern201() {
331 TestContext * context = &contexts[rand() % NUM_CONTEXTS];
332 std::vector<Mutex64*> LS;
333 // STL nightmare starts here - calculate random LS{{{1
334 {
335 std::vector<int> tmp_LS;
336 for (int i = 0; i < NUM_CONTEXTS; i++)
337 tmp_LS.push_back(i);
338 std::random_shuffle(tmp_LS.begin(), tmp_LS.end());
339
340 // TODO: #LS as a parameter
341 for (int i = 0; i < LOCKSET_SIZE; i++)
342 LS.push_back(&contexts[tmp_LS[i]].MU);
343
344 // This LS should contain context's Mutex to have proper synchronization
345 LS.push_back(&context->MU);
346
347 // LS should be sorted to avoid deadlocks
348 std::sort(LS.begin(), LS.end());
349
350 // LS should not contain context->MU twice
351 std::vector<Mutex64*>::iterator new_end = std::unique(LS.begin(), LS.end());
352 LS.erase(new_end, LS.end());
353 } // end of STL nightmare :-)
354
355 for (int i = 0; i < NUM_ITERATIONS; i++) {
356 for (std::vector<Mutex64*>::iterator it = LS.begin(); it != LS.end(); it++)
357 (*it)->Lock();
358 for (int j = 0; j < DATA_SIZE; j++)
359 context->data[j] = 77;
360 for (std::vector<Mutex64*>::reverse_iterator it = LS.rbegin(); it != LS.rend(); it++)
361 (*it)->Unlock();
362 }
363 }
364 REGISTER_PATTERN(201);
365
366 const int MAX_LOCKSET_SIZE = 3;
367 const int NUM_LOCKSETS = 1 << MAX_LOCKSET_SIZE;
368 Mutex64 ls_mu[MAX_LOCKSET_SIZE];
369 char ls_data[NUM_LOCKSETS][DATA_SIZE];
370 // Access random context holding a corresponding LockSet
371 void Pattern202() {
372 int ls_idx = 0;
373 while (ls_idx == 0)
374 ls_idx = rand() % NUM_LOCKSETS;
375
376 char * data = ls_data[ls_idx];
377 for (int i = 0; i < MAX_LOCKSET_SIZE; i++)
378 if (ls_idx & (1 << i))
379 ls_mu[i].Lock();
380
381 for (int j = 0; j < DATA_SIZE; j++)
382 data[j] = 77;
383
384 for (int i = MAX_LOCKSET_SIZE - 1; i >= 0; i--)
385 if (ls_idx & (1 << i))
386 ls_mu[i].Unlock();
387 }
388 REGISTER_PATTERN(202);
389 } // namespace multiple_locks
390 */
391
392 // Publishing objects using different synchronization patterns {{{1
393 namespace publishing {
394 /*namespace pcq {
395 const int NUM_CONTEXTS = 16;
396
397 struct Params {
398 double num_contexts;
399 int NUM_CONTEXTS;
400
401 double data_size_times_runcount;
402 int DATA_SIZE;
403 } params;
404
405 struct TestContext {
406 ProducerConsumerQueue pcq;
407
408 TestContext() : pcq(0) {}
409 ~TestContext() {
410 void * ptr = NULL;
411 // Erase the contents of the PCQ. We assume NULL can't be there
412 pcq.Put(NULL);
413 while(ptr = pcq.Get())
414 free(ptr);
415 }
416 } * contexts;
417
418 // Publish a random string into a random PCQ
419 void Pattern301() {
420 TestContext * context = &contexts[rand() % params.NUM_CONTEXTS];
421 // TODO: str_len as a parameter
422 int str_len = 1 + (rand() % params.DATA_SIZE);
423 char * str = (char*)malloc(str_len + 1);
424 CHECK(str != NULL);
425 memset(str, 'a', str_len);
426 str[str_len] = '\0';
427 context->pcq.Put(str);
428 }
429 REGISTER_PATTERN(301);
430
431 // Read a published string from a random PCQ. MAYFAIL!
432 bool Pattern302() {
433 TestContext * context = &contexts[rand() % NUM_CONTEXTS];
434 char * str = NULL;
435 if (context->pcq.TryGet((void**)&str)) {
436 int tmp = strlen(str);
437 free(str);
438 return true;
439 }
440 return false;
441 }
442 REGISTER_PATTERN(302);
443 }*/
444
445 namespace condvar {
446 struct Params {
447 double num_contexts;
448 int NUM_CONTEXTS;
449
450 double data_size_times_runcount;
451 int DATA_SIZE;
Paramspublishing::condvar::Params452 Params() {
453 DATA_SIZE = 1;
454 HIT_PROBABILITY = 0.3; // estimate. TODO: think of a better idea
455 }
456
457 const static int REDO = 100;
458 double HIT_PROBABILITY;
459
EstimateRuncountpublishing::condvar::Params460 double EstimateRuncount() {
461 return map_of_counts[311] + HIT_PROBABILITY * map_of_counts[312];
462 }
463 } params;
464
465 struct TestContext {
466 Mutex64 MU;
467 CondVar CV;
468 int CV_Signalled;
469
470 char * data;
TestContextpublishing::condvar::TestContext471 TestContext () {
472 data = NULL;
473 CV_Signalled = 0;
474 }
475 } *contexts;
476
477 // Signal a random CV
Pattern311()478 void Pattern311() {
479 int id = rand() % params.NUM_CONTEXTS;
480 TestContext * context = &contexts[id];
481 context->MU.Lock();
482 if (context->data) {
483 free(context->data);
484 }
485 int LEN = params.DATA_SIZE;
486 context->data = (char*)malloc(LEN + 1);
487 for (int i = 0; i < params.REDO; i++)
488 for (int j = 0; j < LEN; j++)
489 context->data[j] = 'a';
490 context->data[LEN] = '\0';
491 context->CV.Signal();
492 context->CV_Signalled = params.NUM_CONTEXTS;
493 context->MU.Unlock();
494 }
ParametersRegistration311()495 void ParametersRegistration311() {
496 double * num_CV_Signals = &map_of_counts[311];
497 goals.RegisterParameter(num_CV_Signals);
498 goals.SetParameterStat(N_CV_SIGNALS, num_CV_Signals, 1);
499 goals.SetParameterStat(N_MUTEX_LOCK_UNLOCK, num_CV_Signals, 9 /* don't ask why */);
500
501 goals.RegisterParameter(¶ms.num_contexts);
502 goals.SetParameterStat(N_CV, ¶ms.num_contexts, 1);
503 goals.SetParameterStat(N_MUTEXES, ¶ms.num_contexts, 1);
504
505 goals.RegisterParameter(¶ms.data_size_times_runcount);
506 goals.SetParameterStat(N_MEM_ACCESSES_K, ¶ms.data_size_times_runcount,
507 (2*params.REDO) * (1.0 + params.HIT_PROBABILITY) / 1000.0);
508 }
ApplyParameters311()509 void ApplyParameters311() {
510 if (map_of_counts[311] < 1.0)
511 map_of_counts[311] = 1.0;
512
513 params.DATA_SIZE = 1 + round(params.data_size_times_runcount / params.EstimateRuncount());
514 /*if (params.DATA_SIZE < 1)
515 params.DATA_SIZE = 1;*/
516
517 params.NUM_CONTEXTS = round(params.num_contexts);
518 if (params.NUM_CONTEXTS < 1)
519 params.NUM_CONTEXTS = 1;
520 contexts = new TestContext[params.NUM_CONTEXTS];
521 }
522 /*void TMP311 () {
523 map_of_counts[311] = 1000;
524 params.NUM_CONTEXTS = 100;
525 params.DATA_SIZE = 10000;
526 contexts = new TestContext[params.NUM_CONTEXTS];
527 }*/
528 REGISTER_PATTERN(311);
529
530 // Wait on a random CV
Pattern312()531 bool Pattern312() {
532 int nAttempts = 0,
533 id = rand() % params.NUM_CONTEXTS;
534 TestContext * context;
535
536 do {
537 if (nAttempts++ > params.NUM_CONTEXTS)
538 return false;
539 context = &contexts[id];
540 id = (id + 1) % params.NUM_CONTEXTS;
541 } while (ANNOTATE_UNPROTECTED_READ(context->CV_Signalled) == 0);
542
543 context->MU.Lock();
544 context->CV_Signalled--;
545 bool ret = !context->CV.WaitWithTimeout(&context->MU, 10);
546 if (ret && context->data) {
547 // int tmp = strlen(context->data);
548 free(context->data);
549 context->data = NULL;
550 }
551 context->MU.Unlock();
552 return ret;
553 }
ParametersRegistration312()554 void ParametersRegistration312() {
555 double * num_CV_Waits = &map_of_counts[312];
556 goals.RegisterParameter(num_CV_Waits);
557 goals.SetParameterStat(N_CV_WAITS, num_CV_Waits, params.HIT_PROBABILITY);
558 goals.SetParameterStat(N_MUTEX_LOCK_UNLOCK, num_CV_Waits, 16);
559 // N_MEM_ACCESSES_K is counted in ParametersRegistration312
560 }
ApplyParameters312()561 void ApplyParameters312() {
562 //nothing to do, see ApplyParameters311
563 }
564 REGISTER_PATTERN_PROB(312);
565 }
566 } // namespace publishing
567
568 /*
569 // Threads work with their memory exclusively {{{1
570 namespace thread_local {
571 // Thread accesses heap
572 void Pattern401() {
573 // TODO: parameters
574 const int DATA_SIZE = 1024;
575 const int ITERATIONS = 16;
576
577 char * temp = (char*)malloc(DATA_SIZE + 1);
578 for (int i = 1; i <= ITERATIONS; i++) {
579 memset(temp, i, DATA_SIZE);
580 temp[DATA_SIZE] = 0;
581 int size = strlen(temp);
582 }
583 free(temp);
584 }
585 REGISTER_PATTERN(401);
586
587 // Thread accesses stack
588 void Pattern402() {
589 // TODO: parameters
590 const int DATA_SIZE = 1024;
591 const int ITERATIONS = 16;
592
593 char temp[DATA_SIZE];
594 for (int i = 1; i <= ITERATIONS; i++) {
595 memset(temp, i, DATA_SIZE);
596 temp[DATA_SIZE] = 0;
597 int size = strlen(temp);
598 }
599 }
600 REGISTER_PATTERN(402);
601 } // namespace thread_local
602
603 // Different benign races scenarios {{{1
604 namespace benign_races {
605 namespace stats {
606 int simple_counter = 0;
607
608 int odd_counter = 1;
609 Mutex64 odd_counter_mu;
610
611 struct __ {
612 __() {
613 ANNOTATE_BENIGN_RACE(&simple_counter, "Pattern501");
614 }
615 } _;
616
617 void Pattern501() {
618 simple_counter++;
619 }
620 REGISTER_PATTERN(501);
621
622 // increment odd_counter, but first check it is >0 (double-check)
623 void Pattern502() {
624 if (ANNOTATE_UNPROTECTED_READ(odd_counter) > 0) {
625 odd_counter_mu.Lock();
626 if (odd_counter > 0)
627 odd_counter++;
628 odd_counter_mu.Unlock();
629 }
630 }
631 REGISTER_PATTERN(502);
632 }
633
634 } // namespace benign_races
635 */
636
637 typedef std::map<std::string, StatType> StatMap;
638 StatMap statNames;
639 int nThreads = 2;
640
PatternDispatcher()641 void PatternDispatcher() {
642 /*std::vector<int> availablePatterns;
643 for (MapOfTests::iterator it = the_map_of_tests.begin();
644 it != the_map_of_tests.end(); it++) {
645 if (map_of_counts[it->first] > 0.0)
646 availablePatterns.push_back(it->first);
647 }*/
648
649 std::map<int, int> this_thread_runcounts;
650
651 int total = 0;
652 for (std::map<int,double>::iterator it = map_of_counts.begin(); it != map_of_counts.end(); it++) {
653 CHECK(it->second >= 0.0);
654 int count = round(it->second / nThreads);
655 this_thread_runcounts[it->first] = count;
656 total += count;
657 }
658
659 CHECK(total > 0);
660
661 for (int i = 0; i < total; i++) {
662 //while (total > 0) {
663 int rnd = rand() % total;
664 int test_idx = -1;
665 for (std::map<int,int>::iterator it = this_thread_runcounts.begin(); it != this_thread_runcounts.end(); it++) {
666 int this_test_count = it->second;
667 if (rnd < this_test_count) {
668 test_idx = it->first;
669 break;
670 }
671 rnd -= this_test_count;
672 }
673 CHECK(test_idx >= 0);
674 // TODO: the above code should be replaced with a proper randomizer
675 // with a "specify distribution function" feature
676 if (the_map_of_tests[test_idx].Run()) {
677 /* this_thread_runcounts[test_idx]--;
678 total--;*/
679 }
680 }
681 }
682
683 namespace N_THREADS_hack {
684 double nThreads_double = 2.0;
685
Registerer()686 void Registerer() {
687 goals.RegisterParameter(&nThreads_double);
688 goals.SetParameterStat(N_THREADS, &nThreads_double, 1);
689 }
690
Applier()691 void Applier() {
692 nThreads = round(nThreads_double);
693 CHECK(nThreads >= 2);
694 }
695 }
696
RegisterStatNames()697 void RegisterStatNames() {
698 #define REGISTER_STAT_NAME(a) statNames[#a] = a
699 goals.AddPattern(N_THREADS_hack::Registerer, N_THREADS_hack::Applier);
700 REGISTER_STAT_NAME(N_THREADS);
701 REGISTER_STAT_NAME(N_CV);
702 REGISTER_STAT_NAME(N_CV_SIGNALS);
703 REGISTER_STAT_NAME(N_CV_WAITS);
704 REGISTER_STAT_NAME(N_MUTEXES);
705 REGISTER_STAT_NAME(N_MUTEX_LOCK_UNLOCK);
706 REGISTER_STAT_NAME(N_MEM_ACCESSES_K);
707 }
708
main(int argc,const char ** argv)709 int main(int argc, const char **argv) {
710 long init = GetTimeInMs();
711 RegisterStatNames();
712 const char *default_goals[] = {"N_THREADS=20", "N_MEM_ACCESSES_K=130000",
713 "N_MUTEXES=1800", "N_CV=80", "N_MUTEX_LOCK_UNLOCK=107000",
714 "N_CV_SIGNALS=3600", "N_CV_WAITS=500"};
715 const char ** goal_list = NULL;
716 int goal_cnt = 0;
717 if (argc == 1) {
718 printf("Running the default pattern\n");
719 goal_list = default_goals;
720 goal_cnt = sizeof(default_goals) / sizeof(*default_goals);
721 } else if (argc == 2 && !strcmp(argv[1], "--help")) {
722 printf("Usage: bigtest [PARAM=VALUE] ...\n Available params: ");
723 for (StatMap::iterator i = statNames.begin(); i != statNames.end(); i++) {
724 printf ("%s%s", (i == statNames.begin()) ? "" : ", ",
725 (*i).first.c_str());
726 }
727 printf("\n");
728 return 0;
729 } else {
730 goal_list = argv + 1;
731 goal_cnt = argc - 1;
732 }
733
734 {
735 // Parse goal strings
736 for (int i = 0; i < goal_cnt; i++) {
737 const char * goal = goal_list[i];
738 char stat[256] = "";
739 int stat_val = -1, j = 0;
740 for (; j < sizeof(stat) - 1
741 && goal[j] != '='
742 && goal[j] != '\0'; j++) {
743 stat[j] = goal[j];
744 }
745 stat[j] = '\0';
746 if (goal[j] == '=')
747 sscanf(goal + j + 1, "%i", &stat_val);
748 printf("%s = %i\n", stat, stat_val);
749 if (goal[j] != '='
750 || strlen(stat) == 0
751 || stat_val < 0
752 || statNames.find(stat) == statNames.end()
753 ) {
754 fprintf(stderr, "Error parsing goal \"%s\"\n", goal);
755 CHECK(0);
756 }
757 goals.AddGoal(statNames[stat], stat_val);
758 }
759 printf("\n");
760 }
761 goals.CompileStatsIntoVector();
762 Vector statsVector = goals.GetStatsVector();
763 goals.RegisterPatterns();
764 goals.CalculateAndApplyParameters();/**/
765 long start = GetTimeInMs();
766 printf("\nParameters calculated in %dms\nBenchmarking...\n",
767 (int)(start - init));
768 // Start (N_THREADS - 1) new threads...
769 mainThreadPool = new ThreadPool(nThreads - 1);
770 mainThreadPool->StartWorkers();
771 for (int i = 0; i < nThreads - 1; i++) {
772 mainThreadPool->Add(NewCallback(PatternDispatcher));
773 }
774 PatternDispatcher(); // and 1 more in the main thread
775 delete mainThreadPool;
776 long end = GetTimeInMs();
777 printf("...done in %dms\n", (int)(end - start));
778 printf("*RESULT bigtest: time= %d ms\n", (int)(end - start));
779 return 0;
780 }
781