HEBench
hebench_dotproduct.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 <cassert>
6 #include <numeric>
7 #include <sstream>
8 
9 #include "../include/hebench_dotproduct.h"
10 #include "hebench/modules/general/include/hebench_math_utils.h"
11 
12 namespace hebench {
13 namespace TestHarness {
14 namespace DotProduct {
15 
16 //------------------------------------
17 // class BenchmarkDescriptionCategory
18 //------------------------------------
19 
23  };
24 
25 std::uint64_t BenchmarkDescriptorCategory::fetchVectorSize(const std::vector<hebench::APIBridge::WorkloadParam> &w_params)
26 {
27  assert(WorkloadParameterCount == 1);
28  assert(OpParameterCount == 2);
29  assert(OpResultCount == 1);
30 
31  std::uint64_t retval;
32 
33  if (w_params.size() < WorkloadParameterCount)
34  {
35  std::stringstream ss;
36  ss << "Insufficient workload parameters in 'w_params'. Expected " << WorkloadParameterCount
37  << ", but " << w_params.size() << "received.";
38  throw std::invalid_argument(IL_LOG_MSG_CLASS(ss.str()));
39  } // end if
40 
41  // validate parameters
42  for (std::size_t i = 0; i < WorkloadParameterCount; ++i)
43  if (w_params[i].data_type != WorkloadParameterType[i])
44  {
45  std::stringstream ss;
46  ss << "Invalid type for workload parameter " << i
47  << ". Expected type ID " << WorkloadParameterType[i] << ", but " << w_params[i].data_type << " received.";
48  throw std::invalid_argument(IL_LOG_MSG_CLASS(ss.str()));
49  } // end if
50  else if (w_params[i].u_param <= 0)
51  {
52  std::stringstream ss;
53  ss << "Invalid number of elements for vector in workload parameter " << i
54  << ". Expected positive integer, but " << w_params[i].u_param << " received.";
55  throw std::invalid_argument(IL_LOG_MSG_CLASS(ss.str()));
56  } // end else if
57 
58  retval = w_params.at(0).u_param;
59 
60  return retval;
61 }
62 
64  const Engine &engine,
65  const BenchmarkDescription::Backend &backend_desc,
66  const BenchmarkDescription::Configuration &config) const
67 {
68  (void)engine;
69  std::stringstream ss;
70 
71  output.concrete_descriptor = backend_desc.descriptor;
73  backend_desc.descriptor,
74  config,
76 
77  // workload name
78 
79  std::uint64_t vector_size = fetchVectorSize(config.w_params);
80  ss << BaseWorkloadName << " " << vector_size;
81 
82  output.workload_name = ss.str();
85 }
86 
88  const std::vector<hebench::APIBridge::WorkloadParam> &w_params) const
89 {
90  bool retval = false;
91 
92  // return true if benchmark is supported
94  {
95  try
96  {
97  fetchVectorSize(w_params);
98  retval = true;
99  }
100  catch (...)
101  {
102  // workload not supported
103  retval = false;
104  }
105  } // end if
106 
107  return retval;
108 }
109 
110 //---------------------------
111 // class DataGeneratorHelper
112 //---------------------------
113 
118 {
119 private:
120  IL_DECLARE_CLASS_NAME(DotProduct::DataGeneratorHelper)
121 
122 public:
123  static void vectorDotProduct(hebench::APIBridge::DataType data_type,
124  void *result, const void *a, const void *b,
125  std::uint64_t elem_count);
126 
127 protected:
129 
130 private:
131  template <class T>
132  static void vectorDotProduct(T &result, const T *a, const T *b, std::uint64_t elem_count)
133  {
134  if (!a)
135  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid null `a`"));
136  if (!b)
137  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid null `b`"));
138  result = std::inner_product(a, a + elem_count, b, T());
139  }
140 };
141 
143  void *result, const void *a, const void *b,
144  std::uint64_t elem_count)
145 {
146  if (!result)
147  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid null 'p_result'."));
148 
149  switch (data_type)
150  {
152  vectorDotProduct<std::int32_t>(*reinterpret_cast<std::int32_t *>(result),
153  reinterpret_cast<const std::int32_t *>(a), reinterpret_cast<const std::int32_t *>(b),
154  elem_count);
155  break;
156 
158  vectorDotProduct<std::int64_t>(*reinterpret_cast<std::int64_t *>(result),
159  reinterpret_cast<const std::int64_t *>(a), reinterpret_cast<const std::int64_t *>(b),
160  elem_count);
161  break;
162 
164  vectorDotProduct<float>(*reinterpret_cast<float *>(result),
165  reinterpret_cast<const float *>(a), reinterpret_cast<const float *>(b),
166  elem_count);
167  break;
168 
170  vectorDotProduct<double>(*reinterpret_cast<double *>(result),
171  reinterpret_cast<const double *>(a), reinterpret_cast<const double *>(b),
172  elem_count);
173  break;
174 
175  default:
176  throw std::invalid_argument(IL_LOG_MSG_CLASS("Unknown data type."));
177  break;
178  } // end switch
179 }
180 
181 //------------------
182 // class DataLoader
183 //------------------
184 
185 DataLoader::Ptr DataLoader::create(std::uint64_t vector_size,
186  std::uint64_t batch_size_a,
187  std::uint64_t batch_size_b,
189 {
191  retval->init(vector_size, batch_size_a, batch_size_b, data_type);
192  return retval;
193 }
194 
195 DataLoader::Ptr DataLoader::create(std::uint64_t vector_size,
196  std::uint64_t batch_size_a,
197  std::uint64_t batch_size_b,
199  const std::string &dataset_filename)
200 {
202  retval->init(vector_size, batch_size_a, batch_size_b, data_type, dataset_filename);
203  return retval;
204 }
205 
206 DataLoader::DataLoader() :
207  m_vector_size(0)
208 {
209 }
210 
211 void DataLoader::init(std::uint64_t vector_size,
212  std::uint64_t batch_size_a,
213  std::uint64_t batch_size_b,
215 {
216  // Load/generate and initialize the data for vector element-wise addition:
217  // C = A . B
218 
219  // number of samples in each input parameter and output
220  std::size_t batch_sizes[InputDim0 + OutputDim0] = {
221  batch_size_a,
222  batch_size_b,
223  batch_size_a * batch_size_b
224  };
225 
226  m_vector_size = vector_size;
227 
228  // compute number of elements in each vector
229  std::uint64_t sample_vector_sizes[InputDim0 + OutputDim0];
230  // input
231  for (std::size_t i = 0; i < InputDim0; ++i)
232  {
233  sample_vector_sizes[i] = vector_size;
234  } // end for
235  // output
236  for (std::size_t i = InputDim0; i < InputDim0 + OutputDim0; ++i)
237  {
238  // an output vector has a single component (result of dot product)
239  sample_vector_sizes[i] = 1;
240  } // end for
241 
242  // allocate memory for each vector buffer
243  PartialDataLoader::init(data_type,
244  InputDim0, batch_sizes, sample_vector_sizes,
245  OutputDim0, sample_vector_sizes + InputDim0,
246  true);
247 
248  // at this point all NativeDataBuffers have been allocated and pointed to the correct locations
249 
250  // fill up each vector data
251 
252  // input
253  for (std::size_t vector_i = 0; vector_i < InputDim0; ++vector_i)
254  {
255  for (std::uint64_t i = 0; i < batch_sizes[vector_i]; ++i)
256  {
257  // generate the data
259  getParameterData(vector_i).p_buffers[i].p,
260  vector_size, 0.0, 10.0);
261  } // end for
262  } // end for
263 
264  // output
265  //#pragma omp parallel for collapse(2)
266  for (std::uint64_t a_i = 0; a_i < batch_sizes[0]; ++a_i)
267  {
268  for (std::uint64_t b_i = 0; b_i < batch_sizes[1]; ++b_i)
269  {
270  // find the index for the result buffer based on the input indices
271  std::uint64_t ppi[] = { a_i, b_i };
272  std::uint64_t r_i = getResultIndex(ppi);
273 
274  // generate the data
276  getResultData(0).p_buffers[r_i].p, // C
277  getParameterData(0).p_buffers[a_i].p, // A
278  getParameterData(1).p_buffers[b_i].p, // B
279  vector_size);
280  } // end for
281  } // end for
282 
283  // all data has been generated at this point
284 }
285 
286 void DataLoader::init(std::uint64_t vector_size,
287  std::uint64_t batch_size_a,
288  std::uint64_t batch_size_b,
290  const std::string &dataset_filename)
291 {
292  // Load/generate and initialize the data for vector element-wise addition:
293  // C = A . B
294 
295  // number of samples in each input parameter and output
296  std::size_t batch_sizes[InputDim0 + OutputDim0] = {
297  batch_size_a,
298  batch_size_b,
299  batch_size_a * batch_size_b
300  };
301 
302  m_vector_size = vector_size;
303 
304  // compute number of elements in each vector
305  std::uint64_t sample_vector_sizes[InputDim0 + OutputDim0];
306  // input
307  for (std::size_t i = 0; i < InputDim0; ++i)
308  {
309  sample_vector_sizes[i] = vector_size;
310  } // end for
311  // output
312  for (std::size_t i = InputDim0; i < InputDim0 + OutputDim0; ++i)
313  {
314  // an output vector has a single component (result of dot product)
315  sample_vector_sizes[i] = 1;
316  } // end for
317 
318  // allocate memory for each vector buffer
319  PartialDataLoader::init(dataset_filename, data_type,
320  InputDim0, batch_sizes, sample_vector_sizes,
321  OutputDim0, sample_vector_sizes + InputDim0);
322 
323  // at this point all NativeDataBuffers have been allocated and pointed to the correct locations
324  // and buffers loaded with data from dataset_filename
325 }
326 
327 void DataLoader::computeResult(std::vector<hebench::APIBridge::NativeDataBuffer *> &result,
328  const std::uint64_t *param_data_pack_indices,
330 {
331  // as protected method, parameters should be valid when called
332 
333  // generate the output
335  result.front()->p, // C
336  getParameterData(0).p_buffers[param_data_pack_indices[0]].p, // A
337  getParameterData(1).p_buffers[param_data_pack_indices[1]].p, // B
338  m_vector_size);
339 }
340 
341 } // namespace DotProduct
342 } // namespace TestHarness
343 } // namespace hebench
const hebench::APIBridge::BenchmarkDescriptor & descriptor
Benchmark backend descriptor, as retrieved by backend, corresponding to the registration handle h_des...
std::vector< hebench::APIBridge::WorkloadParam > w_params
Set of arguments for workload parameters.
Static helper class to generate vector data for all supported data types.
static void generateRandomVectorN(hebench::APIBridge::DataType data_type, void *result, std::uint64_t elem_count, double mean, double stddev)
Generates normally distributed random data of the specified type.
void completeWorkloadDescription(WorkloadDescriptionOutput &output, const Engine &engine, const BenchmarkDescription::Backend &backend_desc, const BenchmarkDescription::Configuration &config) const override
Completes the description for the matched benchmark.
bool matchBenchmarkDescriptor(const hebench::APIBridge::BenchmarkDescriptor &bench_desc, const std::vector< hebench::APIBridge::WorkloadParam > &w_params) const override
Determines if the represented benchmark can perform the workload described by a specified HEBench ben...
static std::uint64_t fetchVectorSize(const std::vector< hebench::APIBridge::WorkloadParam > &w_params)
static hebench::APIBridge::WorkloadParamType::WorkloadParamType WorkloadParameterType[WorkloadParameterCount]
Static helper class to generate vector data for all supported data types.
static void vectorDotProduct(hebench::APIBridge::DataType data_type, void *result, const void *a, const void *b, std::uint64_t elem_count)
void computeResult(std::vector< hebench::APIBridge::NativeDataBuffer * > &result, const std::uint64_t *param_data_pack_indices, hebench::APIBridge::DataType data_type) override
Computes result of the operation on the input data given the of the input sample.
static DataLoader::Ptr create(std::uint64_t vector_size, std::uint64_t batch_size_a, std::uint64_t batch_size_b, hebench::APIBridge::DataType data_type)
static void completeCategoryParams(hebench::APIBridge::BenchmarkDescriptor &out_descriptor, const hebench::APIBridge::BenchmarkDescriptor &in_descriptor, const BenchmarkDescription::Configuration &config, bool force_config)
Completes common elements of category parameters in a descriptor using the specified configuration.
static bool getForceConfigValues()
Specifies whether frontend will override backend descriptors using configuration data or not.
std::size_t operation_params_count
Number of parameters for the represented workload operation.
std::string workload_name
Human-readable friendly name for the represented workload to be used for its description on the repor...
hebench::APIBridge::BenchmarkDescriptor concrete_descriptor
Benchmark descriptor completed with concrete values assigned to configurable fields.
std::string workload_base_name
Human-readable friendly name for the represented workload to be used for its description on the repor...
Bundles values that need to be filled by a workload during completeWorkloadDescription().
const hebench::APIBridge::DataPack & getResultData(std::uint64_t param_position) const override
Data pack corresponding to the specified component of the result.
const hebench::APIBridge::DataPack & getParameterData(std::uint64_t param_position) const override
Data pack for specified operation parameter (operand).
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.
std::uint64_t getResultIndex(const std::uint64_t *param_data_pack_indices) const override
Computes the index of the result NativeDataBuffer given the indices of the input data.
WorkloadParamType
Defines the possible data types for a workload flexible parameter.
Definition: types.h:303
@ Float64
64 bits IEEE 754 standard floating point real numbers.
Definition: types.h:306
@ Int64
64 bits signed integers.
Definition: types.h:304
@ UInt64
64 bits unsigned integers.
Definition: types.h:305
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
Workload workload
Workload for the benchmark.
Definition: types.h:529
Defines a benchmark test.
Definition: types.h:527