11 #include "hebench/modules/general/include/hebench_math_utils.h"
12 #include "hebench/modules/general/include/hebench_utilities.h"
20 return std::string(s.begin(), s.end());
24 m_main_event(std::numeric_limits<decltype(m_main_event)>::max())
30 if (m_event_headers.count(set_id) <= 0)
31 m_event_types.push_back(set_id);
32 m_event_headers[set_id] = set_header;
33 if (is_main_event || m_main_event == std::numeric_limits<decltype(m_main_event)>::max())
34 m_main_event = set_id;
41 if (m_event_headers.count(p_event->event_type_id) <= 0 || !set_header.empty())
43 m_events.push_back(p_event);
49 m_events.reserve(new_capacity);
100 throw std::ios_base::failure(
"Output stream is in an invalid state.");
103 <<
"Events recorded," << m_events.size() << std::endl
104 <<
"Main event," << m_main_event << std::endl
108 throw std::ios_base::failure(
"Error writing report header to stream.");
109 if (m_events.empty())
112 <<
"Failed" << std::endl;
117 <<
",idx,ID,Event,Description,Time ratio num,Time ratio den,"
118 <<
"Wall time start,Wall time end,Elapsed wall time,"
119 <<
"CPU time start,CPU time end,Elapsed CPU time,Input Samples"
122 throw std::ios_base::failure(
"Error writing table header to stream.");
124 for (std::size_t i = 0; i < m_events.size(); ++i)
132 << hebench::Utilities::convertDoubleToStr(timing_event.
wall_time_start) <<
"," << hebench::Utilities::convertDoubleToStr(timing_event.
wall_time_end) <<
","
134 << hebench::Utilities::convertDoubleToStr(timing_event.
cpu_time_start) <<
"," << hebench::Utilities::convertDoubleToStr(timing_event.
cpu_time_end) <<
","
141 throw std::ios_base::failure(
"Error writing report event to stream.");
151 throw std::ios_base::failure(
"Error writing report event to stream.");
156 std::string_view TimingReportImpl::findNextValueCSV(std::size_t &out_start_idx,
157 std::size_t &out_count,
161 std::string_view s_view(s);
162 out_start_idx = std::min(s_view.find_first_not_of(
" \t\n\r\f\v"), s_view.length());
164 s_view.remove_prefix(out_start_idx);
165 if (!s_view.empty() && s_view.front() !=
',')
170 if (s_view.front() ==
'\"')
173 std::size_t value_end_pos = s_view.find_first_of(
'\"', 1);
174 if (value_end_pos != std::string_view::npos)
176 value_end_pos = std::min(s_view.find_first_of(
',', value_end_pos + 1), s_view.length());
177 out_count = value_end_pos;
181 out_count = std::min(s_view.find_first_of(
','), s_view.length());
184 s_view = std::string_view(s + out_start_idx, out_count);
185 auto last_non_blank_idx = s_view.find_last_not_of(
" \t\n\r\f\v");
186 if (last_non_blank_idx != std::string_view::npos)
187 s_view.remove_suffix(s_view.length() - last_non_blank_idx - 1);
192 void TimingReportImpl::parseHeadingValue(std::string &s_out_heading, std::uint64_t &out_value,
193 const std::string &s_line)
195 std::stringstream ss;
196 std::size_t value_start, value_length;
197 std::string_view s_line_view;
200 s_line_view = std::string_view(s_line.c_str());
203 s_out_heading = findNextValueCSV(value_start, value_length, s_line_view.data());
204 if (value_start + value_length < s_line_view.length())
206 s_line_view.remove_prefix(value_start + value_length);
209 s_value =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
210 ss = std::stringstream(s_value);
211 if (s_value.empty() || !(ss >> out_value))
212 throw std::runtime_error(
"Invalid CSV format. Expected type uint64_t for heading \"" + s_out_heading +
"\", but read \"" + s_value +
"\".");
215 void TimingReportImpl::parseHeadingValue(std::string &s_out_heading,
double &out_value,
216 const std::string &s_line)
218 std::stringstream ss;
219 std::size_t value_start, value_length;
220 std::string_view s_line_view;
223 s_line_view = std::string_view(s_line.c_str());
226 s_out_heading = findNextValueCSV(value_start, value_length, s_line_view.data());
227 if (value_start + value_length < s_line_view.length())
229 s_line_view.remove_prefix(value_start + value_length);
232 s_value =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
233 ss = std::stringstream(s_value);
234 if (s_value.empty() || !(ss >> out_value))
235 throw std::runtime_error(
"Invalid CSV format. Expected type double for heading \"" + s_out_heading +
"\", but read \"" + s_value +
"\".");
238 void TimingReportImpl::parseTimingEvent(std::string &s_out_event_header,
239 std::shared_ptr<TimingReportEventC> &p_out_event,
240 const std::string &s_line)
242 std::stringstream ss;
243 std::size_t value_start, value_length;
244 std::string_view s_line_view;
246 std::shared_ptr<TimingReportEventC> retval;
248 s_out_event_header.clear();
252 std::string event_description;
253 double cpu_start_time, cpu_end_time, wall_start_time, wall_end_time;
254 int64_t time_interval_num, time_interval_den;
255 std::uint64_t input_sample_count;
257 s_line_view = std::string_view(s_line.c_str());
263 std::string_view sv_tmp;
266 sv_tmp = findNextValueCSV(value_start, value_length, s_line_view.data());
269 if (value_start + value_length < s_line_view.length())
271 s_line_view.remove_prefix(value_start + value_length);
273 }
while (sv_tmp.empty());
276 findNextValueCSV(value_start, value_length, s_line_view.data());
277 if (value_start + value_length < s_line_view.length())
279 s_line_view.remove_prefix(value_start + value_length);
282 s_value =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
283 if (value_start + value_length < s_line_view.length())
285 s_line_view.remove_prefix(value_start + value_length);
286 ss = std::stringstream(s_value);
287 if (s_value.empty() || !(ss >>
id))
289 ss = std::stringstream();
290 ss <<
"Invalid timing event format. Expected type uint32_t for ID, but read value \"" + s_value +
"\".";
291 throw std::runtime_error(ss.str());
295 s_out_event_header =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
296 if (value_start + value_length < s_line_view.length())
298 s_line_view.remove_prefix(value_start + value_length);
301 event_description =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
302 if (value_start + value_length < s_line_view.length())
304 s_line_view.remove_prefix(value_start + value_length);
307 s_value =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
308 if (value_start + value_length < s_line_view.length())
310 s_line_view.remove_prefix(value_start + value_length);
311 ss = std::stringstream(s_value);
312 if (s_value.empty() || !(ss >> time_interval_num))
314 ss = std::stringstream();
315 ss <<
"Invalid timing event format. Expected type int64 for Time ratio num, but read value \"" << s_value <<
"\".";
316 throw std::runtime_error(ss.str());
320 s_value =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
321 if (value_start + value_length < s_line_view.length())
323 s_line_view.remove_prefix(value_start + value_length);
324 ss = std::stringstream(s_value);
325 if (s_value.empty() || !(ss >> time_interval_den) || time_interval_den == 0)
327 ss = std::stringstream();
328 ss <<
"Invalid timing event format. Expected non-zero of type int64 for Time ratio den, but read value \"" << s_value <<
"\".";
329 throw std::runtime_error(ss.str());
333 s_value =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
334 if (value_start + value_length < s_line_view.length())
336 s_line_view.remove_prefix(value_start + value_length);
337 ss = std::stringstream(s_value);
338 if (s_value.empty() || !(ss >> wall_start_time))
340 ss = std::stringstream();
341 ss <<
"Invalid timing event format. Expected type double for Wall time start, but read value \"" << s_value <<
"\".";
342 throw std::runtime_error(ss.str());
346 s_value =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
347 if (value_start + value_length < s_line_view.length())
349 s_line_view.remove_prefix(value_start + value_length);
350 ss = std::stringstream(s_value);
351 if (s_value.empty() || !(ss >> wall_end_time))
353 ss = std::stringstream();
354 ss <<
"Invalid timing event format. Expected type double for Wall time end, but read value \"" << s_value <<
"\".";
355 throw std::runtime_error(ss.str());
359 findNextValueCSV(value_start, value_length, s_line_view.data());
360 if (value_start + value_length < s_line_view.length())
362 s_line_view.remove_prefix(value_start + value_length);
365 s_value =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
366 if (value_start + value_length < s_line_view.length())
368 s_line_view.remove_prefix(value_start + value_length);
369 ss = std::stringstream(s_value);
370 if (s_value.empty() || !(ss >> cpu_start_time))
372 ss = std::stringstream();
373 ss <<
"Invalid timing event format. Expected type double for CPU time start, but read value \"" << s_value <<
"\".";
374 throw std::runtime_error(ss.str());
378 s_value =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
379 if (value_start + value_length < s_line_view.length())
381 s_line_view.remove_prefix(value_start + value_length);
382 ss = std::stringstream(s_value);
383 if (s_value.empty() || !(ss >> cpu_end_time))
385 ss = std::stringstream();
386 ss <<
"Invalid timing event format. Expected type double for CPU time end, but read value \"" << s_value <<
"\".";
387 throw std::runtime_error(ss.str());
391 findNextValueCSV(value_start, value_length, s_line_view.data());
392 if (value_start + value_length < s_line_view.length())
394 s_line_view.remove_prefix(value_start + value_length);
397 s_value =
to_string(findNextValueCSV(value_start, value_length, s_line_view.data()));
398 if (value_start + value_length < s_line_view.length())
400 s_line_view.remove_prefix(value_start + value_length);
401 ss = std::stringstream(s_value);
402 if (s_value.empty() || !(ss >> input_sample_count))
404 ss = std::stringstream();
405 ss <<
"Invalid timing event format. Expected type uint64_t for number of Input Samples, but read value \"" << s_value <<
"\".";
406 throw std::runtime_error(ss.str());
409 retval = std::make_shared<TimingReportEventC>();
410 retval->event_type_id = id;
412 retval->cpu_time_start = cpu_start_time;
413 retval->cpu_time_end = cpu_end_time;
414 retval->wall_time_start = wall_start_time;
415 retval->wall_time_end = wall_end_time;
416 retval->input_sample_count = input_sample_count;
417 retval->time_interval_ratio_num = time_interval_num;
418 retval->time_interval_ratio_den = time_interval_den;
422 p_out_event = retval;
425 std::istream &TimingReportImpl::getTrimmedLine(std::istream &is, std::string &s_out,
const std::string &extra_trim)
427 return getTrimmedLine(is, s_out, extra_trim, extra_trim);
430 std::istream &TimingReportImpl::getTrimmedLine(std::istream &is, std::string &s_out,
431 const std::string &extra_ltrim,
const std::string &extra_rtrim)
433 std::getline(is, s_out);
434 std::string s_ltrim(
" \t\n\r\f\v");
435 std::string s_rtrim = s_ltrim;
436 if (!extra_ltrim.empty())
437 s_ltrim.append(extra_ltrim);
438 if (!extra_rtrim.empty())
439 s_rtrim.append(extra_rtrim);
440 s_out.erase(0, s_out.find_first_not_of(s_ltrim));
441 s_out.erase(s_out.find_last_not_of(s_rtrim) + 1);
445 std::string TimingReportImpl::readTextBlock(std::istream &is, std::string &s_block,
446 const std::vector<std::string> tags)
448 constexpr
const char *ch_trim =
" \t\n\r\f\v,";
451 std::stringstream ss;
452 bool b_found =
false;
453 bool b_first_block_line =
true;
455 while (is && !b_found)
457 std::getline(is, s_line);
459 s_tag.erase(0, s_tag.find_first_not_of(ch_trim));
460 s_tag.erase(s_tag.find_last_not_of(ch_trim) + 1);
462 for (std::size_t tag_i = 0; !b_found && tag_i < tags.size(); ++tag_i)
463 b_found = s_tag == tags[tag_i];
466 if (b_first_block_line)
467 b_first_block_line =
false;
475 return b_found ? s_tag : std::string();
480 std::istringstream iss(csv_content);
488 std::uint64_t events_count, u64_main_event;
493 getTrimmedLine(is, s_line,
",");
496 std::stringstream ss;
497 ss <<
"Invalid CSV report version found. Expected \"" <<
TagVersion <<
"\", but read \"" << s_line <<
"\".";
498 throw std::runtime_error(ss.str());
502 getTrimmedLine(is, s_line,
",");
504 parseHeadingValue(heading, events_count, s_line);
507 getTrimmedLine(is, s_line,
",");
508 parseHeadingValue(heading, u64_main_event, s_line);
512 throw std::runtime_error(
"Report header not found in CSV. End of file reached.");
522 getTrimmedLine(is, s_line,
",");
527 getTrimmedLine(is, s_line,
",");
535 getTrimmedLine(is, s_line,
",");
539 std::string s_event_header;
540 std::shared_ptr<TimingReportEventC> p_event;
541 parseTimingEvent(s_event_header, p_event, s_line);
542 retval.
newEvent(p_event, s_event_header);
546 if (retval.
getEvents().size() != events_count)
548 std::stringstream ss;
549 ss <<
"Inconsistent number of events read from CSV. Expected " << events_count <<
", but read " << retval.
getEvents().size() <<
".";
550 throw std::runtime_error(ss.str());
554 throw std::runtime_error(
"Report data not found in CSV. End of file reached.");
557 throw std::runtime_error(
"Report footer not found in CSV. End of file reached.");
596 throw std::invalid_argument(
"Unknown prefix.");
606 prefix.
value = seconds;
609 for (scale_i = 0; scale_i < 4 && prefix.
value > 0.0 && prefix.
value < 1.0; ++scale_i)
611 prefix.
value *= 1000.0;
static constexpr const char * TagReportFooter
void appendHeader(const std::string &header, bool new_line)
void prependHeader(const std::string &header, bool new_line)
std::ostream & convert2CSV(std::ostream &os) const
static void setTimingPrefix(TimingPrefixedSeconds &prefix, double seconds, char ch_prefix)
void setHeader(const std::string &header)
void setFooter(const std::string &footer)
void prependFooter(const std::string &footer, bool new_line)
const std::vector< std::shared_ptr< TimingReportEventC > > & getEvents() const
void appendFooter(const std::string &footer, bool new_line)
static constexpr const char * TagVersion
const std::string & getHeader() const
void newEventType(std::uint32_t set_id, const std::string &set_header, bool is_main_event=false)
static constexpr const char * TagReportEnd
void reserveCapacityForEvents(std::size_t new_capacity)
static constexpr const char * TagReportData
static constexpr const char * TagFailedTest
static constexpr const char * TagReportHeader
static void computeTimingPrefix(TimingPrefixedSeconds &prefix, double seconds)
const std::string & getFooter() const
static TimingReportImpl loadCSV(std::istream &is)
void newEvent(std::shared_ptr< TimingReportEventC > p_event, const std::string &set_header=std::string())
#define MAX_TIME_REPORT_EVENT_DESCRIPTION_SIZE
#define MAX_DESCRIPTION_BUFFER_SIZE
#define MAX_SYMBOL_BUFFER_SIZE
int64_t time_interval_ratio_den
Denominator of timing scale ratio with respect to a unit.
char prefix[MAX_DESCRIPTION_BUFFER_SIZE]
Full name for the prefix (or empty string if no prefix).
uint64_t input_sample_count
std::string to_string(const std::string_view &s)
int64_t time_interval_ratio_num
Scale of time interval used for this event.
char symbol[MAX_SYMBOL_BUFFER_SIZE]
Symbol for the prefix.
double value
Value in the specified unit.
uint32_t event_type_id
ID specifying the event type.
int64_t time_interval_ratio_den
Scale of time interval used for this event.
char description[MAX_TIME_REPORT_EVENT_DESCRIPTION_SIZE]
Description attached to this event.
std::uint64_t copyString(char *dst, std::uint64_t size, const std::string &src)
Copies a C++ string object into a C-style string.