SPH
Session.cpp
Go to the documentation of this file.
1 #include "bench/Session.h"
2 #include "io/FileSystem.h"
3 #include "io/Logger.h"
4 #include "system/Platform.h"
5 #include <algorithm>
6 
8 
10  logger = makeAuto<StdOutLogger>();
11  logger->setPrecision(3);
12  logger->setScientific(false);
13 }
14 
15 Session::~Session() = default;
16 
17 AutoPtr<Session> Session::instance;
18 
20  if (!instance) {
21  instance = makeAuto<Session>();
22  }
23  return *instance;
24 }
25 
26 void Session::registerBenchmark(const SharedPtr<Unit>& benchmark, const std::string& groupName) {
27  for (SharedPtr<Unit>& b : benchmarks) {
28  if (b->getName() == benchmark->getName()) {
29  status = makeFailed("Benchmark " + b->getName() + " defined more than once");
30  return;
31  }
32  }
33  benchmarks.push(benchmark);
34  Group& group = this->getGroupByName(groupName);
35  group.addBenchmark(benchmark);
36 }
37 
38 void Session::run(int argc, char* argv[]) {
39  Outcome result = this->parseArgs(argc, argv);
40  if (!result) {
41  this->logError(result.error());
42  return;
43  }
44 #ifdef SPH_DEBUG
45  this->log("Warning: running benchmark in debugging build");
46 #endif
47  if (!status) {
48  this->logError(status.error());
49  return;
50  }
51 
53  if (params.flags.has(Flag::MAKE_BASELINE)) {
54  params.target.mode = Mode::MAKE_BASELINE;
55  params.baseline.path = this->getBaselinePath();
56  FileSystem::removePath(params.baseline.path);
57  } else if (params.flags.has(Flag::RUN_AGAINST_BASELINE)) {
58  params.target.mode = Mode::RUN_AGAINST_BASELINE;
59  if (!baseline.parse(this->getBaselinePath())) {
60  this->logError("Invalid baseline format");
61  return;
62  }
63  }
64 
65  for (SharedPtr<Unit>& b : benchmarks) {
66  if (!params.benchmarksToRun.empty()) {
67  // check if we want to run the benchmark
68  Array<std::string>& names = params.benchmarksToRun;
69  if (std::find(names.begin(), names.end(), b->getName()) == names.end()) {
70  continue;
71  }
72  }
73 
74  try {
75  if (params.flags.has(Flag::RUN_AGAINST_BASELINE)) {
76  if (!baseline.isRecorded(b->getName())) {
77  params.target.iterateCnt = 1;
78  } else {
79  params.target.iterateCnt = baseline[b->getName()].iterateCnt;
80  }
81  }
82  Expected<Result> act = b->run(params.target);
83  if (!act) {
84  this->logError("Fail");
85  } else {
86  if (act->duration > params.maxAllowedDuration) {
87  this->log(
88  "Warning: benchmark ", b->getName(), " is takes too much time, t = ", act->duration);
89  }
90  if (params.flags.has(Flag::RUN_AGAINST_BASELINE)) {
91  if (baseline.isRecorded(b->getName())) {
92  Result result = baseline[b->getName()];
93  SPH_ASSERT(result.iterateCnt == act->iterateCnt, result.iterateCnt, act->iterateCnt);
94  this->log(b->getName() + " ran " + std::to_string(act->iterateCnt) + " iterations");
95  this->compareResults(act.value(), result);
96  } else {
97  this->log(b->getName() + " not recorded in the baseline");
98  }
99  } else {
100  this->log(b->getName(),
101  " completed in ",
102  act->duration,
103  " ms (",
104  act->iterateCnt,
105  " iterations)");
106  this->log(" ",
107  act->mean,
108  " +- ",
109  sqrt(act->variance),
110  " (min. ",
111  act->min,
112  ", max. ",
113  act->max,
114  ")");
115  if (params.flags.has(Flag::MAKE_BASELINE)) {
116  this->writeBaseline(b->getName(), act.value());
117  }
118  }
119  }
120  } catch (const std::exception& e) {
121  this->logError("Exception caught in benchmark " + b->getName() + ":\n" + e.what());
122  return;
123  }
124  }
125 }
126 
127 Path Session::getBaselinePath() {
129  Expected<std::string> sha = getGitCommit(Path("../../src/"), params.baseline.commit);
130  if (!sha) {
131  this->logError("Cannot determine git commit SHA");
132  return Path("");
133  }
134  this->log("Baseline for commit " + sha.value());
135  return Path("../../baseline/") / Path(sha.value());
136 }
137 
138 void Session::writeBaseline(const std::string& name, const Result& measured) {
139  FileLogger logger(params.baseline.path, FileLogger::Options::APPEND);
140  // clang-format off
141  logger.write(name, " / ", measured.duration, ", ", measured.iterateCnt, ", ",
142  measured.mean, ", ", measured.variance, ", ", measured.min, ", ", measured.max);
143  // clang-format on
144 }
145 
146 void Session::compareResults(const Result& measured, const Result& baseline) {
147  const Float diff = measured.mean - baseline.mean;
148  const Float sigma = params.confidence * sqrt(measured.variance + baseline.variance);
149  if (diff < -sigma) {
151  this->log(measured.duration, " < ", baseline.duration);
152  } else if (diff > sigma) {
154  this->log(measured.duration, " > ", baseline.duration);
155  } else {
157  this->log(measured.duration, " == ", baseline.duration);
158  }
159 }
160 
161 Group& Session::getGroupByName(const std::string& groupName) {
162  for (Group& group : groups) {
163  if (group.getName() == groupName) {
164  return group;
165  }
166  }
167  // if not found, create a new one
168  groups.emplaceBack(groupName);
169  return groups[groups.size() - 1];
170 }
171 
172 Outcome Session::parseArgs(int argc, char* argv[]) {
173  for (int i = 1; i < argc; ++i) { // first arg is path to the executable
174  std::string arg = argv[i];
175  SPH_ASSERT(!arg.empty());
176  if (arg == "-b") {
177  params.flags.set(Flag::MAKE_BASELINE);
178  } else if (arg == "-r") {
179  params.flags.set(Flag::RUN_AGAINST_BASELINE);
180  if (i < argc - 1) {
181  params.baseline.commit = std::stoi(argv[i + 1]);
182  i++;
183  }
184  } else if (arg == "--help") {
185  this->printHelp();
186  return Outcome(""); // empty error message to quit the program
187  } else if (arg[0] != '-') {
188  // not starting with '-', it must be a name of the benchmark or group to run
189  if (arg[0] == '[' && arg[arg.size() - 1] == ']') {
190  // find the group by name
191  Group& group = this->getGroupByName(arg);
192  for (auto& unit : group) {
193  params.benchmarksToRun.push(unit->getName());
194  }
195  } else {
196  // single benchmark
197  if (arg[0] == '\"' && arg[arg.size() - 1] == '\"') {
198  // trim the name
199  arg = arg.substr(1, arg.size() - 2);
200  }
201  params.benchmarksToRun.push(arg);
202  }
203  }
204  }
205  return SUCCESS;
206 }
207 
208 void Session::printHelp() {
209  logger->write("Benchmark. Options:\n -b Create baseline");
210 }
211 
212 template <typename... TArgs>
213 void Session::log(TArgs&&... args) {
214  if (!params.flags.has(Flag::SILENT)) {
215  logger->write(std::forward<TArgs>(args)...);
216  }
217 }
218 
219 template <typename... TArgs>
220 void Session::logError(TArgs&&... args) {
223  logger->write(std::forward<TArgs>(args)...);
224 }
225 
226 Register::Register(const SharedPtr<Unit>& benchmark, const std::string& groupName) {
227  Session::getInstance().registerBenchmark(benchmark, groupName);
228 }
229 
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
double Float
Precision used withing the code. Use Float instead of float or double where precision is important.
Definition: Globals.h:13
Logging routines of the run.
INLINE T sqrt(const T f)
Return a squared root of a value.
Definition: MathUtils.h:78
BasicOutcome< std::string > Outcome
Alias for string error message.
Definition: Outcome.h:138
const SuccessTag SUCCESS
Global constant for successful outcome.
Definition: Outcome.h:141
INLINE Outcome makeFailed(TArgs &&... args)
Constructs failed object with error message.
Definition: Outcome.h:157
Expected< std::string > getGitCommit(const Path &pathToGitRoot, const Size prev)
Returns git commit hash of the current or older commit as string.
Definition: Platform.cpp:63
System functions.
Benchmark.
@ RUN_AGAINST_BASELINE
Compare iteration numbers with recorded baseline.
@ MAKE_BASELINE
Store iteration numbers to baseline file.
#define NAMESPACE_BENCHMARK_BEGIN
Definition: Common.h:7
#define NAMESPACE_BENCHMARK_END
Definition: Common.h:8
INLINE Iterator< StorageType > end() noexcept
Definition: Array.h:462
INLINE Iterator< StorageType > begin() noexcept
Definition: Array.h:450
INLINE const TError & error() const
Returns the error message.
Definition: Outcome.h:88
Wrapper of type that either contains a value of given type, or an error message.
Definition: Expected.h:25
Type & value()
Returns the reference to expected value.
Definition: Expected.h:69
File output logger.
Definition: Logger.h:160
Definition: Session.h:147
void setPrecision(const Size newPrecision)
Changes the precision of printed numbers.
Definition: Logger.h:47
void write(TArgs &&... args)
Creates and logs a message by concatenating arguments.
Definition: Logger.h:37
void setScientific(const bool newScientific)
Sets/unsets scientific notation.
Definition: Logger.h:52
Object representing a path on a filesystem.
Definition: Path.h:17
Register(const SharedPtr< Unit > &benchmark, const std::string &groupName)
Definition: Session.cpp:226
Session()
Definition: Session.cpp:9
void run(int argc, char *argv[])
Runs all benchmarks.
Definition: Session.cpp:38
struct Session::@0::@1 baseline
std::string group
Run only selected group of benchmarks.
Definition: Session.h:264
void registerBenchmark(const SharedPtr< Unit > &benchmark, const std::string &groupName)
Adds a new benchmark into the session.
Definition: Session.cpp:26
static Session & getInstance()
Definition: Session.cpp:19
Outcome removePath(const Path &path, const Flags< RemovePathFlag > flags=EMPTY_FLAGS)
Definition: FileSystem.cpp:137
Definition: Session.h:36
uint64_t duration
Definition: Session.h:37
Float max
Definition: Session.h:42
Size iterateCnt
Definition: Session.h:38
Float min
Definition: Session.h:41
Float variance
Definition: Session.h:40
Float mean
Definition: Session.h:39