o-du/phy
Intel O-RAN/X-RAN Generated Doxygen Documentation
common.hpp
Go to the documentation of this file.
1 /******************************************************************************
2 *
3 * Copyright (c) 2019 Intel.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *******************************************************************************/
18 
19 
20 /* This is the new utility file for all tests, all new common functionality has to go here.
21  When contributing to the common.hpp please focus on readability and maintainability rather than
22  execution time. */
23 #ifndef XRANLIB_COMMON_HPP
24 #define XRANLIB_COMMON_HPP
25 
26 /* Disable warnings generated by JSON parser */
27 #pragma warning(disable : 191)
28 #pragma warning(disable : 186)
29 #pragma warning(disable : 192)
30 
31 #include <exception>
32 #include <random>
33 #include <string>
34 #include <utility>
35 #include <vector>
36 
37 #include <immintrin.h>
38 #include <malloc.h>
39 
40 #define _BBLIB_DPDK_
41 
42 #ifdef _BBLIB_DPDK_
43 #include <rte_config.h>
44 #include <rte_malloc.h>
45 #endif
46 
47 #include "gtest/gtest.h"
48 
49 #include "common_typedef_xran.h"
50 
51 #include "json.hpp"
52 
54 
55 #define ASSERT_ARRAY_NEAR(reference, actual, size, precision) \
56  assert_array_near(reference, actual, size, precision)
57 
58 #define ASSERT_ARRAY_EQ(reference, actual, size) \
59  assert_array_eq(reference, actual, size)
60 
61 #define ASSERT_AVG_GREATER_COMPLEX(reference, actual, size, precision) \
62  assert_avg_greater_complex(reference, actual, size, precision)
63 
65 {
66  static long repetition;
67  static long loop;
68  static unsigned cpu_id;
69 };
70 
71 struct missing_config_file_exception : public std::exception
72 {
73  const char * what () const throw () override {
74  return "JSON file cannot be opened!";
75  }
76 };
77 
78 struct reading_input_file_exception : public std::exception
79 {
80  const char * what () const throw () override {
81  return "Input file cannot be read!";
82  }
83 };
84 
90 int bind_to_cpu(const unsigned cpu);
91 
99 std::pair<double, double> calculate_statistics(const std::vector<long> values);
100 
106 std::vector<unsigned> get_sequence(const unsigned number);
107 
114 json read_json_from_file(const std::string &filename);
115 
122 char* read_data_to_aligned_array(const std::string &filename);
123 
128 unsigned long tsc_recovery();
129 
134 unsigned long tsc_tick();
135 
156 class KernelTests : public testing::TestWithParam<unsigned>
157 {
158 public:
159  static json conf;
160  static std::string test_type;
161 
162  static void SetUpTestCase()
163  {
164  test_type = "None";
165 
166  try
167  {
168  conf = read_json_from_file("conf.json");
169  }
171  {
172  std::cout << "[----------] SetUpTestCase failed: " << e.what() << std::endl;
173  exit(-1);
174  }
175 
176  tsc = tsc_recovery();
177 
178  if(!tsc)
179  {
180  std::cout << "[----------] SetUpTestCase failed: TSC recovery failed" << std::endl;
181  exit(-1);
182  }
183  }
184 
185  static void TearDownTestCase()
186  {
187  /* Free resources - nothing to free at the moment */
188  }
189 
190  static unsigned get_number_of_cases(const std::string &type)
191  {
192  try
193  {
194  json json_data = read_json_from_file("conf.json");
195 
196  return json_data[type].size();
197  }
199  {
200  std::cout << "[----------] get_number_of_cases failed: " << e.what() << std::endl;
201 
202  exit(-1);
203  }
204  catch(std::domain_error &e)
205  {
206  std::cout << "[----------] get_number_of_cases failed: " << e.what() << std::endl;
207  std::cout << "[----------] Use a default value: 0" << std::endl;
208 
209  return 0;
210  }
211  }
212 
213 protected:
214  double division_factor = 1.0;
215  std::string result_units = "None";
216  int parallelization_factor = 1;
217 
222  void set_division_factor(const double factor)
223  {
224  division_factor = factor;
225  }
226 
231  void set_results_units(const std::string &units)
232  {
233  result_units = units;
234  }
235 
240  void set_parallelization_factor(const int factor)
241  {
242  parallelization_factor = factor;
243  }
244 
252  template <typename F, typename ... Args>
253  void performance(const std::string &isa, const std::string &module_name, F function,
254  Args ... args) {
255  ASSERT_EQ(0, bind_to_cpu(BenchmarkParameters::cpu_id)) << "Failed to bind to cpu!";
256 
257  const auto result = run_benchmark(function, args ...);
258  const auto scaled_mean = result.first / division_factor / tsc;
259  const auto scaled_stddev = result.second / division_factor / tsc;
260 
261  print_and_store_results(isa, get_case_name(), module_name, get_case_name(), result_units,
262  parallelization_factor, scaled_mean, scaled_stddev);
263  }
264 
271  void print_test_description(const std::string &isa, const std::string &module_name) {
272  print_and_store_results(isa, get_case_name(), module_name, get_case_name(), result_units,
273  parallelization_factor, 0, 0);
274  }
275 
277 
289  template <typename T>
290  T get_input_parameter(const std::string &parameter_name)
291  {
292  try
293  {
294  return get_parameter<T>("parameters", parameter_name);
295  }
296  catch (std::domain_error &e)
297  {
298  std::cout << "[----------] get_input_parameter (" << parameter_name
299  << ") failed: " << e.what()
300  << ". Did you mispell the parameter name?" << std::endl;
301  throw;
302  }
304  {
305  std::cout << "[----------] get_input_parameter (" << parameter_name
306  << ") failed: " << e.what() << std::endl;
307  throw;
308  }
309  }
310 
311  template <typename T>
312  T get_reference_parameter(const std::string &parameter_name)
313  {
314  try
315  {
316  return get_parameter<T>("references", parameter_name);
317  }
318  catch (std::domain_error &e)
319  {
320  std::cout << "[----------] get_reference_parameter (" << parameter_name
321  << ") failed: " << e.what()
322  << ". Did you mispell the parameter name?" << std::endl;
323  throw;
324  }
326  {
327  std::cout << "[----------] get_reference_parameter (" << parameter_name
328  << ") failed: " << e.what() << std::endl;
329  throw;
330  }
331  }
333 
338  const std::string get_case_name()
339  {
340  try
341  {
342  return conf[test_type][GetParam()]["name"];
343  }
344  catch (std::domain_error &e)
345  {
346  std::cout << "[----------] get_case_name failed: " << e.what()
347  << ". Did you specify a test name in JSON?" << std::endl;
348  std::cout << "[----------] Using a default name instead" << std::endl;
349 
350  return "Default test name";
351  }
352  }
353 
358  void init_test(const std::string &type)
359  {
360  test_type = type;
361  const std::string name = get_case_name();
362  std::cout << "[----------] Test case: " << name << std::endl;
363  }
364 
365 private:
366  static unsigned long tsc;
367 
368  template<typename T>
369  struct data_reader {
370  static T read_parameter(const int index, const std::string &type,
371  const std::string &parameter_name)
372  {
373  return conf[test_type][index][type][parameter_name];
374  }
375  };
376 
377  template<typename T>
378  struct data_reader<std::vector<T>> {
379  static std::vector<T> read_parameter(const int index, const std::string &type,
380  const std::string &parameter_name)
381  {
382  auto array_size = conf[test_type][index][type][parameter_name].size();
383 
384  std::vector<T> result(array_size);
385 
386  for(unsigned number = 0; number < array_size; number++)
387  result.at(number) = conf[test_type][index][type][parameter_name][number];
388 
389  return result;
390  }
391  };
392 
393  template<typename T>
394  struct data_reader<T*> {
395  static T* read_parameter(const int index, const std::string &type,
396  const std::string &parameter_name)
397  {
398  return (T*) read_data_to_aligned_array(conf[test_type][index][type][parameter_name]);
399  }
400  };
401 
402  template <typename T>
403  T get_parameter(const std::string &type, const std::string &parameter_name)
404  {
405  return data_reader<T>::read_parameter(GetParam(), type, parameter_name);
406  }
407 
408  void print_and_store_results(const std::string &isa,
409  const std::string &parameters,
410  const std::string &module_name,
411  const std::string &test_name,
412  const std::string &unit,
413  const int para_factor,
414  const double mean,
415  const double stddev);
416 };
417 
424 template <typename F, typename ... Args>
425 std::pair<double, double> run_benchmark(F function, Args ... args)
426 {
427  std::vector<long> results((unsigned long) BenchmarkParameters::repetition);
428 
429  for(unsigned int outer_loop = 0; outer_loop < BenchmarkParameters::repetition; outer_loop++) {
430  const auto start_time = __rdtsc();
431  for (unsigned int inner_loop = 0; inner_loop < BenchmarkParameters::loop; inner_loop++) {
432  function(args ...);
433  }
434  const auto end_time = __rdtsc();
435  results.push_back(end_time - start_time);
436  }
437 
438  return calculate_statistics(results);
439 };
440 
447 template <typename T>
448 void assert_array_eq(const T* reference, const T* actual, const int size)
449 {
450  for(int index = 0; index < size ; index++)
451  {
452  ASSERT_EQ(reference[index], actual[index])
453  <<"The wrong number is index: "<< index;
454  }
455 }
456 
464 template <typename T>
465 void assert_array_near(const T* reference, const T* actual, const int size, const double precision)
466 {
467  for(int index = 0; index < size ; index++)
468  {
469  ASSERT_NEAR(reference[index], actual[index], precision)
470  <<"The wrong number is index: "<< index;
471  }
472 }
473 
474 template <>
475 void assert_array_near<complex_float>(const complex_float* reference, const complex_float* actual, const int size, const double precision)
476 {
477  for(int index = 0; index < size ; index++)
478  {
479  ASSERT_NEAR(reference[index].re, actual[index].re, precision)
480  <<"The wrong number is RE, index: "<< index;
481  ASSERT_NEAR(reference[index].im, actual[index].im, precision)
482  <<"The wrong number is IM, index: "<< index;
483  }
484 }
485 
493 template<typename T>
494 void assert_avg_greater_complex(const T* reference, const T* actual, const int size, const double precision)
495 {
496  float mseDB, MSE;
497  double avgMSEDB = 0.0;
498  for (int index = 0; index < size; index++) {
499  T refReal = reference[2*index];
500  T refImag = reference[(2*index)+1];
501  T resReal = actual[2*index];
502  T resImag = actual[(2*index)+1];
503 
504  T errReal = resReal - refReal;
505  T errIm = resImag - refImag;
506 
507  /* For some unit tests, e.g. PUCCH deomdulation, the expected output is 0. To avoid a
508  divide by zero error, check the reference results to determine if the expected result
509  is 0 and, if so, add a 1 to the division. */
510  if (refReal == 0 && refImag == 0)
511  MSE = (float)(errReal*errReal + errIm*errIm)/(float)(refReal*refReal + refImag*refImag + 1);
512  else
513  MSE = (float)(errReal*errReal + errIm*errIm)/(float)(refReal*refReal + refImag*refImag);
514 
515  if(MSE == 0)
516  mseDB = (float)(-100.0);
517  else
518  mseDB = (float)(10.0) * (float)log10(MSE);
519 
520  avgMSEDB += (double)mseDB;
521  }
522 
523  avgMSEDB /= size;
524 
525  ASSERT_GT(precision, avgMSEDB);
526 }
527 
543 template <typename T>
544 T* aligned_malloc(const int size, const unsigned alignment)
545 {
546 #ifdef _BBLIB_DPDK_
547  return (T*) rte_malloc(NULL, sizeof(T) * size, alignment);
548 #else
549 #ifndef _WIN64
550  return (T*) memalign(alignment, sizeof(T) * size);
551 #else
552  return (T*)_aligned_malloc(sizeof(T)*size, alignment);
553 #endif
554 #endif
555 }
556 
565 template <typename T>
566 void aligned_free(T* ptr)
567 {
568 #ifdef _BBLIB_DPDK_
569  rte_free((void*)ptr);
570 #else
571 
572 #ifndef _WIN64
573  free((void*)ptr);
574 #else
575  _aligned_free((void *)ptr);
576 #endif
577 #endif
578 }
579 
592 template <typename T, typename U>
593 T* generate_random_numbers(const long size, const unsigned alignment, U& distribution)
594 {
595  auto array = (T*) aligned_malloc<char>(size * sizeof(T), alignment);
596 
597  std::random_device random_device;
598  std::default_random_engine generator(random_device());
599 
600  for(long i = 0; i < size; i++)
601  array[i] = (T)distribution(generator);
602 
603  return array;
604 }
605 
617 template <typename T>
618 T* generate_random_data(const long size, const unsigned alignment)
619 {
620  std::uniform_int_distribution<> random(0, 255);
621 
622  return (T*)generate_random_numbers<char, std::uniform_int_distribution<>>(size * sizeof(T), alignment, random);
623 }
624 
639 template <typename T>
640 T* generate_random_int_numbers(const long size, const unsigned alignment, const T lo_range,
641  const T up_range)
642 {
643  std::uniform_int_distribution<T> random(lo_range, up_range);
644 
645  return generate_random_numbers<T, std::uniform_int_distribution<T>>(size, alignment, random);
646 }
647 
662 template <typename T>
663 T* generate_random_real_numbers(const long size, const unsigned alignment, const T lo_range,
664  const T up_range)
665 {
666  std::uniform_real_distribution<T> distribution(lo_range, up_range);
667 
668  return generate_random_numbers<T, std::uniform_real_distribution<T>>(size, alignment, distribution);
669 }
670 
671 #endif //XRANLIB_COMMON_HPP
std::vector< unsigned > get_sequence(const unsigned number)
For a given number return sequence of number from 0 to number - 1.
Definition: common.cpp:78
#define _aligned_malloc(x, y)
void print_test_description(const std::string &isa, const std::string &module_name)
Print unique test description to the results xml file.
Definition: common.hpp:271
unsigned long tsc_tick()
Return the current value of the TSC.
Definition: common.cpp:162
static unsigned get_number_of_cases(const std::string &type)
Definition: common.hpp:190
size_type size() const noexcept
returns the number of elements
Definition: json.hpp:5040
T * generate_random_numbers(const long size, const unsigned alignment, U &distribution)
generate random numbers.
Definition: common.hpp:593
basic_json<> json
default JSON class
Definition: json.hpp:12889
a class to store JSON values
Definition: json.hpp:1016
void performance(const std::string &isa, const std::string &module_name, F function, Args ... args)
Run performance test case for a given function.
Definition: common.hpp:253
void assert_avg_greater_complex(const T *reference, const T *actual, const int size, const double precision)
Assert average diff of two arrays. It calls ASSERT_GT to check the average.
Definition: common.hpp:494
std::pair< double, double > calculate_statistics(const std::vector< long > values)
Calculate the mean and variance from the result of the run_benchmark.
Definition: common.cpp:60
static unsigned cpu_id
Definition: common.hpp:68
static json conf
Definition: common.hpp:159
const char * what() const override
Definition: common.hpp:80
static std::string test_type
Definition: common.hpp:160
void assert_array_near< complex_float >(const complex_float *reference, const complex_float *actual, const int size, const double precision)
Definition: common.hpp:475
void assert_array_eq(const T *reference, const T *actual, const int size)
Assert elements of two arrays. It calls ASSERT_EQ for each element of the array.
Definition: common.hpp:448
void aligned_free(T *ptr)
Frees memory pointed by the given pointer.
Definition: common.hpp:566
std::pair< double, double > run_benchmark(F function, Args ... args)
Run the given function and return the mean run time and stddev.
Definition: common.hpp:425
T * generate_random_data(const long size, const unsigned alignment)
generate random data.
Definition: common.hpp:618
T get_reference_parameter(const std::string &parameter_name)
Definition: common.hpp:312
void set_results_units(const std::string &units)
Set reults units.
Definition: common.hpp:231
T * generate_random_int_numbers(const long size, const unsigned alignment, const T lo_range, const T up_range)
generate integer random numbers.
Definition: common.hpp:640
static long repetition
Definition: common.hpp:66
const char * what() const override
Definition: common.hpp:73
void init_test(const std::string &type)
Defines section in the conf.json that is used to load parameters from.
Definition: common.hpp:358
unsigned long tsc_recovery()
Measure the TSC on the machine.
Definition: common.cpp:119
This header file defines those data type both used by eNB and UE.
json read_json_from_file(const std::string &filename)
Read JSON from the given file.
Definition: common.cpp:106
T * generate_random_real_numbers(const long size, const unsigned alignment, const T lo_range, const T up_range)
generate real random numbers.
Definition: common.hpp:663
const std::string get_case_name()
Get name of the test case from JSON file.
Definition: common.hpp:338
const std::string module_name
int bind_to_cpu(const unsigned cpu)
Attach current process to the selected core.
Definition: common.cpp:47
T get_input_parameter(const std::string &parameter_name)
Load selected data from a JSON object. get_input_parameter loads data from parameters section of the ...
Definition: common.hpp:290
static void SetUpTestCase()
Definition: common.hpp:162
void set_parallelization_factor(const int factor)
Set size of processed data.
Definition: common.hpp:240
static long loop
Definition: common.hpp:67
static void TearDownTestCase()
Definition: common.hpp:185
char * read_data_to_aligned_array(const std::string &filename)
Read binary data from the file.
Definition: common.cpp:86
Defines 64-bit complex structure; both real part and image part have 32 bit width.
void set_division_factor(const double factor)
Set division factor.
Definition: common.hpp:222
void assert_array_near(const T *reference, const T *actual, const int size, const double precision)
Assert elements of two arrays. It calls ASSERT_NEAR for each element of the array.
Definition: common.hpp:465
T * aligned_malloc(const int size, const unsigned alignment)
Allocates memory of the given size.
Definition: common.hpp:544