00001 #ifndef __HAYAI_BENCHMARKER
00002 #define __HAYAI_BENCHMARKER
00003 #include <algorithm>
00004 #include <vector>
00005 #include <limits>
00006 #include <iomanip>
00007 #include <string>
00008 #include <cstring>
00009
00010 #include "hayai/hayai_test_factory.hpp"
00011 #include "hayai/hayai_test_descriptor.hpp"
00012 #include "hayai/hayai_test_result.hpp"
00013 #include "hayai/hayai_console_outputter.hpp"
00014
00015
00016 namespace hayai
00017 {
00019 class Benchmarker
00020 {
00021 public:
00023
00026 static Benchmarker& Instance()
00027 {
00028 static Benchmarker singleton;
00029 return singleton;
00030 }
00031
00032
00034
00042 static TestDescriptor* RegisterTest(
00043 const char* fixtureName,
00044 const char* testName,
00045 std::size_t runs,
00046 std::size_t iterations,
00047 TestFactory* testFactory,
00048 TestParametersDescriptor parameters
00049 )
00050 {
00051
00052 static const char* disabledPrefix = "DISABLED_";
00053 bool isDisabled = ((::strlen(testName) >= 9) &&
00054 (!::memcmp(testName, disabledPrefix, 9)));
00055
00056 if (isDisabled)
00057 {
00058 testName += 9;
00059 }
00060
00061
00062 TestDescriptor* descriptor = new TestDescriptor(fixtureName,
00063 testName,
00064 runs,
00065 iterations,
00066 testFactory,
00067 parameters,
00068 isDisabled);
00069
00070 Instance()._tests.push_back(descriptor);
00071
00072 return descriptor;
00073 }
00074
00075
00077
00080 static void AddOutputter(Outputter& outputter)
00081 {
00082 Instance()._outputters.push_back(&outputter);
00083 }
00084
00085
00087
00093 static void ApplyPatternFilter(const char* pattern)
00094 {
00095 Benchmarker& instance = Instance();
00096
00097
00098 const char* const dash = strchr(pattern, '-');
00099
00100 std::string positive;
00101 std::string negative;
00102
00103 if (dash == NULL)
00104 {
00105 positive = pattern;
00106 }
00107 else
00108 {
00109 positive = std::string(pattern, dash);
00110 negative = std::string(dash + 1);
00111
00112 if (positive.empty())
00113 {
00114 positive = "*";
00115 }
00116 }
00117
00118
00119 std::size_t index = 0;
00120
00121 while (index < instance._tests.size())
00122 {
00123 TestDescriptor* desc = instance._tests[index];
00124
00125 if ((!FilterMatchesString(positive.c_str(),
00126 desc->CanonicalName)) ||
00127 (FilterMatchesString(negative.c_str(),
00128 desc->CanonicalName)))
00129 {
00130 instance._tests.erase(
00131 instance._tests.begin() +
00132 std::vector<TestDescriptor*>::difference_type(index)
00133 );
00134 delete desc;
00135 }
00136 else
00137 {
00138 ++index;
00139 }
00140 }
00141 }
00142
00143
00145 static void RunAllTests()
00146 {
00147 ConsoleOutputter defaultOutputter;
00148 std::vector<Outputter*> defaultOutputters;
00149 defaultOutputters.push_back(&defaultOutputter);
00150
00151 Benchmarker& instance = Instance();
00152 std::vector<Outputter*>& outputters =
00153 (instance._outputters.empty() ?
00154 defaultOutputters :
00155 instance._outputters);
00156
00157
00158 std::vector<TestDescriptor*> tests = instance.GetTests();
00159
00160 const std::size_t totalCount = tests.size();
00161 std::size_t disabledCount = 0;
00162
00163 std::vector<TestDescriptor*>::const_iterator testsIt =
00164 tests.begin();
00165
00166 while (testsIt != tests.end())
00167 {
00168 if ((*testsIt)->IsDisabled)
00169 {
00170 ++disabledCount;
00171 }
00172
00173 ++testsIt;
00174 }
00175
00176 const std::size_t enabledCount = totalCount - disabledCount;
00177
00178
00179 const CalibrationModel calibrationModel = GetCalibrationModel();
00180
00181
00182 for (std::size_t outputterIndex = 0;
00183 outputterIndex < outputters.size();
00184 outputterIndex++)
00185 {
00186 outputters[outputterIndex]->Begin(enabledCount, disabledCount);
00187 }
00188
00189
00190 std::size_t index = 0;
00191
00192 while (index < tests.size())
00193 {
00194
00195 TestDescriptor* descriptor = tests[index++];
00196
00197
00198 if (instance._include.size() > 0)
00199 {
00200 bool included = false;
00201 std::string name =
00202 descriptor->FixtureName + "." +
00203 descriptor->TestName;
00204
00205 for (std::size_t i = 0; i < instance._include.size(); i++)
00206 {
00207 if (name.find(instance._include[i]) !=
00208 std::string::npos)
00209 {
00210 included = true;
00211 break;
00212 }
00213 }
00214
00215 if (!included)
00216 {
00217 continue;
00218 }
00219 }
00220
00221
00222 if (descriptor->IsDisabled)
00223 {
00224 for (std::size_t outputterIndex = 0;
00225 outputterIndex < outputters.size();
00226 outputterIndex++)
00227 outputters[outputterIndex]->SkipDisabledTest(
00228 descriptor->FixtureName,
00229 descriptor->TestName,
00230 descriptor->Parameters,
00231 descriptor->Runs,
00232 descriptor->Iterations
00233 );
00234
00235 continue;
00236 }
00237
00238
00239 for (std::size_t outputterIndex = 0;
00240 outputterIndex < outputters.size();
00241 outputterIndex++)
00242 outputters[outputterIndex]->BeginTest(
00243 descriptor->FixtureName,
00244 descriptor->TestName,
00245 descriptor->Parameters,
00246 descriptor->Runs,
00247 descriptor->Iterations
00248 );
00249
00250
00251 std::vector<uint64_t> runTimes(descriptor->Runs);
00252 uint64_t overheadCalibration =
00253 calibrationModel.GetCalibration(descriptor->Iterations);
00254
00255 std::size_t run = 0;
00256
00257 while (run < descriptor->Runs)
00258 {
00259
00260 Test* test = descriptor->Factory->CreateTest();
00261
00262
00263 uint64_t time = test->Run(descriptor->Iterations);
00264
00265
00266 runTimes[run] = (time > overheadCalibration ?
00267 time - overheadCalibration :
00268 0);
00269
00270
00271 delete test;
00272
00273 ++run;
00274 }
00275
00276
00277 TestResult testResult(runTimes, descriptor->Iterations);
00278
00279
00280 for (std::size_t outputterIndex = 0;
00281 outputterIndex < outputters.size();
00282 outputterIndex++)
00283 outputters[outputterIndex]->EndTest(
00284 descriptor->FixtureName,
00285 descriptor->TestName,
00286 descriptor->Parameters,
00287 testResult
00288 );
00289
00290 }
00291
00292
00293 for (std::size_t outputterIndex = 0;
00294 outputterIndex < outputters.size();
00295 outputterIndex++)
00296 outputters[outputterIndex]->End(enabledCount,
00297 disabledCount);
00298 }
00299
00300
00302 static std::vector<const TestDescriptor*> ListTests()
00303 {
00304 std::vector<const TestDescriptor*> tests;
00305 Benchmarker& instance = Instance();
00306
00307 std::size_t index = 0;
00308
00309 while (index < instance._tests.size())
00310 {
00311 tests.push_back(instance._tests[index++]);
00312 }
00313
00314 return tests;
00315 }
00316
00317
00319
00321 static void ShuffleTests()
00322 {
00323 Benchmarker& instance = Instance();
00324 std::random_shuffle(instance._tests.begin(),
00325 instance._tests.end());
00326 }
00327 private:
00329
00331 struct CalibrationModel
00332 {
00333 public:
00334 CalibrationModel(std::size_t scale,
00335 uint64_t slope,
00336 uint64_t yIntercept)
00337 : Scale(scale),
00338 Slope(slope),
00339 YIntercept(yIntercept)
00340 {
00341
00342 }
00343
00344
00346
00348 const std::size_t Scale;
00349
00350
00352 const uint64_t Slope;
00353
00354
00356 const uint64_t YIntercept;
00357
00358
00360 int64_t GetCalibration(std::size_t iterations) const
00361 {
00362 return YIntercept + (iterations * Slope) / Scale;
00363 }
00364 };
00365
00366
00368 Benchmarker()
00369 {
00370
00371 }
00372
00373
00375 ~Benchmarker()
00376 {
00377
00378 std::size_t index = _tests.size();
00379
00380 while (index--)
00381 {
00382 delete _tests[index];
00383 }
00384 }
00385
00386
00388 std::vector<TestDescriptor*> GetTests() const
00389 {
00390 std::vector<TestDescriptor*> tests;
00391
00392 std::size_t index = 0;
00393
00394 while (index < _tests.size())
00395 {
00396 tests.push_back(_tests[index++]);
00397 }
00398
00399 return tests;
00400 }
00401
00402
00404
00406 static bool FilterMatchesString(const char* filter,
00407 const std::string& str)
00408 {
00409 const char* patternStart = filter;
00410
00411 while (true)
00412 {
00413 if (PatternMatchesString(patternStart, str.c_str()))
00414 {
00415 return true;
00416 }
00417
00418
00419 patternStart = strchr(patternStart, ':');
00420
00421
00422 if (!patternStart)
00423 {
00424 return false;
00425 }
00426
00427
00428 patternStart++;
00429 }
00430 }
00431
00432
00434
00436 static bool PatternMatchesString(const char* pattern, const char* str)
00437 {
00438 switch (*pattern)
00439 {
00440 case '\0':
00441 case ':':
00442 return (*str == '\0');
00443
00444 case '?':
00445 return ((*str != '\0') &&
00446 (PatternMatchesString(pattern + 1, str + 1)));
00447
00448 case '*':
00449 return (((*str != '\0') &&
00450 (PatternMatchesString(pattern, str + 1))) ||
00451 (PatternMatchesString(pattern + 1, str)));
00452
00453 default:
00454 return ((*pattern == *str) &&
00455 (PatternMatchesString(pattern + 1, str + 1)));
00456 }
00457 }
00458
00459
00461
00463 static CalibrationModel GetCalibrationModel()
00464 {
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485 ::hayai::Test* test = new Test();
00486
00487 #define HAYAI_CALIBRATION_INTERESECT_RUNS 10000
00488
00489 #define HAYAI_CALIBRATION_RUNS 10
00490 #define HAYAI_CALIBRATION_SCALE 1000000
00491 #define HAYAI_CALIBRATION_PPR 6
00492
00493
00494 uint64_t
00495 interceptSum = 0,
00496 interceptMin = std::numeric_limits<uint64_t>::min(),
00497 interceptMax = 0;
00498
00499 for (std::size_t run = 0;
00500 run < HAYAI_CALIBRATION_INTERESECT_RUNS;
00501 ++run)
00502 {
00503 uint64_t intercept = test->Run(0);
00504 interceptSum += intercept;
00505
00506 if (intercept < interceptMin)
00507 {
00508 interceptMin = intercept;
00509 }
00510
00511 if (intercept > interceptMax)
00512 {
00513 interceptMax = intercept;
00514 }
00515 }
00516
00517 uint64_t interceptAvg =
00518 interceptSum / HAYAI_CALIBRATION_INTERESECT_RUNS;
00519
00520
00521 std::vector<uint64_t> x(HAYAI_CALIBRATION_RUNS *
00522 HAYAI_CALIBRATION_PPR);
00523 std::vector<uint64_t> t(HAYAI_CALIBRATION_RUNS *
00524 HAYAI_CALIBRATION_PPR);
00525
00526 std::size_t point = 0;
00527
00528 for (std::size_t run = 0; run < HAYAI_CALIBRATION_RUNS; ++run)
00529 {
00530 #define HAYAI_CALIBRATION_POINT(_x) \
00531 x[point] = _x; \
00532 t[point++] = \
00533 test->Run(_x * std::size_t(HAYAI_CALIBRATION_SCALE))
00534
00535 HAYAI_CALIBRATION_POINT(1);
00536 HAYAI_CALIBRATION_POINT(2);
00537 HAYAI_CALIBRATION_POINT(5);
00538 HAYAI_CALIBRATION_POINT(10);
00539 HAYAI_CALIBRATION_POINT(15);
00540 HAYAI_CALIBRATION_POINT(20);
00541
00542 #undef HAYAI_CALIBRATION_POINT
00543 }
00544
00545
00546
00547
00548
00549 uint64_t
00550 sumProducts = 0,
00551 sumXSquared = 0;
00552
00553 std::size_t p = x.size();
00554
00555 while (p--)
00556 {
00557 sumXSquared += x[p] * x[p];
00558 sumProducts += x[p] * (t[p] - interceptAvg);
00559 }
00560
00561 uint64_t slope = sumProducts / sumXSquared;
00562
00563 delete test;
00564
00565 return CalibrationModel(HAYAI_CALIBRATION_SCALE,
00566 slope,
00567 interceptAvg);
00568
00569 #undef HAYAI_CALIBRATION_INTERESECT_RUNS
00570
00571 #undef HAYAI_CALIBRATION_RUNS
00572 #undef HAYAI_CALIBRATION_SCALE
00573 #undef HAYAI_CALIBRATION_PPR
00574 }
00575
00576
00577 std::vector<Outputter*> _outputters;
00578 std::vector<TestDescriptor*> _tests;
00579 std::vector<std::string> _include;
00580 };
00581 }
00582 #endif