Utopia 2
Framework for studying models of complex & adaptive systems.
Loading...
Searching...
No Matches
HdfBench.hh
Go to the documentation of this file.
1#ifndef UTOPIA_MODELS_HDFBENCH_HH
2#define UTOPIA_MODELS_HDFBENCH_HH
3
4#include <map>
5#include <vector>
6#include <chrono>
7#include <limits>
8#include <numeric>
9#include <functional>
10#include <thread>
11#include <iostream>
12
13#include <hdf5.h>
14#include <boost/iterator/counting_iterator.hpp>
15
16#include <utopia/core/model.hh>
17#include <utopia/core/types.hh>
18
19
20namespace Utopia {
21namespace Models {
22namespace HdfBench {
23
26
27
29
35 public Model<HdfBenchModel, HdfBenchModelTypes>
36{
37public:
40
42 using DataSet = typename Base::DataSet;
43
45 using Config = typename Base::Config;
46
47
48 // -- Types for time handling -- //
50 using Clock = std::chrono::high_resolution_clock;
51
53 using Time = std::chrono::high_resolution_clock::time_point;
54
56 using DurationType = std::chrono::duration<double>;
57
59 using BenchFunc = std::function<double(const std::string, Config)>;
60
61
62private:
63 // Base members: _time, _name, _cfg, _hdfgrp, _rng, _monitor
64
65
66 // -- Members of this model -- //
68 std::map<std::string, BenchFunc> _setup_funcs;
69
71 std::map<std::string, BenchFunc> _write_funcs;
72
74 const std::vector<std::string> _benchmarks;
75
77 const std::map<std::string, Config> _bench_cfgs;
78
80 std::map<std::string, double> _times;
81
82
83 // -- Datasets -- //
85 std::shared_ptr<DataSet> _dset_times;
86
88 std::map<std::string, std::shared_ptr<DataSet>> _dsets;
89
90
91 // -- Configuration parameters applicable to all benchmarks -- //
94
96 const std::chrono::duration<double> _sleep_step;
97
99 const std::chrono::duration<double> _sleep_bench;
100
101
102
103 // -- Construction helper functions -- //
105 std::map<std::string, Config> load_benchmarks() {
106 this->_log->debug("Loading benchmark configurations ...");
107 std::map<std::string, Config> cfg;
108
109 for (const auto &bname : _benchmarks) {
110 this->_log->trace("Loading benchmark configuration '{}' ...",
111 bname);
112
113 try {
114 cfg[bname] = get_as<Config>(bname, this->_cfg);
115 }
116 catch (std::exception &e) {
117 std::cerr << "Could not find a benchmark configuration with "
118 "name '" << bname << "'! Make sure the given "
119 "configuration contains such an entry."
120 << std::endl << "Original error message: ";
121 throw;
122 }
123 }
124
125 this->_log->debug("Got {} benchmark configurations.", cfg.size());
126 return cfg;
127 }
128
129public:
131
141 template<class ParentModel>
143 const std::string& name,
145 const DataIO::Config& custom_cfg = {}
146 )
147 :
148 // Initialize first via base model
150
151 // Set maps for setup and write functions
152 _setup_funcs(),
153 _write_funcs(),
154
155 // Get the set of enabled benchmarks from the config
156 _benchmarks(get_as<std::vector<std::string>>("benchmarks", this->_cfg)),
158
159 // Create the temporary map for measured times and the times dataset
160 _times(),
161 _dset_times(this->create_dset("times", {_benchmarks.size()})),
162 _dsets(),
163
164 // Extract config parameters applicable to all benchmarks
165 _delete_afterwards(get_as<bool>("delete_afterwards", this->_cfg)),
166 _sleep_step(get_as<double>("sleep_step", this->_cfg)),
167 _sleep_bench(get_as<double>("sleep_bench", this->_cfg))
168 {
169 // Check arguments
170 if (_delete_afterwards) {
171 throw std::invalid_argument("delete_afterwards feature is not yet "
172 "implemented!");
173 }
174
175 // Set up the function mappings . . . . . . . . . . . . . . . . . . . .
176 // FIXME Creating func maps should be possible in initializer list, but
177 // although it compiles, it leads to segfaults ...
178
179 this->_log->debug("Associating setup functions ...");
180 _setup_funcs["setup_nd"] = setup_nd;
181 _setup_funcs["setup_nd_with_chunks"] = setup_nd_with_chunks;
182
183
184 this->_log->debug("Associating write functions ...");
185 _write_funcs["write_const"] = write_const;
186
187
188 this->_log->debug("Associated {} setup and {} write function(s).",
189 _setup_funcs.size(), _write_funcs.size());
190
191
192 // Carry out the setup benchmark . . . . . . . . . . . . . . . . . . .
193 const bool initial_write = get_as<bool>("initial_write", this->_cfg);
194 this->_log->debug("initial_write: {}, sleep_step: {}s, "
195 "sleep_bench: {}s", initial_write ? "yes" : "no",
197
198 this->_log->info("Performing setup and initial benchmarks ...");
199
200 for (const auto &bname : _benchmarks) {
201 // Setup the dataset and store the time needed
203
204 // Perform one write operation, if configured to do so, and add
205 // the time on top
206 if (initial_write) {
207 _times[bname] += this->benchmark(bname);
208 }
209 }
210
211
212 // Add information to the dataset attributes
213 _dset_times->add_attribute("dim_name__1", "benchmark");
214 _dset_times->add_attribute("coords__benchmark", _benchmarks);
215 _dset_times->add_attribute("initial_write", initial_write);
216
217 // Done now.
218 this->_log->debug("Finished constructing HdfBench '{}'.", this->_name);
219 }
220
221 // Runtime functions ......................................................
222
229 void perform_step () {
230 // Sleep before the actual step is carried out
231 std::this_thread::sleep_for(_sleep_step);
232 // NOTE Duration might be zero, not triggering a sleep. Same below.
233
234 // Carry out the benchmarks, optionally sleeping some time before that
235 for (const auto &bname : _benchmarks) {
236 std::this_thread::sleep_for(_sleep_bench);
237
238 _times[bname] = this->benchmark(bname);
239 }
240 }
241
242
244
249 void monitor ()
250 {
251 // Can supply information to the monitor here in two ways:
252 // this->_monitor.set_entry("key", value);
253 // this->_monitor.set_entry("key", [this](){return 42.;});
254 }
255
256
258 void write_data () {
259 _dset_times->write(_benchmarks.begin(), _benchmarks.end(),
260 [this](const auto& bname) {
261 return this->_times.at(bname);
262 });
263 }
264
265
266protected:
267
268 // Benchmarking ...........................................................
269
271 template<bool setup=false>
272 double benchmark(const std::string &bname) {
273 // Get the benchmark configuration entry
274 const auto bcfg = _bench_cfgs.at(bname);
275
276 // Get the name of the setup/benchmark function, then resolve it
278 if constexpr (setup) {
279 bfunc = _setup_funcs.at(get_as<std::string>("setup_func", bcfg));
280 }
281 else {
282 bfunc = _write_funcs.at(get_as<std::string>("write_func", bcfg));
283 }
284
285 // Call the function; its return value is the time it took to execute
286 const auto btime = bfunc(bname, bcfg);
287
288 // Log the time, then return it
289 this->_log->debug("Benchmark result {:>20s} {} : {:>10.3f} ms",
290 bname, setup ? "setup" : "write", btime * 1E3);
291 return btime;
292 }
293
295 double time_since(const Time start) {
296 return time_between(start, Clock::now());
297 }
298
300 double time_between(const Time start, const Time end) {
301 const DurationType seconds = abs(end - start);
302 return seconds.count();
303 }
304
305
306 // Setup functions ........................................................
307
308 /* @brief Sets up an n-dimensional dataset
309 * @details The dataset shape corresponds to the write_shape argument, but
310 * with an extra dimension in front that has as extend time_max + 1
311 */
312 BenchFunc setup_nd = [this](const auto& bname, auto cfg){
313 // Determine the shape of the final dataset
314 auto shape = get_as<std::vector<hsize_t>>("write_shape", cfg);
315 shape.insert(shape.begin(), this->get_time_max() + 1);
316
317 const auto start = Clock::now();
318 // -- benchmark start -- //
319
320 // Create the dataset and set its capacity
321 _dsets[bname] = this->_hdfgrp->open_dataset(bname);
322 _dsets[bname]->set_capacity(shape);
323
324 // --- benchmark end --- //
325 return time_since(start);
326 };
327
328
329 BenchFunc setup_nd_with_chunks = [this](const auto& bname, auto cfg){
330 // Call the regular setup_nd to set up the dataset
331 const auto time_setup = this->setup_nd(bname, cfg);
332
333 // Extract the chunks argument
334 const auto chunks = get_as<std::vector<hsize_t>>("chunks", cfg);
335
336 const auto start = Clock::now();
337 // -- benchmark start -- //
338
339 // Set the chunks value
340 _dsets[bname]->set_chunksize(chunks);
341
342 // --- benchmark end --- //
343 return time_setup + time_since(start);
344 };
345
346
347 // Write functions ........................................................
348
350 BenchFunc write_const = [this](const auto& bname, auto cfg){
351 // Determine the value to write
352 const auto val = get_as<double>("const_val", cfg);
353
354 // Determine iterator length by factorizing the shape
355 const auto shape = get_as<std::vector<std::size_t>>("write_shape", cfg);
356 const auto it_len = std::accumulate(shape.begin(), shape.end(),
357 1, std::multiplies<std::size_t>());
358
359 const auto start = Clock::now();
360 // -- benchmark start -- //
361
362 // Can use the counting iterator as surrogate
363 _dsets[bname]->write(boost::counting_iterator<std::size_t>(0),
364 boost::counting_iterator<std::size_t>(it_len),
365 [&val]([[maybe_unused]] auto &count){
366 return val;
367 });
368
369 // --- benchmark end --- //
370 return time_since(start);
371 };
372};
373
374
375} // namespace HdfBench
376} // namespace Models
377} // namespace Utopia
378
379#endif // UTOPIA_MODELS_HDFBENCH_HH
Base class interface for Models using the CRT Pattern.
Definition model.hh:112
const std::shared_ptr< DataGroup > _hdfgrp
The HDF group this model instance should write its data to.
Definition model.hh:176
Time get_time_max() const
Return the maximum time possible for this model.
Definition model.hh:403
typename ModelTypes::DataSet DataSet
Data type that is used for storing data.
Definition model.hh:125
const Config _cfg
Config node belonging to this model instance.
Definition model.hh:158
const std::string _name
Name of the model instance.
Definition model.hh:149
typename ModelTypes::Config Config
Data type that holds the configuration.
Definition model.hh:116
const std::shared_ptr< spdlog::logger > _log
The (model) logger.
Definition model.hh:164
std::shared_ptr< DataSet > create_dset(const std::string name, const std::shared_ptr< DataGroup > &hdfgrp, std::vector< hsize_t > add_write_shape, const std::size_t compression_level=1, const std::vector< hsize_t > chunksize={})
Create a new dataset within the given group.
Definition model.hh:752
The HdfBench Model.
Definition HdfBench.hh:36
void monitor()
Monitor model information.
Definition HdfBench.hh:249
const std::vector< std::string > _benchmarks
Names of benchmarks.
Definition HdfBench.hh:74
typename Base::DataSet DataSet
Data type for a dataset.
Definition HdfBench.hh:42
std::map< std::string, Config > load_benchmarks()
Load the benchmark configurations into a map.
Definition HdfBench.hh:105
std::map< std::string, double > _times
The results of the measurements, stored under the benchmark name.
Definition HdfBench.hh:80
double time_since(const Time start)
Returns the time (in seconds) since the given time point.
Definition HdfBench.hh:295
std::shared_ptr< DataSet > _dset_times
Dataset to store the write times in.
Definition HdfBench.hh:85
double benchmark(const std::string &bname)
Carries out the benchmark associated with the given name.
Definition HdfBench.hh:272
HdfBenchModel(const std::string &name, ParentModel &parent_model, const DataIO::Config &custom_cfg={})
Construct the HdfBench model.
Definition HdfBench.hh:142
std::function< double(const std::string, Config)> BenchFunc
Type of a benchmark function pointer.
Definition HdfBench.hh:59
Model< HdfBenchModel, HdfBenchModelTypes > Base
The base model type.
Definition HdfBench.hh:39
const std::chrono::duration< double > _sleep_step
Sleep time in seconds at the beginning of each step.
Definition HdfBench.hh:96
std::chrono::high_resolution_clock Clock
Type of clock.
Definition HdfBench.hh:50
std::map< std::string, std::shared_ptr< DataSet > > _dsets
Dataset to write test data to are stored in a map of dataset pointers.
Definition HdfBench.hh:88
void write_data()
Write the result times of each benchmark.
Definition HdfBench.hh:258
double time_between(const Time start, const Time end)
Returns the absolute time (in seconds) between the given time points.
Definition HdfBench.hh:300
typename Base::Config Config
Data type that holds the configuration.
Definition HdfBench.hh:45
std::chrono::duration< double > DurationType
Type of the duration measure, should be a floating-point type.
Definition HdfBench.hh:56
const std::chrono::duration< double > _sleep_bench
Sleep time in seconds before each benchmark.
Definition HdfBench.hh:99
BenchFunc write_const
Writes a constant value into the dataset.
Definition HdfBench.hh:350
const std::map< std::string, Config > _bench_cfgs
Configuration for the benchmarks.
Definition HdfBench.hh:77
BenchFunc setup_nd_with_chunks
Definition HdfBench.hh:329
std::map< std::string, BenchFunc > _write_funcs
A map of implemented write functions.
Definition HdfBench.hh:71
const bool _delete_afterwards
Whether to delete datasets after the last step.
Definition HdfBench.hh:93
std::chrono::high_resolution_clock::time_point Time
Type of a time point, retrieved from the clock.
Definition HdfBench.hh:53
void perform_step()
Iterate a single step.
Definition HdfBench.hh:229
std::map< std::string, BenchFunc > _setup_funcs
A map of implemented setup functions for datasets.
Definition HdfBench.hh:68
BenchFunc setup_nd
Definition HdfBench.hh:312
YAML::Node Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition types.hh:71
ReturnType get_as(const std::string &key, const DataIO::Config &node)
This function is a wrapper around the yaml-cpp YAML::Node::as function.
Definition cfg_utils.hh:158
Container select_entities(const Manager &mngr, const DataIO::Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition select.hh:213
Definition agent.hh:11
Definition parallel.hh:235
Wrapper struct for defining model class data types.
Definition model.hh:92