Update to odulow per maintenance bronze
[o-du/phy.git] / fhi_lib / test / common / common.cpp
1 /*******************************************************************************
2  *
3  * <COPYRIGHT_TAG>
4  *
5  *******************************************************************************/
6
7 #include <cmath>
8 #include <fstream>
9 #include <numeric>
10
11 #ifndef _WIN64
12 #include <unistd.h>
13 #include <sys/syscall.h>
14 #else
15 #include <Windows.h>
16 #endif
17
18 #include "common.hpp"
19
20 #ifndef CPU_ID
21 #define CPU_ID 4
22 #endif
23
24 /* Required to avoid linker errors */
25 json KernelTests::conf;
26 std::string KernelTests::test_type;
27 unsigned long KernelTests::tsc;
28
29
30 long BenchmarkParameters::repetition = 40;
31 long BenchmarkParameters::loop = 30;
32 unsigned BenchmarkParameters::cpu_id = CPU_ID;
33
34 int bind_to_cpu(const unsigned cpu)
35 {
36 #ifndef _WIN64
37     const auto pid = syscall(SYS_gettid);
38     cpu_set_t mask {};
39     CPU_ZERO(&mask);
40     CPU_SET(cpu, &mask);
41     return sched_setaffinity(__pid_t(pid), sizeof(mask), &mask);
42 #else
43     return -1;
44 #endif
45 }
46
47 std::pair<double, double> calculate_statistics(const std::vector<long> values)
48 {
49     const auto sum = std::accumulate(values.begin(), values.end(), 0L);
50
51     const auto number_of_iterations = BenchmarkParameters::repetition *
52             BenchmarkParameters::loop;
53
54     const auto mean = sum / (double) number_of_iterations;
55
56     auto stddev_accumulator = 0.0;
57     for (auto v : values)
58         stddev_accumulator = pow((v / BenchmarkParameters::loop) - mean, 2);
59
60     const auto stddev = sqrt(stddev_accumulator / BenchmarkParameters::repetition);
61
62     return {mean, stddev};
63 }
64
65 std::vector<unsigned> get_sequence(const unsigned number)
66 {
67     std::vector<unsigned> sequence(number);
68     std::iota(sequence.begin(), sequence.end(), 0);
69
70     return sequence;
71 }
72
73 char* read_data_to_aligned_array(const std::string &filename)
74 {
75     std::ifstream input_stream(filename, std::ios::binary);
76
77     std::vector<char> buffer((std::istreambuf_iterator<char>(input_stream)),
78                               std::istreambuf_iterator<char>());
79
80     if(buffer.size() == 0)
81         throw reading_input_file_exception();
82
83     auto aligned_buffer = aligned_malloc<char>((int) buffer.size(), 64);
84
85     if(aligned_buffer == nullptr)
86         throw std::runtime_error("Failed to allocate memory for the test vector!");
87
88     std::copy(buffer.begin(), buffer.end(), aligned_buffer);
89
90     return aligned_buffer;
91 }
92
93 json read_json_from_file(const std::string &filename)
94 {
95     json result;
96
97     std::ifstream json_stream(filename);
98     if(!json_stream.is_open())
99         throw missing_config_file_exception();
100
101     json_stream >> result;
102
103     return result;
104 }
105
106 unsigned long tsc_recovery()
107 {
108 #ifndef _WIN64
109     constexpr auto ns_per_sec = 1E9;
110
111     struct timespec sleeptime = {.tv_nsec = __syscall_slong_t(5E8) };
112
113     struct timespec t_start, t_end;
114
115     if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_start) == 0)
116     {
117         unsigned long start = tsc_tick();
118
119         nanosleep(&sleeptime,NULL);
120         clock_gettime(CLOCK_MONOTONIC_RAW, &t_end);
121
122         unsigned long end = tsc_tick();
123
124         unsigned long ns = (unsigned long)((t_end.tv_sec - t_start.tv_sec) * ns_per_sec + t_end.tv_nsec - t_start.tv_nsec);
125
126         double secs = (double) ns / ns_per_sec;
127
128         unsigned long resolution_timer = (unsigned long)((end - start)/secs);
129         unsigned long tick_per_usec = (resolution_timer / 1000000);
130
131         std::cout << "[----------] System clock (rdtsc) resolution " << resolution_timer << " [Hz]" << std::endl;
132         std::cout << "[----------] Ticks per us " << tick_per_usec << std::endl;
133
134         return tick_per_usec;
135     }
136 #else
137
138     LARGE_INTEGER tick_per_sec;
139     QueryPerformanceFrequency(&tick_per_sec);
140
141     std::cout << "[----------] System clock (rdtsc) resolution unknown" << std::endl;
142     std::cout << "[----------] Ticks per us " << (tick_per_sec.QuadPart / 1000000) << std::endl;
143     return (unsigned long) tick_per_sec.QuadPart;
144
145 #endif
146     return 0;
147 }
148
149 unsigned long tsc_tick()
150 {
151 #ifndef _WIN64
152     unsigned long hi, lo;
153
154     __asm volatile ("rdtsc" : "=a"(lo), "=d"(hi));
155
156     return lo | (hi << 32);
157 #else
158         return 0;
159 #endif
160 }
161
162 void KernelTests::print_and_store_results(const std::string &isa, const std::string &parameters,
163                                           const std::string &module_name, const std::string &test_name,
164                                           const std::string &unit, const int para_factor,
165                                           const double mean, const double stddev)
166 {
167     std::cout << "[----------] " << "Mean" << " = " << std::fixed << mean << " us" << std::endl;
168     std::cout << "[----------] " << "Stddev" << " = " << stddev << " us" << std::endl;
169
170 #ifndef _WIN64
171     /* Two properties below should uniquely identify a test case */
172     RecordProperty("kernelname", module_name);
173     RecordProperty("parameters", parameters);
174
175     RecordProperty("isa", isa);
176     RecordProperty("unit", unit);
177     RecordProperty("parallelization_factor", para_factor);
178
179     RecordProperty("mean", std::to_string(mean));
180     RecordProperty("stddev", std::to_string(stddev));
181 #endif
182 }