HEBench
hebench_idata_loader.cpp
Go to the documentation of this file.
1 
2 // Copyright (C) 2021 Intel Corporation
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #include <algorithm>
6 #include <cassert>
7 #include <cstring>
8 #include <functional>
9 #include <sstream>
10 #include <stdexcept>
11 
12 #include "hebench_dataset_loader.h"
13 
14 #include "../include/hebench_idata_loader.h"
15 
16 namespace hebench {
17 namespace TestHarness {
18 
19 //-------------------
20 // class IDataLoader
21 //-------------------
22 
24 {
25  std::size_t retval = 0;
26 
27  switch (data_type)
28  {
30  retval = sizeof(std::int32_t);
31  break;
32 
34  retval = sizeof(std::int64_t);
35  break;
36 
38  retval = sizeof(float);
39  break;
40 
42  retval = sizeof(double);
43  break;
44 
45  default:
46  throw std::invalid_argument(IL_LOG_MSG_CLASS("Unknown data type."));
47  break;
48  } // end switch
49 
50  return retval;
51 }
52 
54 IDataLoader::createDataPackCollection(std::uint64_t data_pack_count)
55 {
57  hebench::APIBridge::DataPack *p_data_packs = nullptr;
58 
59  try
60  {
61  p_data_packs = new hebench::APIBridge::DataPack[data_pack_count];
65  if (p)
66  {
67  if (p->p_data_packs)
68  delete[] p->p_data_packs;
69  delete p;
70  }
71  });
72 
73  retval->pack_count = data_pack_count;
74  retval->p_data_packs = p_data_packs;
75  }
76  catch (...)
77  {
78  if (p_data_packs)
79  delete[] p_data_packs;
80  throw;
81  }
82 
83  return retval;
84 }
85 
87 IDataLoader::createDataPack(std::uint64_t buffer_count, std::uint64_t param_position)
88 {
90  hebench::APIBridge::NativeDataBuffer *p_buffers = nullptr;
91 
92  try
93  {
94  p_buffers = new hebench::APIBridge::NativeDataBuffer[buffer_count];
98  if (p)
99  {
100  if (p->p_buffers)
101  delete[] p->p_buffers;
102  delete p;
103  }
104  });
105 
106  retval->buffer_count = buffer_count;
107  retval->param_position = param_position;
108  retval->p_buffers = p_buffers;
109  }
110  catch (...)
111  {
112  if (p_buffers)
113  delete[] p_buffers;
114  throw;
115  }
116 
117  return retval;
118 }
119 
121 IDataLoader::createDataBuffer(std::uint64_t size, std::int64_t tag)
122 {
124  std::uint8_t *p_buffer = nullptr;
125 
126  try
127  {
128  p_buffer = new std::uint8_t[size];
132  if (p)
133  {
134  if (p->p)
135  {
136  std::uint8_t *p_tmp = reinterpret_cast<std::uint8_t *>(p->p);
137  delete[] p_tmp;
138  }
139  delete p;
140  }
141  });
142 
143  retval->size = size;
144  retval->tag = tag;
145  retval->p = p_buffer;
146  }
147  catch (...)
148  {
149  if (p_buffer)
150  delete[] p_buffer;
151  throw;
152  }
153 
154  return retval;
155 }
156 
157 //-------------------------------
158 // class PartialDataLoaderHelper
159 //-------------------------------
160 
161 template <typename T>
163 {
164 private:
165  IL_DECLARE_CLASS_NAME(PartialDataLoaderHelper)
166 public:
167  static void init(PartialDataLoader &data_loader,
168  std::size_t input_dim,
169  const std::size_t *input_sample_count_per_dim,
170  const std::uint64_t *input_count_per_dim,
171  std::size_t output_dim,
172  const std::uint64_t *output_count_per_dim,
173  bool allocate_output);
174  static void loadFromFile(PartialDataLoader &data_loader,
175  const std::string &filename,
176  std::size_t expected_input_dim,
177  const std::size_t *max_input_sample_count_per_dim,
178  const std::uint64_t *expected_input_count_per_dim,
179  std::size_t expected_output_dim,
180  const std::uint64_t *expected_output_count_per_dim);
181 };
182 
183 template <typename T>
185  std::size_t input_dim,
186  const std::size_t *input_sample_count_per_dim,
187  const std::uint64_t *input_count_per_dim,
188  std::size_t output_dim,
189  const std::uint64_t *output_count_per_dim,
190  bool allocate_output)
191 {
192  if (input_dim <= 0)
193  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid input dimensions: 'input_dim' must be positive."));
194  if (output_dim <= 0)
195  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid output dimensions: 'output_dim' must be positive."));
196 
197  data_loader.m_input_data.resize(input_dim);
198  data_loader.m_output_data.resize(output_dim);
199 
200  std::size_t output_sample_count_per_dim = 1;
201  for (std::size_t i = 0; i < data_loader.m_input_data.size(); ++i)
202  {
203  if (input_sample_count_per_dim[i] <= 0)
204  {
205  std::stringstream ss;
206  ss << "Invalid batch size for dimension " << i << ": 'input_count_per_dim[" << i << "]' must be positive.";
207  throw std::invalid_argument(IL_LOG_MSG_CLASS(ss.str()));
208  } // end if
209 
210  output_sample_count_per_dim *= input_sample_count_per_dim[i];
211  data_loader.m_input_data[i] = data_loader.createDataPack(input_sample_count_per_dim[i], i);
212  } // end for
213 
214  for (std::size_t i = 0; i < data_loader.m_output_data.size(); ++i)
215  {
216  data_loader.m_output_data[i] = data_loader.createDataPack(output_sample_count_per_dim, i);
217  } // end for
218 
219  // allocate buffers for the data
220 
221  std::size_t single_size = sizeof(T);
222  std::vector<std::uint64_t> input_buffer_sizes(input_dim);
223  std::vector<std::uint64_t> output_buffer_sizes(output_dim);
224  std::transform(input_count_per_dim, input_count_per_dim + input_dim, input_buffer_sizes.begin(),
225  [single_size](std::uint64_t n) -> std::uint64_t { return n * single_size; });
226  std::transform(output_count_per_dim, output_count_per_dim + output_dim, output_buffer_sizes.begin(),
227  [single_size](std::uint64_t n) -> std::uint64_t { return n * single_size; });
228  data_loader.allocate(input_buffer_sizes.data(), input_buffer_sizes.size(),
229  output_buffer_sizes.data(), output_buffer_sizes.size(),
230  allocate_output);
231 }
232 
233 template <typename T>
235  const std::string &filename,
236  std::size_t expected_input_dim,
237  const std::size_t *max_input_sample_count_per_dim,
238  const std::uint64_t *expected_input_count_per_dim,
239  std::size_t expected_output_dim,
240  const std::uint64_t *expected_output_count_per_dim)
241 {
242  if (expected_input_dim <= 0)
243  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid input dimensions: 'expected_input_dim' must be positive."));
244  if (expected_output_dim <= 0)
245  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid output dimensions: 'expected_output_dim' must be positive."));
246 
248  std::size_t max_output_sample_count = 1;
249 
250  // validate loaded data
251 
252  // inputs
253  if (dataset.inputs.size() != expected_input_dim)
254  {
255  std::stringstream ss;
256  ss << "Loaded input dimensions do not match the number of parameters for the operation. "
257  << "Expected " << expected_input_dim << ", but " << dataset.inputs.size() << "loaded.";
258  throw std::runtime_error(IL_LOG_MSG_CLASS(ss.str()));
259  } // end if
260  for (std::size_t input_dim_i = 0; input_dim_i < dataset.inputs.size(); ++input_dim_i)
261  {
262  std::vector<std::vector<T>> &input_component = dataset.inputs[input_dim_i];
263  if (input_component.size() < max_input_sample_count_per_dim[input_dim_i])
264  {
265  std::stringstream ss;
266  ss << "Insufficient data loaded for operation input parameter " << input_dim_i << ". "
267  << "Expected " << max_input_sample_count_per_dim[input_dim_i] << " samples, but "
268  << input_component.size() << " found.";
269  throw std::runtime_error(IL_LOG_MSG_CLASS(ss.str()));
270  } // end if
271  if (input_component.size() > max_input_sample_count_per_dim[input_dim_i])
272  // discard excess data
273  input_component.resize(max_input_sample_count_per_dim[input_dim_i]);
274  max_output_sample_count *= input_component.size(); // count the samples into the output
275  // check each sample size
276  for (std::size_t input_sample_i = 0; input_sample_i < input_component.size(); ++input_sample_i)
277  {
278  if (input_component[input_sample_i].size() != expected_input_count_per_dim[input_dim_i])
279  {
280  std::stringstream ss;
281  ss << "Incorrect vector size loaded for input dimension " << input_dim_i << ", sample " << input_sample_i << ". "
282  << "Expected vector with " << expected_input_count_per_dim[input_dim_i] << " elements, but "
283  << input_component[input_sample_i].size() << " received.";
284  throw std::runtime_error(IL_LOG_MSG_CLASS(ss.str()));
285  } // end if
286  } // end for
287  } // end for
288 
289  // outputs
290  if (!dataset.outputs.empty()
291  && dataset.outputs.size() != expected_output_dim)
292  {
293  std::stringstream ss;
294  ss << "Loaded output dimensions do not match the dimensions of the result for the operation. "
295  << "Expected " << expected_output_dim << ", but " << dataset.outputs.size() << "loaded.";
296  throw std::runtime_error(IL_LOG_MSG_CLASS(ss.str()));
297  } // end if
298  for (std::size_t output_dim_i = 0; output_dim_i < dataset.outputs.size(); ++output_dim_i)
299  {
300  std::vector<std::vector<T>> &output_component = dataset.outputs[output_dim_i];
301  if (output_component.size() < max_output_sample_count)
302  {
303  // must have a ground truth for each combination of inputs
304  std::stringstream ss;
305  ss << "Insufficient ground-truth output samples loaded for output component " << output_dim_i << ". "
306  << "Expected, at least, " << max_input_sample_count_per_dim << " samples, but "
307  << output_component.size() << " received.";
308  throw std::runtime_error(IL_LOG_MSG_CLASS(ss.str()));
309  } // end if
310  if (output_component.size() > max_output_sample_count)
311  // discard excess data
312  output_component.resize(max_output_sample_count);
313  for (std::size_t output_sample_i = 0; output_sample_i < output_component.size(); ++output_sample_i)
314  {
315  if (output_component[output_sample_i].size() != expected_output_count_per_dim[output_dim_i])
316  {
317  std::stringstream ss;
318  ss << "Incorrect vector size loaded for output dimension " << output_dim_i << ", sample " << output_sample_i << ". "
319  << "Expected vector with " << expected_output_count_per_dim[output_dim_i] << " elements, but "
320  << output_component[output_sample_i].size() << " received.";
321  throw std::runtime_error(IL_LOG_MSG_CLASS(ss.str()));
322  } // end if
323  } // end for
324  } // end for
325 
326  // initialize the data loader object
327 
328  std::vector<std::size_t> input_sample_count_per_dim(dataset.inputs.size());
329  for (std::size_t i = 0; i < dataset.inputs.size(); ++i)
330  input_sample_count_per_dim[i] = dataset.inputs[i].size();
331  init(data_loader,
332  dataset.inputs.size(), input_sample_count_per_dim.data(), expected_input_count_per_dim,
333  expected_output_dim, expected_output_count_per_dim, !dataset.outputs.empty());
334 
335  assert(data_loader.getParameterCount() == dataset.inputs.size());
336  assert(data_loader.getResultCount() == expected_output_dim);
337 
338  // copy loaded data into internal allocation
339 
340  for (std::size_t input_dim_i = 0; input_dim_i < dataset.inputs.size(); ++input_dim_i)
341  {
342  // input parameter
343  const std::vector<std::vector<T>> &input_component = dataset.inputs[input_dim_i];
344  hebench::APIBridge::DataPack &data_pack = *data_loader.m_input_data[input_dim_i];
345  assert(data_pack.param_position == input_dim_i
346  && data_pack.buffer_count == input_component.size());
347  for (std::size_t sample_i = 0; sample_i < data_pack.buffer_count; ++sample_i)
348  {
349  // parameter sample
350  const std::vector<T> &input_component_sample = input_component[sample_i];
351  hebench::APIBridge::NativeDataBuffer &param_sample = data_pack.p_buffers[sample_i];
352  assert(param_sample.p
353  && param_sample.size == input_component_sample.size() * sizeof(T));
354  std::memcpy(param_sample.p, input_component_sample.data(), param_sample.size);
355  } // end for
356  } // end for
357 
358  for (std::size_t output_dim_i = 0; output_dim_i < dataset.outputs.size(); ++output_dim_i)
359  {
360  // output component
361  const std::vector<std::vector<T>> &output_component = dataset.outputs[output_dim_i];
362  hebench::APIBridge::DataPack &data_pack = *data_loader.m_output_data[output_dim_i];
363  assert(data_pack.param_position == output_dim_i
364  && data_pack.buffer_count == output_component.size());
365  for (std::size_t sample_i = 0; sample_i < data_pack.buffer_count; ++sample_i)
366  {
367  // parameter sample
368  const std::vector<T> &output_component_sample = output_component[sample_i];
369  hebench::APIBridge::NativeDataBuffer &result_component_sample = data_pack.p_buffers[sample_i];
370  assert(result_component_sample.p
371  && result_component_sample.size == output_component_sample.size() * sizeof(T));
372  std::memcpy(result_component_sample.p, output_component_sample.data(), result_component_sample.size);
373  } // end for
374  } // end for
375 }
376 
377 //-------------------------
378 // class PartialDataLoader
379 //-------------------------
380 
382  m_data_type(hebench::APIBridge::DataType::Int32),
383  m_b_is_output_allocated(false),
384  m_b_initialized(false)
385 {
386 }
387 
389  std::size_t input_dim,
390  const std::size_t *input_sample_count_per_dim,
391  const std::uint64_t *input_count_per_dim,
392  std::size_t output_dim,
393  const std::uint64_t *output_count_per_dim,
394  bool allocate_output)
395 {
396  switch (data_type)
397  {
399  PartialDataLoaderHelper<std::int32_t>::init(*this, input_dim, input_sample_count_per_dim, input_count_per_dim,
400  output_dim, output_count_per_dim,
401  allocate_output);
402  break;
403 
405  PartialDataLoaderHelper<std::int64_t>::init(*this, input_dim, input_sample_count_per_dim, input_count_per_dim,
406  output_dim, output_count_per_dim,
407  allocate_output);
408  break;
409 
411  PartialDataLoaderHelper<float>::init(*this, input_dim, input_sample_count_per_dim, input_count_per_dim,
412  output_dim, output_count_per_dim,
413  allocate_output);
414  break;
415 
417  PartialDataLoaderHelper<double>::init(*this, input_dim, input_sample_count_per_dim, input_count_per_dim,
418  output_dim, output_count_per_dim,
419  allocate_output);
420  break;
421 
422  default:
423  throw std::invalid_argument(IL_LOG_MSG_CLASS("Unknown 'data_type'."));
424  break;
425  } // end switch
426 
427  m_data_type = data_type;
428  m_b_initialized = true;
429 }
430 
431 void PartialDataLoader::init(const std::string &filename,
433  std::size_t expected_input_dim,
434  const std::size_t *max_input_sample_count_per_dim,
435  const std::uint64_t *expected_input_count_per_dim,
436  std::size_t expected_output_dim,
437  const std::uint64_t *expected_output_count_per_dim)
438 {
439  switch (data_type)
440  {
443  expected_input_dim, max_input_sample_count_per_dim, expected_input_count_per_dim,
444  expected_output_dim, expected_output_count_per_dim);
445  break;
446 
449  expected_input_dim, max_input_sample_count_per_dim, expected_input_count_per_dim,
450  expected_output_dim, expected_output_count_per_dim);
451  break;
452 
455  expected_input_dim, max_input_sample_count_per_dim, expected_input_count_per_dim,
456  expected_output_dim, expected_output_count_per_dim);
457  break;
458 
461  expected_input_dim, max_input_sample_count_per_dim, expected_input_count_per_dim,
462  expected_output_dim, expected_output_count_per_dim);
463  break;
464 
465  default:
466  throw std::invalid_argument(IL_LOG_MSG_CLASS("Unknown 'data_type'."));
467  break;
468  } // end switch
469 
470  m_data_type = data_type;
471  m_b_initialized = true;
472 }
473 
474 void PartialDataLoader::allocate(const std::uint64_t *input_buffer_sizes,
475  std::size_t input_buffer_sizes_count,
476  const std::uint64_t *output_buffer_sizes,
477  std::size_t output_buffer_sizes_count,
478  bool allocate_output)
479 {
480  // allocate data for parameters and results
481 
482  if (!input_buffer_sizes)
483  {
484  std::stringstream ss;
485  ss << "Invalid null `input_buffer_sizes`.";
486  throw std::invalid_argument(IL_LOG_MSG_CLASS(ss.str()));
487  } // end if
488  if (!output_buffer_sizes)
489  {
490  std::stringstream ss;
491  ss << "Invalid null `output_buffer_sizes`.";
492  throw std::invalid_argument(IL_LOG_MSG_CLASS(ss.str()));
493  } // end if
494 
495  m_b_is_output_allocated = allocate_output;
496 
497  // cache batch sizes
498  std::vector<std::uint64_t> input_batch_sizes(m_input_data.size());
499  for (std::size_t i = 0; i < m_input_data.size(); ++i)
500  input_batch_sizes[i] = m_input_data[i]->buffer_count;
501  std::vector<std::uint64_t> output_batch_sizes(m_output_data.size());
502  for (std::size_t i = 0; i < m_output_data.size(); ++i)
503  output_batch_sizes[i] = m_output_data[i]->buffer_count;
504 
505  if (input_buffer_sizes_count < input_batch_sizes.size())
506  {
507  std::stringstream ss;
508  ss << "Invalid number of input buffers `input_buffer_sizes_count`. Expected, at least "
509  << input_batch_sizes.size() << ", but " << input_buffer_sizes_count << " received.";
510  throw std::invalid_argument(IL_LOG_MSG_CLASS(ss.str()));
511  } // end if
512  if (output_buffer_sizes_count < output_batch_sizes.size())
513  {
514  std::stringstream ss;
515  ss << "Invalid number of output buffers `output_buffer_sizes_count`. Expected, at least "
516  << output_batch_sizes.size() << ", but " << output_buffer_sizes_count << " received.";
517  throw std::invalid_argument(IL_LOG_MSG_CLASS(ss.str()));
518  } // end if
519 
520  // compute total space required for the data buffers
521  std::uint64_t total_raw_size = 0;
522  std::uint64_t output_start;
523  for (std::size_t i = 0; i < input_batch_sizes.size(); ++i)
524  total_raw_size += input_buffer_sizes[i] * input_batch_sizes[i];
525  output_start = total_raw_size;
526  for (std::size_t i = 0; i < output_batch_sizes.size(); ++i)
527  total_raw_size += output_buffer_sizes[i] * (allocate_output ? output_batch_sizes[i] : 1);
528 
529  // allocate space for the data buffers
530  m_raw_buffer.resize(total_raw_size, 0);
531 
532  // m_raw_buffer has a chunk of memory allocated enough to hold the all the data and
533  // now, we have to point the NativeDataBuffers for each sample to the right locations:
534 
535  // param[0, 0] param[0, 1] param[0, 2] param[1, 0] param[1, 1] param[1, 2] ...
536  // v v v v v v
537  // [ ***********************************************************************************... ]
538 
539  // initialize the buffers
540 
541  // point parameter start buffers to correct start location
542  std::vector<std::uint8_t *> input_buffers(input_batch_sizes.size(), m_raw_buffer.data());
543  for (std::uint64_t i = 1; i < input_batch_sizes.size(); ++i)
544  input_buffers[i] = input_buffers[i - 1] + input_buffer_sizes[i - 1] * input_batch_sizes[i - 1];
545  if (!input_buffers.empty())
546  {
547  // make sure we are pointing to the right location in the master buffer
548  assert(input_buffers.front() == m_raw_buffer.data());
549  } // end if
550  std::vector<std::uint8_t *> output_buffers;
551  if (allocate_output)
552  {
553  output_buffers.resize(output_batch_sizes.size(), m_raw_buffer.data() + output_start);
554  for (std::uint64_t i = 1; i < output_batch_sizes.size(); ++i)
555  output_buffers[i] = output_buffers[i - 1] + output_buffer_sizes[i - 1] * output_batch_sizes[i - 1];
556  if (!output_buffers.empty())
557  {
558  // make sure we are pointing to the right location in the master buffer
559  assert(output_buffers.front() == m_raw_buffer.data() + output_start);
560  } //end if
561  } // end if
562 
563  // point the NativeDataBuffers for each data sample to the correct memory location
564 
565  // input
566  for (std::size_t param_i = 0; param_i < m_input_data.size(); ++param_i)
567  {
568  //#pragma omp parallel for
569  for (std::uint64_t i = 0; i < input_batch_sizes[param_i]; ++i)
570  {
571  // point to the start of the data in the raw data buffer
572  m_input_data[param_i]->p_buffers[i].p = input_buffers[param_i] + i * input_buffer_sizes[param_i];
573  m_input_data[param_i]->p_buffers[i].size = input_buffer_sizes[param_i];
574  m_input_data[param_i]->p_buffers[i].tag = 0;
575  } // end for
576  } // end for
577  // output
578  for (std::size_t output_i = 0; output_i < m_output_data.size(); ++output_i)
579  {
580  //#pragma omp parallel for
581  for (std::uint64_t i = 0; i < output_batch_sizes[output_i]; ++i)
582  {
583  // point to the start of the data in the raw data buffer
584  m_output_data[output_i]->p_buffers[i].p = allocate_output ?
585  output_buffers[output_i] + i * output_buffer_sizes[output_i] :
586  nullptr;
587  m_output_data[output_i]->p_buffers[i].size = output_buffer_sizes[output_i];
588  m_output_data[output_i]->p_buffers[i].tag = 0;
589  } // end for
590  } // end for
591 
592  // all data has been allocated and pointed to at this point
593 }
594 
595 std::vector<std::shared_ptr<hebench::APIBridge::DataPack>> PartialDataLoader::getResultTempDataPacks(std::uint64_t result_index) const
596 {
597  if (!m_b_initialized)
598  throw std::logic_error(IL_LOG_MSG_CLASS("Not initialized."));
599 
600  std::vector<std::shared_ptr<hebench::APIBridge::DataPack>> retval(m_output_data.size());
601 
602  for (std::size_t result_component_i = 0; result_component_i < m_output_data.size(); ++result_component_i)
603  {
604  if (!m_output_data[result_component_i]
605  || !m_output_data[result_component_i]->p_buffers)
606  throw std::logic_error(IL_LOG_MSG_CLASS("Description for output component " + std::to_string(result_component_i) + " is not initialized."));
607  if (result_index >= m_output_data[result_component_i]->buffer_count)
608  {
609  std::stringstream ss;
610  ss << "Out of range `result_index`."
611  << " Expected value less than " << m_output_data[result_component_i]->buffer_count << ", but "
612  << result_index << " received.";
613  throw std::out_of_range(IL_LOG_MSG_CLASS(ss.str()));
614  } // end if
615  retval[result_component_i] =
616  std::shared_ptr<hebench::APIBridge::DataPack>(new hebench::APIBridge::DataPack(),
618  if (p)
619  {
620  if (p->p_buffers)
621  {
622  for (std::uint64_t buffer_i = 0; buffer_i < p->buffer_count; ++buffer_i)
623  {
624  hebench::APIBridge::NativeDataBuffer &buffer = p->p_buffers[buffer_i];
625  if (buffer.p)
626  delete[] reinterpret_cast<std::int8_t *>(buffer.p);
627  } // end for
628  delete[] p->p_buffers;
629  } // end if
630  delete p;
631  } // end if
632  });
633  retval[result_component_i]->param_position = result_component_i;
634  retval[result_component_i]->buffer_count = 1;
635  retval[result_component_i]->p_buffers = new hebench::APIBridge::NativeDataBuffer[retval[result_component_i]->buffer_count];
636  retval[result_component_i]->p_buffers[0] = m_output_data[result_component_i]->p_buffers[result_index];
637  retval[result_component_i]->p_buffers[0].p = new std::int8_t[retval[result_component_i]->p_buffers[0].size];
638  } // end for
639 
640  return retval;
641 }
642 
643 const hebench::APIBridge::DataPack &PartialDataLoader::getParameterData(std::uint64_t param_position) const
644 {
645  if (!m_b_initialized)
646  throw std::logic_error(IL_LOG_MSG_CLASS("Not initialized."));
647 
648  if (!m_input_data.at(param_position))
649  throw std::runtime_error(IL_LOG_MSG_CLASS("Invalid null element accessed at 'param_position'."));
650 
651  return *m_input_data.at(param_position);
652 }
653 
654 const hebench::APIBridge::DataPack &PartialDataLoader::getResultData(std::uint64_t param_position) const
655 {
656  if (!m_b_initialized)
657  throw std::logic_error(IL_LOG_MSG_CLASS("Not initialized."));
658 
659  if (!m_output_data.at(param_position))
660  throw std::runtime_error(IL_LOG_MSG_CLASS("Invalid null element accessed at 'param_position'."));
661 
662  return *m_output_data.at(param_position);
663 }
664 
665 IDataLoader::ResultDataPtr PartialDataLoader::getResultFor(const std::uint64_t *param_data_pack_indices)
666 {
667  if (!m_b_initialized)
668  throw std::logic_error(IL_LOG_MSG_CLASS("Not initialized."));
669 
670  ResultDataPtr p_retval = ResultDataPtr(new ResultData());
671  std::vector<const hebench::APIBridge::NativeDataBuffer *> &retval = p_retval->result;
672  std::uint64_t r_i = getResultIndex(param_data_pack_indices);
673  p_retval->sample_index = r_i;
674 
675  retval.resize(getResultCount());
676  for (std::size_t result_component_i = 0; result_component_i < retval.size(); ++result_component_i)
677  {
678  const hebench::APIBridge::DataPack &result_component = getResultData(result_component_i);
679  assert((result_component.buffer_count == 0 || result_component.p_buffers)
680  && result_component.param_position == result_component_i);
681  if (r_i >= result_component.buffer_count)
682  {
683  std::stringstream ss;
684  ss << "Unexpected error! Result sample " << r_i << " for result component " << result_component_i << " not found.";
685  throw std::logic_error(IL_LOG_MSG_CLASS(ss.str()));
686  } // end if
687  retval[result_component_i] = result_component.p_buffers + r_i;
688  } // end for
689 
690  return p_retval;
691 }
692 
693 std::uint64_t PartialDataLoader::getResultIndex(const std::uint64_t *param_data_pack_indices) const
694 {
695  if (!m_b_initialized)
696  throw std::logic_error(IL_LOG_MSG_CLASS("Not initialized."));
697 
698  if (!param_data_pack_indices)
699  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid null argument 'param_data_pack_indices'."));
700 
701  std::uint64_t retval = getParameterCount() > 0 ?
702  param_data_pack_indices[0] :
703  0;
704 
705  for (std::size_t param_i = 1; param_i < getParameterCount(); ++param_i)
706  {
707  assert(getParameterData(param_i).param_position == param_i);
708  if (param_data_pack_indices[param_i] >= getParameterData(param_i).buffer_count)
709  {
710  std::stringstream ss;
711  ss << "Index out of range: 'param_data_pack_indices['" << param_i << "] == " << param_data_pack_indices[param_i] << ". "
712  << "Expected value less than " << getParameterData(param_i).buffer_count << ".";
713  throw std::out_of_range(IL_LOG_MSG_CLASS(ss.str()));
714  } // end if
715  retval = param_data_pack_indices[param_i] + getParameterData(param_i).buffer_count * retval;
716  } // end for
717 
718  return retval;
719 }
720 
721 } // namespace TestHarness
722 } // namespace hebench
static ExternalDataset< T > loadFromCSV(const std::string &filename, std::uint64_t max_loaded_size=0)
Loads a dataset from an external csv file that follows the defined structure.
static std::size_t sizeOf(hebench::APIBridge::DataType data_type)
static unique_ptr_custom_deleter< hebench::APIBridge::NativeDataBuffer > createDataBuffer(std::uint64_t size, std::int64_t tag)
static unique_ptr_custom_deleter< hebench::APIBridge::DataPackCollection > createDataPackCollection(std::uint64_t data_pack_count)
Creates shallow packed data that self cleans up.
std::shared_ptr< ResultData > ResultDataPtr
hebench::TestHarness::unique_ptr_custom_deleter< T > unique_ptr_custom_deleter
static unique_ptr_custom_deleter< hebench::APIBridge::DataPack > createDataPack(std::uint64_t buffer_count, std::uint64_t param_position)
Creates shallow data pack that self cleans up.
static void loadFromFile(PartialDataLoader &data_loader, const std::string &filename, std::size_t expected_input_dim, const std::size_t *max_input_sample_count_per_dim, const std::uint64_t *expected_input_count_per_dim, std::size_t expected_output_dim, const std::uint64_t *expected_output_count_per_dim)
static void init(PartialDataLoader &data_loader, std::size_t input_dim, const std::size_t *input_sample_count_per_dim, const std::uint64_t *input_count_per_dim, std::size_t output_dim, const std::uint64_t *output_count_per_dim, bool allocate_output)
Base class for data loaders and data generators.
std::vector< std::shared_ptr< hebench::APIBridge::DataPack > > getResultTempDataPacks() const
Retrieves a pre-allocated result providing memory space to store a single operation result sample.
std::uint64_t getResultCount() const override
Number of components in a result for the represented operation.
std::uint64_t getParameterCount() const override
Number of parameter components (operands) for the represented operation.
void init(hebench::APIBridge::DataType data_type, std::size_t input_dim, const std::size_t *input_sample_count_per_dim, const std::uint64_t *input_count_per_dim, std::size_t output_dim, const std::uint64_t *output_count_per_dim, bool allocate_output)
Initializes dimensions of inputs and outputs. No allocation is performed.
@ Float64
64 bits IEEE 754 standard floating point real numbers.
Definition: types.h:306
@ Int64
64 bits signed integers.
Definition: types.h:304
DataType
Defines data types for a workload.
Definition: types.h:379
@ Float32
32 bits IEEE 754 standard floating point real numbers.
Definition: types.h:382
@ Int32
32 bits signed integers.
Definition: types.h:380
DataPack * p_data_packs
Collection of data packs.
Definition: types.h:625
std::uint64_t size
Size of underlying data.
Definition: types.h:564
std::uint64_t param_position
The 0-based position of this parameter in the corresponding function call.
Definition: types.h:614
void * p
Pointer to underlying data.
Definition: types.h:558
std::uint64_t buffer_count
Number of data buffers in p_buffers.
Definition: types.h:613
NativeDataBuffer * p_buffers
Array of data buffers for parameter.
Definition: types.h:612
Defines a data package for an operation.
Definition: types.h:611
Defines a collection of data packs.
Definition: types.h:624
Structure to contain flexible data.
Definition: types.h:552
std::vector< std::vector< std::vector< T > > > inputs
Contains the samples for each input parameter as loaded from external source.
std::vector< std::vector< std::vector< T > > > outputs
Contains the samples for each result component as loaded from external source.
std::string to_string(const std::string_view &s)