GCC Code Coverage Report


Directory: ./
File: apps/volk_profile.cc
Date: 2023-10-23 23:10:04
Exec Total Coverage
Lines: 0 186 0.0%
Functions: 0 15 0.0%
Branches: 0 486 0.0%

Line Branch Exec Source
1 /* -*- c++ -*- */
2 /*
3 * Copyright 2012-2014 Free Software Foundation, Inc.
4 *
5 * This file is part of VOLK
6 *
7 * SPDX-License-Identifier: LGPL-3.0-or-later
8 */
9
10 #if HAS_STD_FILESYSTEM_EXPERIMENTAL
11 #include <experimental/filesystem>
12 #else
13 #include <filesystem>
14 #endif
15 #include <stddef.h> // for size_t
16 #include <sys/stat.h> // for stat
17 #include <volk/volk_prefs.h> // for volk_get_config_path
18 #include <fstream> // IWYU pragma: keep
19 #include <iostream> // for operator<<, basic_ostream
20 #include <map> // for map, map<>::iterator
21 #include <utility> // for pair
22 #include <vector> // for vector, vector<>::const_...
23
24 #include "kernel_tests.h" // for init_test_list
25 #include "qa_utils.h" // for volk_test_results_t, vol...
26 #include "volk/volk_complex.h" // for lv_32fc_t
27 #include "volk_option_helpers.h" // for option_list, option_t
28 #include "volk_profile.h"
29
30 #if HAS_STD_FILESYSTEM_EXPERIMENTAL
31 namespace fs = std::experimental::filesystem;
32 #else
33 namespace fs = std::filesystem;
34 #endif
35
36 volk_test_params_t test_params(1e-6f, 327.f, 131071, 1987, false, "");
37
38 void set_benchmark(bool val) { test_params.set_benchmark(val); }
39 void set_tolerance(float val) { test_params.set_tol(val); }
40 void set_vlen(int val) { test_params.set_vlen((unsigned int)val); }
41 void set_iter(int val) { test_params.set_iter((unsigned int)val); }
42 void set_substr(std::string val) { test_params.set_regex(val); }
43 bool update_mode = false;
44 void set_update(bool val) { update_mode = val; }
45 bool dry_run = false;
46 void set_dryrun(bool val) { dry_run = val; }
47 std::string json_filename("");
48 void set_json(std::string val) { json_filename = val; }
49 std::string volk_config_path("");
50 void set_volk_config(std::string val) { volk_config_path = val; }
51
52 int main(int argc, char* argv[])
53 {
54
55 option_list profile_options("volk_profile");
56 profile_options.add(
57 option_t("benchmark", "b", "Run all kernels (benchmark mode)", set_benchmark));
58 profile_options.add(
59 option_t("tol", "t", "Set the default tolerance for all tests", set_tolerance));
60 profile_options.add(
61 option_t("vlen", "v", "Set the default vector length for tests", set_vlen));
62 profile_options.add((option_t(
63 "iter", "i", "Set the default number of test iterations per kernel", set_iter)));
64 profile_options.add(
65 (option_t("tests-substr", "R", "Run tests matching substring", set_substr)));
66 profile_options.add(
67 (option_t("update", "u", "Run only kernels missing from config", set_update)));
68 profile_options.add(
69 (option_t("dry-run",
70 "n",
71 "Dry run. Respect other options, but don't write to file",
72 set_dryrun)));
73 profile_options.add((option_t(
74 "json", "j", "Write results to JSON file named as argument value", set_json)));
75 profile_options.add(
76 (option_t("path", "p", "Specify the volk_config path", set_volk_config)));
77 profile_options.parse(argc, argv);
78
79 if (profile_options.present("help")) {
80 return 0;
81 }
82
83 if (dry_run) {
84 std::cout << "Warning: this IS a dry-run. Config will not be written!"
85 << std::endl;
86 }
87
88 // Adding program options
89 std::ofstream json_file;
90 std::string config_file;
91
92 if (json_filename != "") {
93 json_file.open(json_filename.c_str());
94 }
95
96 if (volk_config_path != "") {
97 config_file = volk_config_path + "/volk_config";
98 }
99
100 // Run tests
101 std::vector<volk_test_results_t> results;
102 if (update_mode) {
103 if (config_file != "")
104 read_results(&results, config_file);
105 else
106 read_results(&results);
107 }
108
109 // Initialize the list of tests
110 std::vector<volk_test_case_t> test_cases = init_test_list(test_params);
111
112 // Iterate through list of tests running each one
113 std::string substr_to_match(test_params.kernel_regex());
114 for (unsigned int ii = 0; ii < test_cases.size(); ++ii) {
115 bool regex_match = true;
116
117 volk_test_case_t test_case = test_cases[ii];
118 // if the kernel name matches regex then do the test
119 std::string test_case_name = test_case.name();
120 if (test_case_name.find(substr_to_match) == std::string::npos) {
121 regex_match = false;
122 }
123
124 // if we are in update mode check if we've already got results
125 // if we have any, then no need to test that kernel
126 bool update = true;
127 if (update_mode) {
128 for (unsigned int jj = 0; jj < results.size(); ++jj) {
129 if (results[jj].name == test_case.name() ||
130 results[jj].name == test_case.puppet_master_name()) {
131 update = false;
132 break;
133 }
134 }
135 }
136
137 if (regex_match && update) {
138 try {
139 run_volk_tests(test_case.desc(),
140 test_case.kernel_ptr(),
141 test_case.name(),
142 test_case.test_parameters(),
143 &results,
144 test_case.puppet_master_name());
145 } catch (std::string& error) {
146 std::cerr << "Caught Exception in 'run_volk_tests': " << error
147 << std::endl;
148 }
149 }
150 }
151
152
153 // Output results according to provided options
154 if (json_filename != "") {
155 write_json(json_file, results);
156 json_file.close();
157 }
158
159 if (!dry_run) {
160 if (config_file != "")
161 write_results(&results, false, config_file);
162 else
163 write_results(&results, false);
164 } else {
165 std::cout << "Warning: this was a dry-run. Config not generated" << std::endl;
166 }
167 return 0;
168 }
169
170 void read_results(std::vector<volk_test_results_t>* results)
171 {
172 char path[1024];
173 volk_get_config_path(path, true);
174 if (path[0] == 0) {
175 std::cout << "No prior test results found ..." << std::endl;
176 return;
177 }
178
179 read_results(results, std::string(path));
180 }
181
182 void read_results(std::vector<volk_test_results_t>* results, std::string path)
183 {
184 struct stat buffer;
185 bool config_status = (stat(path.c_str(), &buffer) == 0);
186
187 if (config_status) {
188 // a config exists and we are reading results from it
189 std::ifstream config(path.c_str());
190 char config_line[256];
191 while (config.getline(config_line, 255)) {
192 // tokenize the input line by kernel_name unaligned aligned
193 // then push back in the results vector with fields filled in
194
195 std::vector<std::string> single_kernel_result;
196 std::string config_str(config_line);
197 std::size_t str_size = config_str.size();
198 std::size_t found = config_str.find(' ');
199
200 // Split line by spaces
201 while (found && found < str_size) {
202 found = config_str.find(' ');
203 // kernel names MUST be less than 128 chars, which is
204 // a length restricted by volk/volk_prefs.c
205 // on the last token in the parsed string we won't find a space
206 // so make sure we copy at most 128 chars.
207 if (found > 127) {
208 found = 127;
209 }
210 str_size = config_str.size();
211 char line_buffer[128] = { '\0' };
212 config_str.copy(line_buffer, found + 1, 0);
213 line_buffer[found] = '\0';
214 single_kernel_result.push_back(std::string(line_buffer));
215 config_str.erase(0, found + 1);
216 }
217
218 if (single_kernel_result.size() == 3) {
219 volk_test_results_t kernel_result;
220 kernel_result.name = std::string(single_kernel_result[0]);
221 kernel_result.config_name = std::string(single_kernel_result[0]);
222 kernel_result.best_arch_u = std::string(single_kernel_result[1]);
223 kernel_result.best_arch_a = std::string(single_kernel_result[2]);
224 results->push_back(kernel_result);
225 }
226 }
227 }
228 }
229
230 void write_results(const std::vector<volk_test_results_t>* results, bool update_result)
231 {
232 char path[1024];
233 volk_get_config_path(path, false);
234 if (path[0] == 0) {
235 std::cout << "Aborting 'No config save path found' ..." << std::endl;
236 return;
237 }
238
239 write_results(results, update_result, std::string(path));
240 }
241
242 void write_results(const std::vector<volk_test_results_t>* results,
243 bool update_result,
244 const std::string path)
245 {
246 // struct stat buffer;
247 // bool config_status = (stat (path.c_str(), &buffer) == 0);
248
249 /*
250 * These
251 */
252 const fs::path config_path(path);
253 if (!fs::exists(config_path.parent_path())) {
254 std::cout << "Creating " << config_path.parent_path() << "..." << std::endl;
255 fs::create_directories(config_path.parent_path());
256 }
257
258 std::ofstream config;
259 if (update_result) {
260 std::cout << "Updating " << path << "..." << std::endl;
261 config.open(path.c_str(), std::ofstream::app);
262 if (!config.is_open()) { // either we don't have write access or we don't have the
263 // dir yet
264 std::cout << "Error opening file " << path << std::endl;
265 }
266 } else {
267 std::cout << "Writing " << path << "..." << std::endl;
268 config.open(path.c_str());
269 if (!config.is_open()) { // either we don't have write access or we don't have the
270 // dir yet
271 std::cout << "Error opening file " << path << std::endl;
272 }
273
274 config << "\
275 #this file is generated by volk_profile.\n\
276 #the function name is followed by the preferred architecture.\n\
277 ";
278 }
279
280 std::vector<volk_test_results_t>::const_iterator profile_results;
281 for (profile_results = results->begin(); profile_results != results->end();
282 ++profile_results) {
283 config << profile_results->config_name << " " << profile_results->best_arch_a
284 << " " << profile_results->best_arch_u << std::endl;
285 }
286 config.close();
287 }
288
289 void write_json(std::ofstream& json_file, std::vector<volk_test_results_t> results)
290 {
291 json_file << "{" << std::endl;
292 json_file << " \"volk_tests\": [" << std::endl;
293 size_t len = results.size();
294 size_t i = 0;
295 std::vector<volk_test_results_t>::iterator result;
296 for (result = results.begin(); result != results.end(); ++result) {
297 json_file << " {" << std::endl;
298 json_file << " \"name\": \"" << result->name << "\"," << std::endl;
299 json_file << " \"vlen\": " << (int)(result->vlen) << "," << std::endl;
300 json_file << " \"iter\": " << result->iter << "," << std::endl;
301 json_file << " \"best_arch_a\": \"" << result->best_arch_a << "\","
302 << std::endl;
303 json_file << " \"best_arch_u\": \"" << result->best_arch_u << "\","
304 << std::endl;
305 json_file << " \"results\": {" << std::endl;
306 size_t results_len = result->results.size();
307 size_t ri = 0;
308
309 std::map<std::string, volk_test_time_t>::iterator kernel_time_pair;
310 for (kernel_time_pair = result->results.begin();
311 kernel_time_pair != result->results.end();
312 ++kernel_time_pair) {
313 volk_test_time_t time = kernel_time_pair->second;
314 json_file << " \"" << time.name << "\": {" << std::endl;
315 json_file << " \"name\": \"" << time.name << "\"," << std::endl;
316 json_file << " \"time\": " << time.time << "," << std::endl;
317 json_file << " \"units\": \"" << time.units << "\"" << std::endl;
318 json_file << " }";
319 if (ri + 1 != results_len) {
320 json_file << ",";
321 }
322 json_file << std::endl;
323 ri++;
324 }
325 json_file << " }" << std::endl;
326 json_file << " }";
327 if (i + 1 != len) {
328 json_file << ",";
329 }
330 json_file << std::endl;
331 i++;
332 }
333 json_file << " ]" << std::endl;
334 json_file << "}" << std::endl;
335 }
336