HEBench
hebench_benchmark_latency.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 <bitset>
7 #include <cassert>
8 #include <cstring>
9 #include <filesystem>
10 #include <iostream>
11 #include <stdexcept>
12 
13 #include "hebench/modules/timer/include/timer.h"
14 
15 #include "hebench/api_bridge/api.h"
16 #include "hebench/modules/general/include/hebench_math_utils.h"
17 #include "include/hebench_engine.h"
18 
19 #include "../include/hebench_benchmark_latency.h"
20 
21 namespace hebench {
22 namespace TestHarness {
23 
24 BenchmarkLatency::BenchmarkLatency(std::shared_ptr<Engine> p_engine,
25  const IBenchmarkDescriptor::DescriptionToken &description_token) :
26  PartialBenchmarkCategory(p_engine, description_token)
27 {
28 }
29 
31 {
32 }
33 
35  IBenchmark::RunConfig &run_config)
36 {
37  return run(out_report, getDataset(), run_config);
38 }
39 
41  IDataLoader::Ptr p_dataset,
42  IBenchmark::RunConfig &run_config)
43 {
44  std::cout << std::endl
45  << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Starting test...") << std::endl;
46 
47  std::stringstream ss;
48  hebench::Common::EventTimer<true> timer; // high precision
49  std::uint32_t event_id = getEventIDNext();
50  std::string event_name;
51  hebench::Common::TimingReportEvent::Ptr p_timing_event;
52 
53  // The following is simplified since latency test is predefined to have a
54  // single sample for each parameter and result
55 
56  constexpr std::uint64_t LatencySampleSize = 1; // all batch sizes are 1 in latency test
57 
58  // prepare the parameters for encoding
59 
60  // constexpr int op_params_count = 2; // operation has 2 parameters
61 
62  // DataPack param_packs[op_params_count];
63  // param_packs[0].p_buffers = p_raw_inputs_A;
64  // param_packs[0].buffer_count = A_count;
65  // param_packs[0].param_position = 0; // A is the first parameter, so, position 0
66  // param_packs[1].p_buffers = p_raw_inputs_B;
67  // param_packs[1].buffer_count = B_count;
68  // param_packs[1].param_position = 1; // B is the second parameter, so, position 1
69 
70  // create the data packs
71  std::vector<hebench::APIBridge::DataPack> param_packs(p_dataset->getParameterCount());
72  for (std::size_t param_i = 0; param_i < param_packs.size(); ++param_i)
73  {
74  assert(p_dataset->getParameterData(param_i).param_position == param_i);
75  param_packs[param_i].p_buffers = p_dataset->getParameterData(param_i).p_buffers;
76  param_packs[param_i].buffer_count = LatencySampleSize;
77  param_packs[param_i].param_position = param_i;
78 
79  // validate param pack
80  if (param_packs[param_i].buffer_count > 0
81  && !param_packs[param_i].p_buffers)
82  throw std::invalid_argument(IL_LOG_MSG_CLASS("Invalid empty DataPack in IDataLoader `p_dataset` for parameter " + std::to_string(param_i) + "."));
83  } // end for
84 
85  // validate results
86  for (std::uint64_t result_i = 0; result_i < p_dataset->getResultCount(); ++result_i)
87  {
88  // make sure we have enough results in the dataset
89  if (p_dataset->getResultData(result_i).buffer_count < LatencySampleSize)
90  {
91  std::stringstream ss;
92  ss << "Invalid number of result samples for result component " << result_i << ". "
93  << "Expected " << LatencySampleSize << ", but " << p_dataset->getResultData(result_i).buffer_count << " received.";
94  throw std::invalid_argument(IL_LOG_MSG_CLASS(ss.str()));
95  } // end if
96  // make sure the results are not null
97  if (!p_dataset->getResultData(result_i).p_buffers)
98  {
99  std::stringstream ss;
100  ss << "Invalid null DataPack buffer in IDataLoader `p_dataset` for result component " << result_i << ".";
101  throw std::invalid_argument(IL_LOG_MSG_CLASS(ss.str()));
102  } // end if
103  } // end if
104 
105  // pack the parameters based on encrypted/plain
106 
107  // DataPackCollection packed_parameters;
108  // packed_parameters.p_data_packs = param_packs;
109  // packed_parameters.pack_count = op_params_count;
110 
111  // separate param packs in encrypted/plain
112  std::vector<hebench::APIBridge::DataPackCollection> packed_parameters(2);
113  std::vector<std::vector<hebench::APIBridge::DataPack>> packed_parameters_data_packs(packed_parameters.size());
114  std::bitset<sizeof(std::uint32_t)> cipher_param_mask(this->getBackendDescription().descriptor.cipher_param_mask);
115  for (std::size_t i = 0; i < param_packs.size(); ++i)
116  {
117  // determine if this parameter is encrypted
118  if (cipher_param_mask.test(i)) // m_descriptor.cipher_param_mask & (1 << i)
119  packed_parameters_data_packs.front().push_back(param_packs[i]);
120  else
121  packed_parameters_data_packs.back().push_back(param_packs[i]);
122  } // end for
123 
124  // pack the data in API bridge format
125  std::memset(packed_parameters.data(), 0, sizeof(hebench::APIBridge::DataPackCollection) * packed_parameters.size());
126  for (std::size_t i = 0; i < packed_parameters.size(); ++i)
127  {
128  if (!packed_parameters_data_packs[i].empty())
129  {
130  packed_parameters[i].p_data_packs = packed_parameters_data_packs[i].data();
131  packed_parameters[i].pack_count = packed_parameters_data_packs[i].size();
132  } // end for
133  } // end for
134 
135  // encode the raw parameters data
136 
137  // Handle h_encoded_inputs;
138  // encode(h_benchmark, &packed_parameters, &h_encoded_inputs);
139 
140  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Encoding.") << std::endl;
141 
142  std::vector<RAIIHandle> h_inputs(packed_parameters.size());
143  for (std::size_t i = 0; i < packed_parameters.size(); ++i)
144  {
145  event_id = getEventIDNext();
146  if (packed_parameters[i].pack_count > 0)
147  {
148  event_name = "Encoding pack " + std::to_string(i);
149  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log(event_name + "...") << std::endl;
150  timer.start();
151  validateRetCode(hebench::APIBridge::encode(handle(), &packed_parameters[i], &h_inputs[i].handle));
152  p_timing_event = timer.stop<DefaultTimeInterval>(event_id, 1, nullptr);
153  out_report.addEvent<DefaultTimeInterval>(p_timing_event, event_name);
154  } // end if
155  else
156  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Pack " + std::to_string(i) + " is empty (skipping).") << std::endl;
157  } // end for
158 
159  std::cout << IOS_MSG_OK << std::endl;
160 
161  // encrypt the encoded data
162 
163  event_id = getEventIDNext();
164  event_name = "Encryption";
165 
166  // Handle h_cipher_inputs;
167  // encrypt(h_benchmark, h_encoded_inputs, &h_cipher_inputs);
168 
169  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Encryption.") << std::endl;
170 
171  if (packed_parameters[0].pack_count > 0)
172  {
173  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Encrypting...") << std::endl;
174 
175  hebench::APIBridge::Handle encrypted_input;
176  // we have data to encrypt
177  timer.start();
178  validateRetCode(hebench::APIBridge::encrypt(handle(), h_inputs.front().handle, &encrypted_input));
179  p_timing_event = timer.stop<DefaultTimeInterval>(event_id, 1, nullptr);
180  out_report.addEvent<DefaultTimeInterval>(p_timing_event, event_name);
181 
182  // overwrite the first input handle by its encrypted version
183  h_inputs.front() = encrypted_input; // old handle automatically destroyed by RAII
184 
185  std::cout << IOS_MSG_OK << std::endl;
186  } // end if
187  else
188  std::cout << IOS_MSG_WARNING << hebench::Logging::GlobalLogger::log("No encrypted parameters requested (skipping).") << std::endl;
189 
190  // load encrypted data into backend's remote to use as input to the operation
191 
192  event_id = getEventIDNext();
193  event_name = "Loading";
194 
195  // Handle h_remote_inputs;
196  // load(h_benchmark,
197  // &h_cipher_inputs, 1, // only 1 DataPackCollection
198  // &h_remote_inputs);
199 
200  // prepare handles for loading
201  std::vector<hebench::APIBridge::Handle> h_inputs_local;
202  for (std::size_t i = 0; i < packed_parameters.size(); ++i)
203  {
204  if (packed_parameters[i].pack_count > 0)
205  h_inputs_local.push_back(h_inputs[i].handle);
206  } // end for
207 
208  // load handles
209 
210  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Loading data to remote backend...") << std::endl;
211 
212  RAIIHandle h_inputs_remote;
213  timer.start();
215  h_inputs_local.data(), h_inputs_local.size(),
216  &h_inputs_remote.handle));
217  p_timing_event = timer.stop<DefaultTimeInterval>(event_id, 1, nullptr);
218  out_report.addEvent<DefaultTimeInterval>(p_timing_event, event_name);
219 
220  std::cout << IOS_MSG_OK << std::endl;
221 
222  // clean up data we no longer need (in reverse order of creation)
223 
224  // destroyHandle(h_cipher_inputs);
225  // destroyHandle(h_encoded_inputs);
226 
227  h_inputs_local.clear();
228  h_inputs.clear(); // input handles cleaned up automatically here
229  packed_parameters.clear();
230  packed_parameters_data_packs.clear();
231  param_packs.clear();
232 
233  // prepare parameter indexers for operation
234 
235  // ParameterIndexer params[op_params_count]; // one indexer per parameter
236  // params[0].batch_size = 1; // for parameter A we will be using one value
237  // params[0].value_index = 0; // starting with the first value
238 
239  // params[1].batch_size = 10; // for parameter B we will be using 10 values
240  // params[1].value_index = 0; // starting with the first.
241 
242  std::vector<hebench::APIBridge::ParameterIndexer> params(p_dataset->getParameterCount());
243  for (std::size_t i = 0; i < params.size(); ++i)
244  {
245  params[i].batch_size = LatencySampleSize;
246  params[i].value_index = 0;
247  } // end if
248 
249  // operate
250 
251  event_id = getEventIDNext();
252  event_name = "Warmup";
253 
254  // Handle h_remote_result;
255  // operate(h_benchmark,
256  // h_remote_inputs, params,
257  // &h_remote_result);
258 
259  std::uint64_t warmup_terations_count = this->getBackendDescription().descriptor.cat_params.latency.warmup_iterations_count;
260  if (warmup_terations_count > 0)
261  {
262  std::cout << IOS_MSG_INFO
263  << hebench::Logging::GlobalLogger::log("Starting warm-up iterations: requested "
264  + std::to_string(warmup_terations_count)
265  + " iterations.")
266  << std::endl;
267 
268  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Warming up...") << std::endl;
269 
270  // warm up
271  for (std::uint64_t rep_i = 0; rep_i < warmup_terations_count; ++rep_i)
272  {
273  RAIIHandle h_result_remote;
274  timer.start();
276  h_inputs_remote.handle,
277  params.data(), params.size(),
278  &h_result_remote.handle));
279  p_timing_event = timer.stop<DefaultTimeInterval>(event_id, 1, nullptr);
280  out_report.addEvent<DefaultTimeInterval>(p_timing_event, event_name);
281  } // end for
282 
283  std::cout << IOS_MSG_DONE << std::endl;
284  } // end if
285  else
286  {
287  std::cout << IOS_MSG_WARNING
288  << hebench::Logging::GlobalLogger::log("No warm-up requested (skipping).")
289  << std::endl;
290  } // end else
291 
292  std::uint64_t min_test_time_ms =
296 
297  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Starting latency test.") << std::endl
298  << std::string(sizeof(IOS_MSG_INFO) + 1, ' ') << hebench::Logging::GlobalLogger::log("Requested time: " + std::to_string(this->getBackendDescription().descriptor.cat_params.min_test_time_ms) + " ms") << std::endl
299  << std::string(sizeof(IOS_MSG_INFO) + 1, ' ') << hebench::Logging::GlobalLogger::log("Actual time: " + std::to_string(min_test_time_ms) + " ms") << std::endl;
300 
301  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Testing...") << std::endl;
302 
303  event_id = getEventIDNext();
304  event_name = "Operation";
305 
306  out_report.addEventType(event_id, event_name, true);
307 
308  // measure the operation after warm up
309  std::vector<RAIIHandle> h_remote_results;
310  h_remote_results.reserve(20); // initial capacity for 20 iterations
311  out_report.setEventCapacity(out_report.getEventCapacity() + h_remote_results.capacity());
312  std::uint64_t op_count = 0;
313  double elapsed_ms = 0.0;
314  while (op_count < 2 || elapsed_ms < min_test_time_ms)
315  {
316  hebench::APIBridge::Handle h_result_remote;
317  timer.start();
319  h_inputs_remote.handle,
320  params.data(), params.size(),
321  &h_result_remote));
322  p_timing_event = timer.stop<DefaultTimeInterval>(event_id, 1, nullptr);
323  elapsed_ms += p_timing_event->elapsedWallTime<std::milli>();
324  // check if we have enough capacity
325  if (h_remote_results.capacity() == h_remote_results.size()
326  && elapsed_ms > 0.0)
327  {
328  // capacity exceeded: over estimate capacity needed for the whole operation
329  // so that it is less likely that we need to reallocate again
330  std::size_t tmp_multiplier = static_cast<std::size_t>(min_test_time_ms / elapsed_ms);
331  std::size_t max_capacity = h_remote_results.capacity()
332  * (tmp_multiplier + (tmp_multiplier > 0 ? 1 : 2));
333  out_report.setEventCapacity(out_report.getEventCapacity()
334  + (max_capacity - h_remote_results.capacity()));
335  h_remote_results.reserve(max_capacity);
336  } // end if
337  out_report.addEvent<DefaultTimeInterval>(p_timing_event, event_name);
338  h_remote_results.emplace_back(h_result_remote);
339 
340  ++op_count;
341  } // end while
342 
343  std::cout << IOS_MSG_DONE << std::endl;
344 
345  // clean up data we no longer need
346 
347  // destroyHandle(h_remote_inputs);
348 
349  h_inputs_remote.destroy();
350 
351  // postprocess output
352 
353  // Handle h_cipher_output;
354 
355  // retrieve data from backend's remote and store in host
356 
357  event_id = getEventIDNext();
358  event_name = "Store";
359 
360  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Retrieving data from remote backend...") << std::endl;
361 
362  std::vector<RAIIHandle> h_cipher_results(h_remote_results.size());
363  for (std::size_t i = 0; i < h_remote_results.size(); ++i)
364  {
365  // store(h_benchmark, h_remote_result,
366  // &h_cipher_output, 1 // Only 1 local DataPackCollection for result expected for this operation.
367  // );
368 
369  timer.start();
371  h_remote_results[i].handle,
372  &h_cipher_results[i].handle,
373  1));
374  p_timing_event = timer.stop<DefaultTimeInterval>(event_id, 1, nullptr);
375  out_report.addEvent<DefaultTimeInterval>(p_timing_event, event_name);
376 
377  // clean up data we no longer need
378  // destroyHandle(h_remote_result);
379 
380  // even though it is RAII, it is better to free up space on remote manually here,
381  // just in case it is a device with low memory capacity
382  h_remote_results[i].destroy();
383  } // end for
384  h_remote_results.clear();
385 
386  std::cout << IOS_MSG_OK << std::endl;
387 
388  // decrypt results
389 
390  event_id = getEventIDNext();
391  event_name = "Decryption";
392 
393  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Decrypting results...") << std::endl;
394 
395  std::vector<RAIIHandle> h_plain_results(h_cipher_results.size());
396  for (std::size_t i = 0; i < h_cipher_results.size(); ++i)
397  {
398  // Handle h_plain_result;
399  // decrypt(h_benchmark, h_cipher_output, &h_plain_result);
400 
401  timer.start();
402  validateRetCode(hebench::APIBridge::decrypt(handle(), h_cipher_results[i].handle, &h_plain_results[i].handle));
403  p_timing_event = timer.stop<DefaultTimeInterval>(event_id, 1, nullptr);
404  out_report.addEvent<DefaultTimeInterval>(p_timing_event, event_name);
405 
406  // // clean up data we no longer need
407  // destroyHandle(h_cipher_output);
408 
409  // even though it is RAII, it is better to free up space here,
410  // just in case these local handles are large
411  h_cipher_results[i].destroy();
412  } // end for
413  h_cipher_results.clear();
414 
415  std::cout << IOS_MSG_OK << std::endl;
416 
417  // allocate space for decoded results
418 
419  // NativeDataBuffer p_raw_results[?]; // allocate space for all outputs
420 
421  // std::vector<std::uint8_t> raw_result_buffer(p_dataset->getResultData(0).p_buffers[0].size);
422  // hebench::APIBridge::NativeDataBuffer raw_results;
423  // raw_results.p = raw_result_buffer.data();
424  // raw_results.size = raw_result_buffer.size();
425  // raw_results.tag = 0;
426 
427  // allocate space for all outputs
428 
429  std::vector<std::uint8_t> raw_result_buffer;
430  std::uint64_t max_raw_result_size = 0;
431  for (std::uint64_t result_pos = 0; result_pos < p_dataset->getResultCount(); ++result_pos)
432  {
433  if (p_dataset->getResultData(result_pos).buffer_count <= 0
434  || !p_dataset->getResultData(result_pos).p_buffers)
435  throw std::logic_error(IL_LOG_MSG_CLASS("Invalid empty NativeDataBuffer in IDataLoader `p_dataset` at result " + std::to_string(result_pos) + "."));
436  max_raw_result_size += p_dataset->getResultData(result_pos).p_buffers[0].size;
437  } // end for
438  raw_result_buffer.resize(max_raw_result_size);
439 
440  // point to the allocated buffers
441 
442  std::vector<hebench::APIBridge::NativeDataBuffer> raw_results(p_dataset->getResultCount(),
444  std::uint64_t offset_p = 0;
445  for (std::uint64_t result_pos = 0; result_pos < p_dataset->getResultCount(); ++result_pos)
446  {
447  if (p_dataset->getResultData(result_pos).buffer_count > 0)
448  {
449  raw_results[result_pos].p = raw_result_buffer.data() + offset_p;
450  raw_results[result_pos].size = p_dataset->getResultData(result_pos).p_buffers[0].size;
451  raw_results[result_pos].tag = 0;
452 
453  offset_p += raw_results[result_pos].size;
454  } // end if
455  } // end for
456 
457  // DataPack results_pack;
458  // results_pack.p_buffers = p_raw_results;
459  // results_pack.buffer_count = A_count// B_count;
460  // results_pack.param_position = 0; // we are retrieving results in the first position into this data pack
461 
462  std::vector<hebench::APIBridge::DataPack> results_pack(p_dataset->getResultCount());
463  for (std::size_t result_pos = 0; result_pos < results_pack.size(); ++result_pos)
464  {
465  results_pack[result_pos].p_buffers = &raw_results[result_pos];
466  results_pack[result_pos].buffer_count = 1;
467  results_pack[result_pos].param_position = result_pos;
468  } // end for
469 
470  // DataPackCollection packed_results;
471  // packed_results.p_data_packs = &results_pack;
472  // packed_results.pack_count = 1; // result shape is [1, 10]
473 
475  packed_results.p_data_packs = results_pack.data();
476  packed_results.pack_count = results_pack.size();
477 
478  // decode the results
479 
480  event_id = getEventIDNext();
481  event_name = "Decoding";
482 
483  if (run_config.b_validate_results)
484  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Decoding and Validation.") << std::endl;
485  else
486  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Decoding...") << std::endl;
487 
488  std::cout << IOS_MSG_INFO << hebench::Logging::GlobalLogger::log("Result", false);
489 
490  std::size_t mini_reports_cnt = 3;
491  std::size_t report_every_n_elements = h_plain_results.size() / 7 + 1;
492  if (report_every_n_elements <= 0)
493  report_every_n_elements = 1;
494  std::size_t report_every_n_by_3_elements = report_every_n_elements / 4;
495  if (report_every_n_by_3_elements <= 0)
496  report_every_n_by_3_elements = 1;
497  std::size_t progress_report_batch = 0;
498 
499  bool b_valid = true;
500  for (std::size_t i = 0; i < h_plain_results.size(); ++i)
501  {
502  const auto &h_plain = h_plain_results[i].handle;
503  if (b_valid)
504  {
505  if (i == 0 || (i + 1) % report_every_n_elements == 0)
506  {
507  // report operation to show sign of life
508  while (mini_reports_cnt++ < 3)
509  std::cout << hebench::Logging::GlobalLogger::log(".", false) << std::flush;
510  mini_reports_cnt = 0;
511  progress_report_batch = 0;
512  std::cout << hebench::Logging::GlobalLogger::log(" " + std::to_string(i + 1), false) << std::flush;
513  } // end if
514 
515  // decode(Handle h_benchmark, h_plain_result, &packed_results);
516 
517  timer.start();
518  validateRetCode(hebench::APIBridge::decode(handle(), h_plain, &packed_results));
519  p_timing_event = timer.stop<DefaultTimeInterval>(event_id, 1, nullptr);
520  out_report.addEvent<DefaultTimeInterval>(p_timing_event, event_name);
521 
522  // if (i + 1 < h_plain_results.size() && mini_reports_cnt < 3
523  // && (mini_reports_cnt == 0 || (i + 1) % report_every_n_by_3_elements == 0))
524  if (mini_reports_cnt < 3
525  && progress_report_batch > 0
526  && progress_report_batch % report_every_n_by_3_elements == 0)
527  {
528  // report operation to show sign of life
529  ++mini_reports_cnt;
530  std::cout << hebench::Logging::GlobalLogger::log(".", false) << std::flush;
531  } // end if
532 
533  // validate output
534  if (run_config.b_validate_results)
535  {
536  std::string s_error_msg;
537  std::vector<std::uint64_t> data_pack_indices;
538  std::vector<hebench::APIBridge::NativeDataBuffer *> outputs;
539  try
540  {
541  outputs.resize(p_dataset->getResultCount());
542  for (std::uint64_t i = 0; i < packed_results.pack_count; ++i)
543  {
544  if (packed_results.p_data_packs[i].param_position > outputs.size())
545  throw std::runtime_error("Invalid result position received from decoding.");
546  if (packed_results.p_data_packs[i].buffer_count <= 0)
547  throw std::runtime_error("Invalid empty result buffers received from decoding.");
548  outputs[packed_results.p_data_packs[i].param_position] =
549  &packed_results.p_data_packs[i].p_buffers[0];
550  } // end for
551 
552  data_pack_indices.resize(p_dataset->getParameterCount(), 0);
553  b_valid = validateResult(p_dataset, data_pack_indices.data(),
554  outputs,
555  this->getBackendDescription().descriptor.data_type);
556  }
557  catch (std::exception &ex)
558  {
559  b_valid = false;
560  s_error_msg = ex.what();
561  }
562  catch (...)
563  {
564  b_valid = false;
565  }
566 
567  if (!b_valid)
568  {
569  std::cout << hebench::Logging::GlobalLogger::log("", true) << std::endl;
570  ss = std::stringstream();
571  ss << "Validation failed" << std::endl
572  << "Result, " << i + 1 << std::endl;
573  if (!s_error_msg.empty())
574  ss << s_error_msg << std::endl;
575  std::cout << IOS_MSG_FAILED << hebench::Logging::GlobalLogger::log(ss.str()) << std::endl;
576 
577  // log the parameters, expected result and received result.
578  ss << std::endl;
579  logResult(ss, p_dataset, data_pack_indices.data(),
580  outputs,
581  this->getBackendDescription().descriptor.data_type);
582  out_report.appendFooter(ss.str());
583  } // end else
584  } // end if
585 
586  ++progress_report_batch;
587  } // end if
588 
589  // clean up data we no longer need
590 
591  // even though it is RAII, it is better to free up space here,
592  // just in case these local handles are large
593  h_plain_results[i].destroy();
594  } // end for
595  h_plain_results.clear();
596 
597  if (b_valid)
598  {
599  // report valid op
600  if ((op_count) % report_every_n_elements != 0)
601  {
602  // pretty-print end of result report
603  while (mini_reports_cnt++ < 3)
604  std::cout << hebench::Logging::GlobalLogger::log(".", false) << std::flush;
605  std::cout << hebench::Logging::GlobalLogger::log(" " + std::to_string(op_count)) << std::endl;
606  } // end if
607  else
608  std::cout << hebench::Logging::GlobalLogger::log("") << std::endl;
609  std::cout << IOS_MSG_OK << std::endl;
610  } // end if
611 
612  if (!run_config.b_validate_results)
613  {
614  out_report.prependFooter("Validation skipped");
615  std::cout << IOS_MSG_WARNING << hebench::Logging::GlobalLogger::log("Validation skipped.") << std::endl;
616  } // end if
617 
618  std::cout << IOS_MSG_DONE << hebench::Logging::GlobalLogger::log("Test Completed.") << std::endl;
619 
620  return b_valid;
621 }
622 
623 } // namespace TestHarness
624 } // namespace hebench
void prependFooter(const std::string &new_header, bool new_line=true)
void setEventCapacity(uint64_t new_capacity)
void addEventType(uint32_t event_type_id, const std::string &event_type_header, bool is_main_event=false)
Adds a new event type to the types of events.
void appendFooter(const std::string &new_header, bool new_line=true)
const hebench::APIBridge::BenchmarkDescriptor & descriptor
Benchmark backend descriptor, as retrieved by backend, corresponding to the registration handle h_des...
std::uint64_t default_min_test_time_ms
Default minimum test time in milliseconds.
BenchmarkLatency(std::shared_ptr< Engine > p_engine, const IBenchmarkDescriptor::DescriptionToken &description_token)
bool run(hebench::Utilities::TimingReportEx &out_report, IBenchmark::RunConfig &config) override
Executes the benchmark latency test.
Token returned by a successful call to IBenchmarkDescriptor::matchBenchmarkDescriptor().
bool b_validate_results
Specifies whether the benchmark will validate backend results (true) or it will simply benchmark with...
Provides configuration to and retrieves data from a benchmark run.
std::shared_ptr< IDataLoader > Ptr
virtual IDataLoader::Ptr getDataset() const =0
Dataset to be used for operations previously initialized during creation of this object.
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.
const BenchmarkDescription::Backend & getBackendDescription() const
Allows read-only access to this benchmark backend description.
const BenchmarkDescription::Configuration & getBenchmarkConfiguration() const
Allows read-only access to this benchmark configuration.
const hebench::APIBridge::Handle & handle() const override
std::uint32_t getEventIDNext()
Returns the next available event ID.
void validateRetCode(hebench::APIBridge::ErrorCode err_code, bool last_error=true) const
Validates whether the specified HEBench API return code represents a success or error.
void addEvent(hebench::Common::TimingReportEvent::Ptr p_event)
#define IOS_MSG_FAILED
#define IOS_MSG_DONE
#define IOS_MSG_WARNING
#define IOS_MSG_INFO
#define IOS_MSG_OK
ErrorCode encrypt(Handle h_benchmark, Handle h_plaintext, Handle *h_ciphertext)
Encrypts a plain text into a cipher text.
ErrorCode operate(Handle h_benchmark, Handle h_remote_packed_params, const ParameterIndexer *p_param_indexers, uint64_t indexers_count, Handle *h_remote_output)
Performs the workload operation of the benchmark.
DataPack * p_data_packs
Collection of data packs.
Definition: types.h:625
ErrorCode encode(Handle h_benchmark, const DataPackCollection *p_parameters, Handle *h_plaintext)
Given a pack of parameters in raw, native data format, encodes them into plain text suitable for back...
std::uint64_t min_test_time_ms
Specifies the minimum time, in milliseconds, to run the test.
Definition: types.h:447
ErrorCode decrypt(Handle h_benchmark, Handle h_ciphertext, Handle *h_plaintext)
Decrypts a cipher text into corresponding plain text.
std::uint32_t cipher_param_mask
Input mask to define which operation parameters for the computation are plain text or cipher text.
Definition: types.h:533
ErrorCode store(Handle h_benchmark, Handle h_remote, Handle *h_local_packed_params, std::uint64_t local_count)
Retrieves the specified data from the backend.
CategoryParams cat_params
Parameters for the category.
Definition: types.h:532
std::uint64_t pack_count
Number of data packs in the collection.
Definition: types.h:626
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
ErrorCode load(Handle h_benchmark, const Handle *h_local_packed_params, std::uint64_t local_count, Handle *h_remote)
Loads the specified data from the local host into the remote backend to use as parameter during a cal...
std::uint64_t buffer_count
Number of data buffers in p_buffers.
Definition: types.h:613
ErrorCode decode(Handle h_benchmark, Handle h_plaintext, DataPackCollection *p_native)
Decodes plaintext data into the appropriate raw, native format.
NativeDataBuffer * p_buffers
Array of data buffers for parameter.
Definition: types.h:612
Defines a collection of data packs.
Definition: types.h:624
Structure to contain flexible data.
Definition: types.h:552
std::string to_string(const std::string_view &s)