1 #ifndef DUNE_PDELAB_COMMON_BENCHMARKHELPER_HH
2 #define DUNE_PDELAB_COMMON_BENCHMARKHELPER_HH
3 
4 #include <ostream>
5 #include <iomanip>
6 #include <map>
7 #include <string>
8 #include <vector>
9 #include <limits>
10 #include <cmath>
11 
12 #include <dune/common/exceptions.hh>
13 #include <dune/common/ios_state.hh>
14 
15 #include <ctime>
16 
17 namespace Dune {
18   namespace PDELab {
19 
20     struct CppClockWallTimeSource
21     {
22 
operator ()Dune::PDELab::CppClockWallTimeSource23       double operator()() const
24       {
25         return static_cast<double>(std::clock()) / static_cast<double>(CLOCKS_PER_SEC);
26       }
27 
28     };
29 
30 #if HAVE_MPI
31 #include"mpi.h"
32 
33     struct MPIWallTimeSource
34     {
35 
operator ()Dune::PDELab::MPIWallTimeSource36       double operator()() const
37       {
38         return MPI_Wtime();
39       }
40 
41     };
42 
43     typedef MPIWallTimeSource DefaultTimeSource;
44 
45 #else
46 
47     typedef CppClockWallTimeSource DefaultTimeSource;
48 
49 #endif
50 
51     struct Timing
52     {
53       double start;
54       double end;
55 
elapsedDune::PDELab::Timing56       double elapsed() const
57       {
58         return end - start;
59       }
60 
61     };
62 
63     struct BenchmarkEntry
64     {
65       std::vector<Timing> timings;
66       double min;
67       double max;
68       double avg;
69       double std_dev;
70     };
71 
72     template<typename TimeSource = DefaultTimeSource>
73     struct BenchmarkHelper
74     {
75 
BenchmarkHelperDune::PDELab::BenchmarkHelper76       BenchmarkHelper(std::string name, std::size_t max_runs = 1, TimeSource timeSource = TimeSource())
77         : _name(name)
78         , _time(timeSource)
79         , _run(0)
80         , _max_runs(max_runs)
81         , _statistics_stale(true)
82       {
83         _run_times.timings.resize(max_runs);
84       }
85 
86 
start_runDune::PDELab::BenchmarkHelper87       void start_run()
88       {
89         if (_run >= _max_runs)
90           {
91             DUNE_THROW(Dune::RangeError,"maximum number of benchmark runs exceeded");
92           }
93         _statistics_stale = true;
94         _run_times.timings[_run].start = _time();
95       }
96 
start_runDune::PDELab::BenchmarkHelper97       void start_run(std::ostream& s)
98       {
99         start_run();
100         ios_base_all_saver ios_saver(s);
101         s << _name << " (" << std::setw(2) << _run << " of " << std::setw(2) << _max_runs << ") " << std::flush;
102       }
103 
104 
end_runDune::PDELab::BenchmarkHelper105       void end_run()
106       {
107         _run_times.timings[_run].end = _time();
108         ++_run;
109       }
110 
end_runDune::PDELab::BenchmarkHelper111       void end_run(std::ostream& s)
112       {
113         end_run();
114         ios_base_all_saver ios_saver(s);
115         s << " " << std::setw(10) << std::setprecision(3) << _run_times.timings[_run-1].elapsed() << " sec" << std::endl;
116       }
117 
startDune::PDELab::BenchmarkHelper118       void start(std::string task)
119       {
120         std::pair<
121           std::map<std::string,BenchmarkEntry>::iterator,
122           bool
123           > res = _tasks.insert(make_pair(task,BenchmarkEntry()));
124         if (res.second)
125           res.first->second.timings.resize(_max_runs);
126         res.first->second.timings[_run].start = _time();
127         _statistics_stale = true;
128       }
129 
startDune::PDELab::BenchmarkHelper130       void start(std::string task, std::ostream& s)
131       {
132         start(task);
133       }
134 
endDune::PDELab::BenchmarkHelper135       void end(std::string task)
136       {
137         _tasks[task].timings[_run].end = _time();
138         _statistics_stale = true;
139       }
140 
endDune::PDELab::BenchmarkHelper141       void end(std::string task, std::ostream& s)
142       {
143         end(task);
144         s << "." << std::flush;
145       }
146 
update_entryDune::PDELab::BenchmarkHelper147       void update_entry(BenchmarkEntry& entry)
148       {
149         entry.min = std::numeric_limits<double>::max();
150         entry.max = 0;
151         entry.avg = 0;
152         entry.std_dev = 0;
153 
154         for (std::vector<Timing>::iterator it = entry.timings.begin(), end = entry.timings.end();
155              it != end;
156              ++it)
157           {
158             const double elapsed = it->elapsed();
159             entry.min = std::min(entry.min,elapsed);
160             entry.max = std::max(entry.max,elapsed);
161             entry.avg += elapsed;
162             entry.std_dev += elapsed*elapsed;
163           }
164 
165         entry.avg /= entry.timings.size();
166         entry.std_dev /= entry.timings.size();
167         entry.std_dev = std::sqrt(entry.std_dev - entry.avg*entry.avg);
168       }
169 
update_statisticsDune::PDELab::BenchmarkHelper170       void update_statistics()
171       {
172         _max_name_len = 5; // strlen("total")
173         for (std::map<std::string,BenchmarkEntry>::iterator it = _tasks.begin(), end = _tasks.end();
174              it != end;
175              ++it)
176           {
177             _max_name_len = std::max(_max_name_len,it->first.size());
178             update_entry(it->second);
179           }
180 
181         update_entry(_run_times);
182 
183         _statistics_stale = false;
184       }
185 
print_entryDune::PDELab::BenchmarkHelper186       void print_entry(std::ostream& s, std::string name, const BenchmarkEntry& entry, bool summary_only = false) const
187       {
188         s << std::setw(_max_name_len + 1) << std::left << name
189           << std::right << std::scientific << std::setw(10) << std::setprecision(2);
190         if (!summary_only)
191           for (std::vector<Timing>::const_iterator it = entry.timings.begin(),
192                  end = entry.timings.end();
193                it != end;
194                ++it)
195             {
196               s << std::setw(10) << it->elapsed();
197             }
198         s << std::setw(10) << entry.min
199           << std::setw(10) << entry.max
200           << std::setw(10) << entry.avg
201           << std::setw(10) << entry.std_dev;
202 
203         s << std::endl;
204       }
205 
printDune::PDELab::BenchmarkHelper206       void print(std::ostream& s, bool summary_only = false)
207       {
208         ios_base_all_saver ios_saver(s);
209 
210         if (_statistics_stale)
211           update_statistics();
212 
213         s << _name << " (" << std::setw(2) << _run << " of " << std::setw(2) << _max_runs << ") runs" << std::endl;
214 
215         s << std::setw(_max_name_len + 1) << "";
216 
217         if (!summary_only)
218           for (std::size_t i = 0; i < _max_runs; ++i)
219             s << std::setw(10) << i;
220 
221         s << std::setw(10) << "min"
222           << std::setw(10) << "max"
223           << std::setw(10) << "avg"
224           << std::setw(10) << "std_dev" << std::endl;
225 
226         for (std::map<std::string,BenchmarkEntry>::const_iterator it = _tasks.begin(), end = _tasks.end();
227              it != end;
228              ++it)
229           print_entry(s,it->first,it->second,summary_only);
230 
231         print_entry(s,"total",_run_times,summary_only);
232       }
233 
234     private:
235       const std::string _name;
236       TimeSource _time;
237       std::size_t _run;
238       const std::size_t _max_runs;
239       std::map<std::string,BenchmarkEntry> _tasks;
240       bool _statistics_stale;
241       BenchmarkEntry _run_times;
242       std::size_t _max_name_len;
243 
244     };
245 
246 
247 
248   } // namespace PDELAB
249 } // namespace Dune
250 
251 #endif // DUNE_PDELAB_COMMON_BENCHMARKHELPER_HH
252