]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // Copyright John Maddock 2015. |
2 | // Use, modification and distribution are subject to the | |
3 | // Boost Software License, Version 1.0. (See accompanying file | |
4 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
5 | ||
6 | #ifdef _MSC_VER | |
7 | # pragma warning (disable : 4224) | |
8 | #endif | |
9 | ||
10 | #include <boost/regex.hpp> | |
11 | #include <boost/lexical_cast.hpp> | |
12 | #include <boost/filesystem.hpp> | |
13 | #include <boost/filesystem/fstream.hpp> | |
b32b8144 | 14 | #include <boost/functional/hash.hpp> |
7c673cae FG |
15 | #include <vector> |
16 | #include <set> | |
17 | #include <iostream> | |
18 | #include <sstream> | |
19 | #include <iomanip> | |
20 | ||
21 | std::vector<std::vector<double> > data; | |
22 | ||
23 | inline std::string sanitize_string(const std::string& s) | |
24 | { | |
25 | static const boost::regex e("[^a-zA-Z0-9]+"); | |
26 | std::string result = boost::regex_replace(s, e, "_"); | |
27 | while(result[0] == '_') | |
28 | result.erase(0); | |
29 | return result; | |
30 | } | |
31 | ||
b32b8144 FG |
32 | inline std::string sanitize_short_string(const std::string& s) |
33 | { | |
34 | unsigned id = boost::hash<std::string>()(s); | |
35 | return sanitize_string("id" + boost::lexical_cast<std::string>(id)); | |
36 | } | |
37 | ||
7c673cae FG |
38 | std::string format_precision(double val, int digits) |
39 | { | |
40 | std::stringstream ss; | |
41 | ss << std::setprecision(digits); | |
42 | ss << std::fixed; | |
43 | ss << val; | |
44 | return ss.str(); | |
45 | } | |
46 | ||
47 | static std::string content; | |
48 | boost::filesystem::path path_to_content; | |
49 | ||
50 | struct content_loader | |
51 | { | |
52 | content_loader() | |
53 | { | |
54 | boost::filesystem::path p(__FILE__); | |
55 | p = p.parent_path(); | |
56 | p /= "doc"; | |
57 | p /= "performance_tables.qbk"; | |
58 | path_to_content = p; | |
59 | if(boost::filesystem::exists(p)) | |
60 | { | |
61 | boost::filesystem::ifstream is(p); | |
62 | if(is.good()) | |
63 | { | |
64 | do | |
65 | { | |
66 | char c = static_cast<char>(is.get()); | |
67 | if(c != EOF) | |
68 | content.append(1, c); | |
69 | } while(is.good()); | |
70 | } | |
71 | } | |
72 | } | |
73 | ~content_loader() | |
74 | { | |
75 | boost::filesystem::ofstream os(path_to_content); | |
76 | os << content; | |
77 | } | |
78 | void instantiate()const | |
79 | { | |
80 | } | |
81 | }; | |
82 | ||
83 | static const content_loader loader; | |
84 | ||
85 | void load_table(std::vector<std::vector<std::string> >& table, std::string::const_iterator begin, std::string::const_iterator end) | |
86 | { | |
87 | static const boost::regex item_e( | |
88 | "\\[" | |
89 | "([^\\[\\]]*(?0)?)*" | |
90 | "\\]" | |
91 | ); | |
92 | ||
93 | boost::regex_token_iterator<std::string::const_iterator> i(begin, end, item_e), j; | |
94 | ||
95 | while(i != j) | |
96 | { | |
97 | // Add a row: | |
98 | table.push_back(std::vector<std::string>()); | |
99 | boost::regex_token_iterator<std::string::const_iterator> k(i->first + 1, i->second - 1, item_e); | |
100 | while(k != j) | |
101 | { | |
102 | // Add a cell: | |
103 | table.back().push_back(std::string(k->first + 1, k->second - 1)); | |
104 | ++k; | |
105 | } | |
106 | ++i; | |
107 | } | |
108 | } | |
109 | ||
110 | std::string save_table(std::vector<std::vector<std::string> >& table) | |
111 | { | |
112 | std::string result; | |
113 | ||
114 | for(std::vector<std::vector<std::string> >::const_iterator i = table.begin(), j = table.end(); i != j; ++i) | |
115 | { | |
116 | result += "["; | |
117 | for(std::vector<std::string>::const_iterator k = i->begin(), l = i->end(); k != l; ++k) | |
118 | { | |
119 | result += "["; | |
120 | result += *k; | |
121 | result += "]"; | |
122 | } | |
123 | result += "]\n"; | |
124 | } | |
125 | return result; | |
126 | } | |
127 | ||
128 | void add_to_all_sections(const std::string& id, std::string list_name = "performance_all_sections") | |
129 | { | |
130 | std::string::size_type pos = content.find("[template " + list_name + "[]"), end_pos; | |
131 | if(pos == std::string::npos) | |
132 | { | |
133 | // | |
134 | // Just append to the end: | |
135 | // | |
136 | content.append("\n[template ").append(list_name).append("[]\n[").append(id).append("]\n]\n"); | |
137 | } | |
138 | else | |
139 | { | |
140 | // | |
141 | // Read in the all list of sections, add our new one (in alphabetical order), | |
142 | // and then rewrite the whole thing: | |
143 | // | |
144 | static const boost::regex item_e( | |
145 | "\\[" | |
146 | "((?=[^\\]])[^\\[\\]]*+(?0)?+)*+" | |
147 | "\\]|\\]" | |
148 | ); | |
149 | boost::regex_token_iterator<std::string::const_iterator> i(content.begin() + pos + 12 + list_name.size(), content.end(), item_e), j; | |
150 | std::set<std::string> sections; | |
151 | while(i != j) | |
152 | { | |
153 | if(i->length() == 1) | |
154 | { | |
155 | end_pos = i->first - content.begin(); | |
156 | break; | |
157 | } | |
158 | sections.insert(std::string(i->first + 1, i->second - 1)); | |
159 | ++i; | |
160 | } | |
161 | sections.insert(id); | |
162 | std::string new_list = "\n"; | |
163 | for(std::set<std::string>::const_iterator sec = sections.begin(); sec != sections.end(); ++sec) | |
164 | { | |
165 | new_list += "[" + *sec + "]\n"; | |
166 | } | |
167 | content.replace(pos + 12 + list_name.size(), end_pos - pos - 12 - list_name.size(), new_list); | |
168 | } | |
169 | } | |
170 | ||
171 | std::string get_colour(boost::uintmax_t val, boost::uintmax_t best) | |
172 | { | |
173 | if(val <= best * 1.2) | |
174 | return "green"; | |
175 | if(val > best * 4) | |
176 | return "red"; | |
177 | return "blue"; | |
178 | } | |
179 | ||
180 | boost::intmax_t get_value_from_cell(const std::string& cell) | |
181 | { | |
182 | static const boost::regex time_e("(\\d+)ns"); | |
183 | boost::smatch what; | |
184 | if(regex_search(cell, what, time_e)) | |
185 | { | |
186 | return boost::lexical_cast<boost::uintmax_t>(what.str(1)); | |
187 | } | |
188 | return -1; | |
189 | } | |
190 | ||
191 | void add_cell(boost::intmax_t val, const std::string& table_name, const std::string& row_name, const std::string& column_heading) | |
192 | { | |
193 | // | |
194 | // Load the table, add our data, and re-write: | |
195 | // | |
196 | std::string table_id = "table_" + sanitize_string(table_name); | |
197 | boost::regex table_e("\\[table:" + table_id | |
198 | + "\\s(?:[^\\[]|\\\\.)++" | |
199 | "((\\[" | |
200 | "((?:[^\\[\\]]|\\\\.)*+(?2)?+)*+" | |
201 | "\\]\\s*+)*+\\s*+)" | |
202 | "\\]" | |
203 | ); | |
204 | ||
205 | boost::smatch table_location; | |
206 | if(regex_search(content, table_location, table_e)) | |
207 | { | |
208 | std::vector<std::vector<std::string> > table_data; | |
209 | load_table(table_data, table_location[1].first, table_location[1].second); | |
210 | // | |
211 | // Figure out which column we're on: | |
212 | // | |
213 | unsigned column_id = 1001u; | |
214 | for(unsigned i = 0; i < table_data[0].size(); ++i) | |
215 | { | |
216 | if(table_data[0][i] == column_heading) | |
217 | { | |
218 | column_id = i; | |
219 | break; | |
220 | } | |
221 | } | |
222 | if(column_id > 1000) | |
223 | { | |
224 | // | |
225 | // Need a new column, must be adding a new compiler to the table! | |
226 | // | |
227 | table_data[0].push_back(column_heading); | |
228 | for(unsigned i = 1; i < table_data.size(); ++i) | |
229 | table_data[i].push_back(std::string()); | |
230 | column_id = table_data[0].size() - 1; | |
231 | } | |
232 | // | |
233 | // Figure out the row: | |
234 | // | |
235 | unsigned row_id = 1001; | |
236 | for(unsigned i = 1; i < table_data.size(); ++i) | |
237 | { | |
238 | if(table_data[i][0] == row_name) | |
239 | { | |
240 | row_id = i; | |
241 | break; | |
242 | } | |
243 | } | |
244 | if(row_id > 1000) | |
245 | { | |
246 | // | |
247 | // Need a new row, add it now: | |
248 | // | |
249 | table_data.push_back(std::vector<std::string>()); | |
250 | table_data.back().push_back(row_name); | |
251 | for(unsigned i = 1; i < table_data[0].size(); ++i) | |
252 | table_data.back().push_back(std::string()); | |
253 | row_id = table_data.size() - 1; | |
254 | } | |
255 | // | |
256 | // Find the best result in this row: | |
257 | // | |
258 | boost::uintmax_t best = (std::numeric_limits<boost::uintmax_t>::max)(); | |
259 | std::vector<boost::intmax_t> values; | |
260 | for(unsigned i = 1; i < table_data[row_id].size(); ++i) | |
261 | { | |
262 | if(i == column_id) | |
263 | { | |
264 | if(val < best) | |
265 | best = val; | |
266 | values.push_back(val); | |
267 | } | |
268 | else | |
269 | { | |
270 | std::cout << "Existing cell value was " << table_data[row_id][i] << std::endl; | |
271 | boost::uintmax_t cell_val = get_value_from_cell(table_data[row_id][i]); | |
272 | std::cout << "Extracted value: " << cell_val << std::endl; | |
273 | if(cell_val < best) | |
274 | best = cell_val; | |
275 | values.push_back(cell_val); | |
276 | } | |
277 | } | |
278 | // | |
279 | // Update the row: | |
280 | // | |
281 | for(unsigned i = 1; i < table_data[row_id].size(); ++i) | |
282 | { | |
283 | std::string& s = table_data[row_id][i]; | |
284 | s = "[role "; | |
285 | if(values[i - 1] < 0) | |
286 | { | |
287 | s += "grey -]"; | |
288 | } | |
289 | else | |
290 | { | |
291 | s += get_colour(values[i - 1], best); | |
292 | s += " "; | |
293 | s += format_precision(static_cast<double>(values[i - 1]) / best, 2); | |
294 | s += "[br]("; | |
295 | s += boost::lexical_cast<std::string>(values[i - 1]) + "ns)]"; | |
296 | } | |
297 | } | |
298 | // | |
299 | // Convert back to a string and insert into content: | |
300 | std::sort(table_data.begin() + 1, table_data.end(), [](std::vector<std::string> const& a, std::vector<std::string> const& b) { return a[0] < b[0]; } ); | |
301 | std::string c = save_table(table_data); | |
302 | content.replace(table_location.position(1), table_location.length(1), c); | |
303 | } | |
304 | else | |
305 | { | |
306 | // | |
307 | // Create a new table and try again: | |
308 | // | |
309 | std::string new_table = "\n[template " + table_id; | |
310 | new_table += "[]\n[table:" + table_id; | |
311 | new_table += " "; | |
312 | new_table += table_name; | |
313 | new_table += "\n[[Expression[br]Text]["; | |
314 | new_table += column_heading; | |
315 | new_table += "]]\n"; | |
316 | new_table += "[["; | |
317 | new_table += row_name; | |
318 | new_table += "][[role blue 1.00[br]("; | |
319 | new_table += boost::lexical_cast<std::string>(val); | |
320 | new_table += "ns)]]]\n]\n]\n"; | |
321 | ||
322 | std::string::size_type pos = content.find("[/tables:]"); | |
323 | if(pos != std::string::npos) | |
324 | content.insert(pos + 10, new_table); | |
325 | else | |
326 | content += "\n\n[/tables:]\n" + new_table; | |
327 | // | |
328 | // Add a section for this table as well: | |
329 | // | |
b32b8144 | 330 | std::string section_id = "section_" + sanitize_short_string(table_name); |
7c673cae FG |
331 | if(content.find(section_id + "[]") == std::string::npos) |
332 | { | |
333 | std::string new_section = "\n[template " + section_id + "[]\n[section:" + section_id + " " + table_name + "]\n[" + table_id + "]\n[endsect]\n]\n"; | |
334 | pos = content.find("[/sections:]"); | |
335 | if(pos != std::string::npos) | |
336 | content.insert(pos + 12, new_section); | |
337 | else | |
338 | content += "\n\n[/sections:]\n" + new_section; | |
339 | add_to_all_sections(section_id); | |
340 | } | |
341 | // | |
342 | // Add to list of all tables (not in sections): | |
343 | // | |
344 | add_to_all_sections(table_id, "performance_all_tables"); | |
345 | } | |
346 | } | |
347 | ||
348 | void report_execution_time(double t, std::string table, std::string row, std::string heading) | |
349 | { | |
350 | try { | |
351 | add_cell(static_cast<boost::uintmax_t>(t / 1e-9), table, row, heading); | |
352 | } | |
353 | catch(const std::exception& e) | |
354 | { | |
355 | std::cout << "Error in adding cell: " << e.what() << std::endl; | |
356 | throw; | |
357 | } | |
358 | } | |
359 | ||
360 | std::string boost_name() | |
361 | { | |
362 | return "boost " + boost::lexical_cast<std::string>(BOOST_VERSION / 100000) + "." + boost::lexical_cast<std::string>((BOOST_VERSION / 100) % 1000); | |
363 | } | |
364 | ||
365 | std::string compiler_name() | |
366 | { | |
367 | #ifdef COMPILER_NAME | |
368 | return COMPILER_NAME; | |
369 | #else | |
370 | return BOOST_COMPILER; | |
371 | #endif | |
372 | } | |
373 | ||
374 | std::string platform_name() | |
375 | { | |
376 | #ifdef _WIN32 | |
377 | return "Windows x64"; | |
378 | #else | |
379 | return BOOST_PLATFORM; | |
380 | #endif | |
381 | } | |
382 | ||
383 | ||
384 | std::string get_compiler_options_name() | |
385 | { | |
386 | #if defined(BOOST_MSVC) || defined(__ICL) | |
387 | std::string result; | |
388 | #ifdef BOOST_MSVC | |
389 | result = "cl "; | |
390 | #else | |
391 | result = "icl "; | |
392 | #endif | |
393 | #ifdef _M_AMD64 | |
394 | #ifdef __AVX__ | |
395 | result += "/arch:AVX /Ox"; | |
396 | #else | |
397 | result += "/Ox"; | |
398 | #endif | |
399 | result += " (x64 build)"; | |
400 | #else | |
401 | #ifdef _DEBUG | |
402 | result += "/Od"; | |
403 | #elif defined(__AVX2__) | |
404 | result += "/arch:AVX2 /Ox"; | |
405 | #elif defined(__AVX__) | |
406 | result += "/arch:AVX /Ox"; | |
407 | #elif _M_IX86_FP == 2 | |
408 | result += "/arch:sse2 /Ox"; | |
409 | #else | |
410 | result += "/arch:ia32 /Ox"; | |
411 | #endif | |
412 | result += " (x86 build)"; | |
413 | #endif | |
414 | std::cout << "Compiler options are found as: " << result << std::endl; | |
415 | return result; | |
416 | #else | |
417 | return "Unknown"; | |
418 | #endif | |
419 | } | |
420 |