Evo C++ Library v0.5.1
benchmark.h
Go to the documentation of this file.
1 // Evo C++ Library
2 /* Copyright 2019 Justin Crowell
3 Distributed under the BSD 2-Clause License -- see included file LICENSE.txt for details.
4 */
6 
7 #pragma once
8 #ifndef INCL_evo_benchmark_h
9 #define INCL_evo_benchmark_h
10 
11 #include "fmt.h"
12 #include "io.h"
13 #include "thread.h"
14 #include "timer.h"
15 
17 
25 #define EVO_BENCH_SETUP(F, WARMUP) Benchmark bench(0, WARMUP); bench.scale(F)
26 
33 #define EVO_BENCH_RUN(F) bench.run(#F, F)
34 
44 #define EVO_BENCH_RUN2(F) { String evo_bench_name_(#F); F.get_name(evo_bench_name_); bench.run(evo_bench_name_, F); }
45 
47 
48 namespace evo {
51 
53 
182 class Benchmark {
183 public:
188  Benchmark(ulongl default_count=0, ulongl default_warmup_count=0) : default_count_(default_count), default_warmup_count_(default_warmup_count) {
189  }
190 
195  if (report_.size() > 0)
196  report();
197  }
198 
207  template<class T>
208  ulongl scale(const T& func, uint factor=1) {
209  const ulong THRESHOLD_MSEC = 100;
210  const ulong THRESHOLD_COUNT = 1000000000;
211  const ulongl MAX_COUNT = ULongL::MAX;
212  assert( factor > 0 );
213 
214  Timer timer;
215  timer.start();
216 
217  ulongl count = 1;
218  func();
219  while (timer.msec() < THRESHOLD_MSEC && count < MAX_COUNT) {
220  const ulongl target = count * (count > THRESHOLD_COUNT ? 2 : 10);
221  while (count < target) {
223  func();
224  ++count;
225  }
226  }
227 
228  if (count > MAX_COUNT / factor)
229  count = MAX_COUNT;
230  else
231  count *= factor;
232 
233  default_count_ = count;
234  return count;
235  }
236 
248  template<class T>
249  Benchmark& run(const SubString& name, const T& func, ulongl count, ulongl warmup_count=0) {
250  const ulongl WARMUP_COUNT = 100;
251  if (count < 1)
252  count = 1;
253  const ulongl start_count = count;
254 
255  if (warmup_count == 0)
256  warmup_count = WARMUP_COUNT;
257  for (; warmup_count > 0; --warmup_count)
258  func(); // warmup
259 
260  // Start a monitor thread to keep run from taking too long
261  AtomicInt monitor_flag;
262  monitor_flag.store(0);
263  Thread monitor(monitor_thread, &monitor_flag);
264  monitor.thread_start();
265 
266  Timer walltimer;
267  TimerCpu cputimer;
268  walltimer.start();
269  cputimer.start();
270  for (; count > 0 && monitor_flag.load() == 0; --count)
271  func();
272  cputimer.stop();
273  walltimer.stop();
274 
275  // Stop thread, if needed
276  monitor_flag.store(1);
277  monitor.thread_join();
278 
279  report_.add(ReportItem(name, walltimer.nsec(), cputimer.nsec(), start_count - count));
280  return *this;
281  }
282 
292  template<class T>
293  Benchmark& run(const SubString& name, const T& func) {
294  return run(name, func, default_count_, default_warmup_count_);
295  }
296 
299  report_.clear();
300  return *this;
301  }
302 
312  template<class T>
314  if (!report_.empty()) {
315  const SubString COLUMN_NAMES[] = {
316  "Name",
317  "Time(nsec)",
318  "CPU(nsec)",
319  "Count",
320  "AvgTime(nsec)",
321  "AvgCPU(nsec)",
322  "DiffBest(nsec)",
323  ""
324  };
325 
326  // Find lowest cpu time
327  const ReportItem* fastest = NULL;
328  for (ReportList::Iter iter(report_); iter; ++iter) {
329  const ReportItem& item = *iter;
330  if (fastest == NULL || item.cputime_nsec < fastest->cputime_nsec)
331  fastest = &item;
332  }
333 
334  // Tests
335  FmtTable table(COLUMN_NAMES, 0);
336  FmtTableOut<T> table_out(out, table, type);
337  for (ReportList::Iter iter(report_); iter; ++iter) {
338  const ReportItem& item = *iter;
339  const double cpu_avg_diff = (fastest == NULL ? 0.0 : ((double)item.cputime_nsec / item.count) - ((double)fastest->cputime_nsec / fastest->count));
340  table_out
341  << item.name << item.walltime_nsec << item.cputime_nsec << item.count
342  << ((double)item.walltime_nsec / item.count) << ((double)item.cputime_nsec / item.count)
343  << cpu_avg_diff << NL;
344  }
345  table_out << fFLUSH;
346  out << NL;
347 
348  report_.clear();
349  }
350  return *this;
351  }
352 
360  return report_out(con().out, type);
361  }
362 
363 private:
364  // Disable copying
365  Benchmark(const Benchmark&) EVO_ONCPP11(= delete);
366  Benchmark& operator=(const Benchmark&) EVO_ONCPP11(= delete);
367 
368  struct ReportItem {
369  String name;
370  ulongl walltime_nsec;
371  ulongl cputime_nsec;
372  ulongl count;
373 
374  ReportItem() : walltime_nsec(0), cputime_nsec(0), count(0) {
375  }
376 
377  ReportItem(const SubString& name, ulongl walltime_nsec, ulongl cputime_nsec, ulongl count) : name(name), walltime_nsec(walltime_nsec), cputime_nsec(cputime_nsec), count(count) {
378  }
379 
380  ReportItem(const ReportItem& src) : name(src.name), walltime_nsec(src.walltime_nsec), cputime_nsec(src.cputime_nsec), count(src.count) {
381  }
382  };
383 
385 
386  ulongl default_count_;
387  ulongl default_warmup_count_;
388  ReportList report_;
389 
390  static void monitor_thread(void* arg) {
391  const uint WAIT_TIME = 5000;
392  const uint WAIT_INC = 200;
393  AtomicInt& flag = *(AtomicInt*)arg;
394  for (uint msec = 0; msec < WAIT_TIME && flag.load() == 0; msec += WAIT_INC)
395  sleepms(WAIT_INC);
396  flag.store(1);
397  }
398 };
399 
401 
402 }
403 #endif
ListType & add(const Item *data, Size size)
Append new items copied from data pointer (modifier).
Definition: list.h:2019
Evo extended output formatting helpers.
ulongl nsec() const
Get current time elapsed in nanoseconds.
Definition: timer.h:125
Size size() const
Get size.
Definition: list.h:759
void store(T num, MemOrder mem_order=std::memory_order_seq_cst)
Store new value.
#define EVO_ATOMIC_SYNC
Full sync (sequentially consistent) memory barrier.
Definition: atomic.h:36
Benchmark & run(const SubString &name, const T &func)
Run benchmark on given function/functor with default repeat and wramup counts.
Definition: benchmark.h:293
This & stop()
Stop timer.
Definition: timer.h:153
Console & con()
Shortcut for Console::get().
Definition: io.h:1015
Timer that works like a stopwatch.
Definition: timer.h:56
Benchmark & report_out(T &out, FmtTable::Type type=FmtTable::tTEXT)
Write benchmark report to output stream or string.
Definition: benchmark.h:313
Random access iterator.
Definition: iter.h:904
Manages a single thread of execution.
Definition: thread.h:529
#define EVO_ONCPP11(EXPR)
Compile EXPR only if C++11 support is detected, otherwise this is a no-op.
Definition: sys.h:259
Benchmark(ulongl default_count=0, ulongl default_warmup_count=0)
Constructor.
Definition: benchmark.h:188
Text table formatter.
Definition: fmt.h:45
static const T MAX
Maximum interger value.
Definition: type.h:996
Evo I/O streams and Console I/O.
#define EVO_ATOMIC_FENCE(MEM_ORDER)
Sets a memory fence/barrier.
Definition: atomic.h:52
Output interface for writing text table to stream/string.
Definition: fmt.h:614
Evo Timer classes.
Evo threads implementation.
bool empty() const
Get whether empty.
Definition: list.h:753
String container.
Definition: string.h:674
bool thread_join()
Join thread by waiting for thread to stop.
Definition: thread.h:626
ulongl msec() const
Get current time elapsed in milliseconds.
Definition: timer.h:107
Benchmark & clear()
Clear current report.
Definition: benchmark.h:298
ulongl scale(const T &func, uint factor=1)
Scale the current default repeat count using given function/functor.
Definition: benchmark.h:208
Micro benchmarking class.
Definition: benchmark.h:182
ListType & clear()
Clear by removing all items.
Definition: list.h:574
static const NewlineDefault & NL
Default newline type.
Definition: sys.h:785
Type
Formatting type to use.
Definition: fmt.h:47
Evo C++ Library namespace.
Definition: alg.h:11
Definition: sys.h:739
bool thread_start()
Start thread.
Definition: thread.h:596
Basic text table using whitespace to line up columns.
Definition: fmt.h:48
bool sleepms(ulong msec)
Sleep for number of milliseconds.
Definition: sys.h:654
Benchmark & run(const SubString &name, const T &func, ulongl count, ulongl warmup_count=0)
Run benchmark on given function/functor.
Definition: benchmark.h:249
Reference and access existing string data.
Definition: substring.h:229
T load(MemOrder mem_order=std::memory_order_seq_cst) const
Load and return current value.
void start()
Start timer.
Definition: timer.h:133
~Benchmark()
Destructor.
Definition: benchmark.h:194
Benchmark & report(FmtTable::Type type=FmtTable::tTEXT)
Write benchmark report to stdout.
Definition: benchmark.h:359