HEBench
hebench_benchmark_category.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 <sstream>
8 
10 
11 #include "../include/hebench_benchmark_category.h"
12 #include "hebench/modules/general/include/hebench_math_utils.h"
14 
15 namespace hebench {
16 namespace TestHarness {
17 
18 //-------------------
19 // struct RAIIHandle
20 //-------------------
21 
23 {
24  handle.p = nullptr;
25  handle.size = 0;
26  handle.tag = 0;
27 }
28 
30  handle(rhs.handle)
31 {
32  rhs.detach();
33 }
34 
36 {
37  if (&rhs != this)
38  {
39  this->handle = rhs.handle;
40  rhs.detach();
41  } // end if
42  return *this;
43 }
44 
46 {
47  destroy();
48  this->handle = rhs;
49  return *this;
50 }
51 
53 {
54  destroy();
55 }
56 
58 {
59  return (!h.p && h.size == 0 && h.tag == 0);
60 }
61 
63 {
64  handle.p = nullptr;
65  handle.size = 0;
66  handle.tag = 0;
67 }
68 
70 {
72  if (!isEmpty(handle))
73  {
75  detach();
76  } // end if
77  return retval;
78 }
79 
80 //--------------------------------
81 // class PartialBenchmarkCategory
82 //--------------------------------
83 
84 PartialBenchmarkCategory::PartialBenchmarkCategory(std::shared_ptr<Engine> p_engine,
85  const IBenchmarkDescriptor::DescriptionToken &description_token) :
86  PartialBenchmark(p_engine, description_token)
87 {
88 }
89 
91 {
92 }
93 
95  const std::uint64_t *param_data_pack_indices,
96  const std::vector<hebench::APIBridge::NativeDataBuffer *> &outputs,
97  hebench::APIBridge::DataType data_type) const
98 {
99  static constexpr const std::size_t MaxErrorPrint = 10;
100  bool retval = true;
101  std::vector<std::uint64_t> is_valid;
102 
103  // extract the pointers to the actual results
104 
105  if (outputs.size() != dataset->getResultCount())
106  {
107  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid number of outputs: 'outputs'."));
108  }
109 
110  IDataLoader::ResultDataPtr ptr_truths = dataset->getResultFor(param_data_pack_indices);
111  const std::vector<const hebench::APIBridge::NativeDataBuffer *> &truths =
112  ptr_truths->result;
113 
114  // TODO:
115  //#pragma message("This should go over all elements of truth and outputs")
116 
117  // There's at least 1 element that requires processing.
118  if (!truths.empty() && !outputs.empty() && truths.front())
119  {
120  std::size_t index = 0;
121  for (index = 0; retval && index < truths.size(); ++index)
122  {
123  // in case outputs.size() < truths.size() an exception can be triggered
124  try
125  {
126  if (!outputs.at(index))
127  {
128  throw std::invalid_argument(IL_LOG_MSG_CLASS("Unexpected null output component in: 'outputs[" + std::to_string(index) + "]'."));
129  }
130  }
131  catch (const std::out_of_range &out_of_range)
132  {
133  throw std::invalid_argument(IL_LOG_MSG_CLASS("Unexpected out of range index output component in: 'outputs[" + std::to_string(index) + "]'."));
134  }
135 
136  if (outputs.at(index)->size < truths.at(index)->size)
137  {
138  throw std::invalid_argument(IL_LOG_MSG_CLASS("Buffer in outputs is not large enough to contain the expected output: 'outputs[" + std::to_string(index) + "]'."));
139  }
140 
141  std::uint64_t count = truths.at(index)->size / IDataLoader::sizeOf(data_type);
142  void *p_truth = truths.at(index)->p;
143  void *p_output = outputs.at(index)->p; // single output
144 
145  // validate the results
146  switch (data_type)
147  {
149  is_valid = hebench::Utilities::Math::almostEqual(reinterpret_cast<const std::int32_t *>(p_truth),
150  reinterpret_cast<const std::int32_t *>(p_output),
151  count,
152  0.01);
153  break;
154 
156  is_valid = hebench::Utilities::Math::almostEqual(reinterpret_cast<const std::int64_t *>(p_truth),
157  reinterpret_cast<const std::int64_t *>(p_output),
158  count,
159  0.01);
160  break;
161 
163  is_valid = hebench::Utilities::Math::almostEqual(reinterpret_cast<const float *>(p_truth),
164  reinterpret_cast<const float *>(p_output),
165  count,
166  0.01);
167  break;
168 
170  is_valid = hebench::Utilities::Math::almostEqual(reinterpret_cast<const double *>(p_truth),
171  reinterpret_cast<const double *>(p_output),
172  count,
173  0.01);
174  break;
175 
176  default:
177  retval = false;
178  break;
179  } // end switch
180 
181  // In case retval is set to false, it will break the for loop
182  retval = retval && is_valid.empty();
183  } // end for
184 
185  if (!retval)
186  {
187  std::stringstream ss;
188  ss << "Result component, " << (index - 1) << std::endl
189  << "Elements not within 1% of each other, " << is_valid.size() << std::endl
190  << "Failed indices, ";
191  for (std::size_t i = 0; i < is_valid.size() && i < MaxErrorPrint; ++i)
192  {
193  ss << is_valid[i];
194  if (i + 1 < is_valid.size() && i + 1 < MaxErrorPrint)
195  {
196  ss << ", ";
197  }
198  } // end for
199  if (is_valid.size() > MaxErrorPrint)
200  {
201  ss << ", ...";
202  }
203  throw std::runtime_error(ss.str());
204  } // end if
205  } // end if
206 
207  return retval;
208 }
209 
211  IDataLoader::Ptr dataset,
212  const std::uint64_t *param_data_pack_indices,
213  const std::vector<hebench::APIBridge::NativeDataBuffer *> &outputs,
214  hebench::APIBridge::DataType data_type) const
215 {
216  std::stringstream ss;
217 
218  // validate dataset and param_data_pack_indices
219  if (!dataset)
220  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid null `dataset`."));
221  if (!param_data_pack_indices)
222  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid null `param_data_pack_indices`."));
223  for (std::size_t param_i = 0; param_i < dataset->getParameterCount(); ++param_i)
224  {
225  if (param_data_pack_indices[param_i] >= dataset->getParameterData(param_i).buffer_count)
226  {
227  std::stringstream ss;
228  ss << "Index out of range `param_data_pack_indices[" << param_i << "]` = " << param_data_pack_indices[param_i] << ". "
229  << "Expected less than " << dataset->getParameterData(param_i).buffer_count << ".";
230  throw std::out_of_range(IL_LOG_MSG_CLASS(ss.str()));
231  } // end if
232  if (!dataset->getParameterData(param_i).p_buffers)
233  {
234  std::stringstream ss;
235  ss << "Invalid empty DataPack buffer in IDataLoader `dataset` for parameter " << param_i << ".";
236  throw std::invalid_argument(IL_LOG_MSG_CLASS(ss.str()));
237  } // end if
238  } // end for
239 
240  os << "Number of parameters, " << dataset->getParameterCount() << std::endl
241  << "Number of result components (expected), " << dataset->getResultCount() << std::endl
242  << "Number of result components (received), " << outputs.size() << std::endl
243  << std::endl;
244 
245  IDataLoader::ResultDataPtr ptr_truths = dataset->getResultFor(param_data_pack_indices);
246  const std::vector<const hebench::APIBridge::NativeDataBuffer *> &truths =
247  ptr_truths->result;
248 
249  // param_data_pack_indices already validated by previous call
250 
251  ss << ", Parameters, ";
252 
253  os << "Parameter index, size" << std::endl;
254  for (std::size_t param_i = 0; param_i < dataset->getParameterCount(); ++param_i)
255  {
256  assert(dataset->getParameterData(param_i).param_position == param_i);
257  const hebench::APIBridge::NativeDataBuffer &arg = dataset->getParameterData(param_i).p_buffers[param_data_pack_indices[param_i]];
258  os << param_i << ", " << arg.size / IDataLoader::sizeOf(data_type) << std::endl;
259 
260  if (param_i > 0)
261  ss << ", ";
262  } // end for
263 
264  ss << "Ground truth, ";
265 
266  os << std::endl
267  << "Ground truth index, size" << std::endl;
268  for (std::size_t result_component_i = 0; result_component_i < truths.size(); ++result_component_i)
269  {
270  assert(truths[result_component_i]);
271  os << result_component_i << ", " << truths[result_component_i]->size / IDataLoader::sizeOf(data_type) << std::endl;
272 
273  if (result_component_i > 0)
274  ss << ", ";
275  } // end for
276 
277  ss << "Output";
278 
279  os << std::endl
280  << "Output index, size" << std::endl;
281  for (std::size_t result_component_i = 0; result_component_i < outputs.size(); ++result_component_i)
282  {
283  assert(outputs[result_component_i]);
284  os << result_component_i << ", " << outputs[result_component_i]->size / IDataLoader::sizeOf(data_type) << std::endl;
285 
286  if (result_component_i + 1 < outputs.size())
287  ss << ", ";
288  } // end for
289 
290  // table header
291  os << std::endl
292  << ss.str() << std::endl
293  << "Index, ";
294 
295  // prepare arguments to print out in columns
296  std::vector<const hebench::APIBridge::NativeDataBuffer *> all_values;
297 
298  // parameters
299  for (std::size_t param_i = 0; param_i < dataset->getParameterCount(); ++param_i)
300  {
301  all_values.push_back(&dataset->getParameterData(param_i).p_buffers[param_data_pack_indices[param_i]]);
302  os << param_i << ", ";
303  } // end for
304  // ground truth
305  for (std::size_t result_component_i = 0; result_component_i < truths.size(); ++result_component_i)
306  {
307  all_values.push_back(truths[result_component_i]);
308  os << result_component_i << ", ";
309  } // end for
310  // received output
311  for (std::size_t result_component_i = 0; result_component_i < outputs.size(); ++result_component_i)
312  {
313  if (result_component_i > 0)
314  os << ", ";
315  all_values.push_back(outputs[result_component_i]);
316  os << result_component_i;
317  } // end for
318 
319  os << std::endl;
320  hebench::Utilities::printArraysAsColumns(os, all_values.data(), all_values.size(),
321  data_type, true, ", ");
322 }
323 
324 } // namespace TestHarness
325 } // namespace hebench
Token returned by a successful call to IBenchmarkDescriptor::matchBenchmarkDescriptor().
static std::size_t sizeOf(hebench::APIBridge::DataType data_type)
std::shared_ptr< ResultData > ResultDataPtr
std::shared_ptr< IDataLoader > Ptr
static bool isEmpty(const hebench::APIBridge::Handle &h) noexcept
virtual bool validateResult(IDataLoader::Ptr dataset, const std::uint64_t *param_data_pack_indices, const std::vector< hebench::APIBridge::NativeDataBuffer * > &outputs, hebench::APIBridge::DataType data_type) const
Validates the result of an operation against the ground truth.
virtual void logResult(std::ostream &os, IDataLoader::Ptr dataset, const std::uint64_t *param_data_pack_indices, const std::vector< hebench::APIBridge::NativeDataBuffer * > &outputs, hebench::APIBridge::DataType data_type) const
Outputs the arguments, expected ground truth, and received result to an output stream.
PartialBenchmarkCategory(std::shared_ptr< Engine > p_engine, const IBenchmarkDescriptor::DescriptionToken &description_token)
const hebench::APIBridge::Handle & handle() const override
@ Float64
64 bits IEEE 754 standard floating point real numbers.
Definition: types.h:306
@ Int64
64 bits signed integers.
Definition: types.h:304
ErrorCode destroyHandle(Handle h)
Releases resources held by the specified handle.
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
std::uint64_t size
Size of underlying data.
Definition: types.h:564
void * p
Pointer to underlying data.
Definition: types.h:558
std::int32_t ErrorCode
Return value for API bridge functions.
Definition: types.h:34
std::int64_t tag
Optional tag.
Definition: types.h:569
Structure to contain flexible data.
Definition: types.h:552
std::string to_string(const std::string_view &s)
void printArraysAsColumns(std::ostream &os, const hebench::APIBridge::NativeDataBuffer **p_buffers, std::size_t count, hebench::APIBridge::DataType data_type, bool output_row_index=false, const char *separator=" ")
Writes the collection of NativeDataBuffer as columns to the specified output stream.
#define HEBENCH_ECODE_SUCCESS
Function call succeeded without error.
Definition: types.h:39