• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&params.num_iterations_times_runcount);
268       goals.SetParameterStat(N_MUTEX_LOCK_UNLOCK, &params.num_iterations_times_runcount, 3 /*don't ask why*/);
269       goals.SetParameterStat(N_MEM_ACCESSES_K, &params.num_iterations_times_runcount,
270                              params.DATA_SIZE * params.REDO_CNT * 2.0 / 1000.0);
271 
272       goals.RegisterParameter(&params.num_contexts);
273       goals.SetParameterStat(N_MUTEXES, &params.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(&params.num_contexts);
502          goals.SetParameterStat(N_CV, &params.num_contexts, 1);
503          goals.SetParameterStat(N_MUTEXES, &params.num_contexts, 1);
504 
505          goals.RegisterParameter(&params.data_size_times_runcount);
506          goals.SetParameterStat(N_MEM_ACCESSES_K, &params.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