1 /* 2 This file is part of Valgrind, a dynamic binary instrumentation 3 framework. 4 5 Copyright (C) 2008-2008 Google Inc 6 opensource (at) 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 (at) 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: 71 Test() : Run_v_(0), Run_b_(0) {} 72 Test(int id, void_func_void_t _Run) : Run_v_(_Run), Run_b_(0) { 73 CHECK(Run_v_ != NULL); 74 } 75 Test(int id, bool_func_void_t _Run) : Run_v_(0), Run_b_(_Run) { 76 CHECK(Run_b_ != NULL); 77 } 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 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: 113 GoalStats(): stats(NULL), cost_m(NULL), calculated(false) {} 114 ~GoalStats() { delete stats; delete cost_m; } 115 116 // Note that this function is called before main() 117 void AddPattern(void (*register_func)(), void (*paramapply_func)()) { 118 param_registerers.insert(register_func); 119 param_appliers.insert(paramapply_func); 120 } 121 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 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 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 146 const Vector & GetStatsVector() { 147 return *stats; 148 } 149 150 TypesVector GetTypes() { 151 CHECK(stats != NULL); 152 return types; 153 } 154 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 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 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 { 206 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 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; 245 TestContext() { 246 data = new int[params.DATA_SIZE]; 247 } 248 } *contexts; 249 250 // Write accesses 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 } 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 } 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; 452 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 460 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; 471 TestContext () { 472 data = NULL; 473 CV_Signalled = 0; 474 } 475 } *contexts; 476 477 // Signal a random CV 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 } 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 } 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 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 } 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 } 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 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 686 void Registerer() { 687 goals.RegisterParameter(&nThreads_double); 688 goals.SetParameterStat(N_THREADS, &nThreads_double, 1); 689 } 690 691 void Applier() { 692 nThreads = round(nThreads_double); 693 CHECK(nThreads >= 2); 694 } 695 } 696 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 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