NCBI C++ ToolKit
perf_view.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: perf_view.cpp 96019 2022-01-27 20:44:04Z vakatov $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors: Denis Vakatov
27  *
28  * File Description:
29  * Compose performance summary of testsuite results ('check.sh.log' files
30  * named as 'perf_view.EXT' where EXT is the name of a run).
31  *
32  */
33 
34 #include <ncbi_pch.hpp>
35 #include <corelib/ncbiapp.hpp>
36 #include <corelib/ncbienv.hpp>
37 #include <corelib/ncbiargs.hpp>
38 #include <util/xregexp/regexp.hpp>
39 
41 
42 
43 /////////////////////////////////////////////////////////////////////////////
44 // CPsgPerfApplication::
45 
47 {
48  typedef string TTime; // Test timing
49  typedef string TType; // "OSG", "PSG" or "PSOS" (or "UNK")
50  typedef string TName; // "1_acc/0/S,BD"
51  typedef string TSetup; // Extension of the run file 'perf_view.EXT'
52 
55  void ProcessFile(const string& file_ext);
56  void ProcessLine(const char* line, const string& file_ext);
57  void PrintResult(bool print_unk);
58 
61 
62  string ColorMe(const string& value, const TSetup& color,
63  bool is_header = false);
64 
66  struct SNameAttr {
69  };
71 
72 private:
73  virtual void Init(void);
74  virtual int Run(void);
75 };
76 
77 
78 /////////////////////////////////////////////////////////////////////////////
79 // Init test for all different types of arguments
80 
82 {
83  // Create command-line argument descriptions class
84  unique_ptr<CArgDescriptions> arg_desc(new CArgDescriptions);
85 
86  // Specify USAGE context
87  arg_desc->SetUsageContext(GetArguments().GetProgramBasename(),
88  "Compose summary of 'check.sh.log' files");
89 
90  // Describe the expected command-line arguments
91 
92  // Whether to use JIRA colors
93  arg_desc->AddFlag
94  ("nocolor", "Do not use JIRA colors");
95 
96  // Set of file extensions (perf_view.EXT) to process
97  arg_desc->AddExtra
98  (1, // one mandatory extra args
99  8, // up to 8 extra args
100  "Files to process: list of extensions (as in 'perf_view.EXT')",
102 
103  // Setup arg.descriptions for this application
104  SetupArgDescriptions(arg_desc.release());
105 }
106 
107 
108 /////////////////////////////////////////////////////////////////////////////
109 // Run test (printout arguments obtained from command-line)
110 
112 {
113  // Get arguments
114  const CArgs& args = GetArgs();
115 
116  // Printout obtained argument values
117  string str;
118  //cout << args.Print(str) << endl;
119 
120  for (size_t extra = 1; extra <= args.GetNExtra(); extra++) {
121  ProcessFile(args[extra].AsString());
122  }
123 
124  PrintResult(false);
125  cout << endl;
126  PrintResult(true);
127 
128  return 0;
129 }
130 
131 void CPsgPerfApplication::ProcessFile(const string& file_ext)
132 {
133  ifstream file("perf_view." + file_ext);
134  if ( !file.good() )
135  throw runtime_error("Error opening file perf_view." + file_ext);
136 
137  while ( file.good() ) {
138  char line[1024];
139 
140  file.getline(line, sizeof(line));
141  //cout << line << endl;
142  ProcessLine(line, file_ext);
143  }
144 
145  if ( !file.eof() )
146  throw runtime_error("Error reading file perf_view." + file_ext);
147 }
148 
149 
150 void CPsgPerfApplication::ProcessLine(const char* line, const string& file_ext)
151 {
152  if ( NStr::IsBlank(line) )
153  return;
154 
155  CRegexpUtil re;
156  TType loader;
157  TName name;
158 
159  if ( CRegexpUtil(line).Extract("objmgr_perf_test").empty() ) {
160  loader = "UNK";
161 
162  // TEST NAME
163  re.Reset(line);
164  re.Reset( re.Extract("-- .*") );
165  name = re.Extract("] [^(]*");
166  name.erase(0, 1);
168  //NStr::ReplaceInPlace(name, "-", "\\-");
169  //NStr::ReplaceInPlace(name, "+", "\\+");
170  //NStr::ReplaceInPlace(name, "_", "\\_");
171 
172  //cout << "NAME: '" << name << "'" << endl;
173  }
174  else { // Important special case here: OBJMGR_PERF_TEST
175  // IDS
176  re.Reset(line);
177  re.Reset( re.Extract("-ids *[^ ]*") );
178  string ids = re.Extract("[^ ]*$");
179  //cout << "IDS: '" << ids << "'" << endl;
180 
181  // THREADS
182  re.Reset(line);
183  re.Reset( re.Extract("-threads *[^ ]*") );
184  string threads = re.Extract("[^ ]*$");
185  if ( threads.empty() )
186  threads = "-";
187  //cout << "THREADS: '" << threads << "'" << endl;
188 
189  // LOADER
190  re.Reset(line);
191  re.Reset( re.Extract("-loader *[^ ]*") );
192  loader = re.Extract("[^ ]*$");
193  if (loader == "gb") {
194  if ( CRegexpUtil(line).Extract("-pubseqos").empty() )
195  loader = "OSG";
196  else
197  loader = "PSOS";
198  } else if (loader == "psg") {
199  loader = "PSG";
200  } else {
201  loader = "UNK";
202  }
203  //cout << "LOADER: '" << loader << "'" << endl;
204 
205  // SPLIT
206  string split;
207  if ( CRegexpUtil(line).Extract("-no_split").empty() )
208  split = "S";
209  else
210  split = "N";
211  //cout << "SPLIT: '" << split << "'" << endl;
212 
213  // BULK
214  re.Reset(line);
215  re.Reset( re.Extract("-bulk *[^ ]*") );
216  string bulk = re.Extract("[^ ]*$");
217  if (bulk == "bioseq")
218  bulk = "BB";
219  else if (bulk == "data")
220  bulk = "BD";
221  //cout << "BULK: '" << bulk << "'" << endl;
222 
223  // TEST NAME
224  name = ids + "/" + threads + "/" + split;
225  if ( !bulk.empty() )
226  name = name + "," + bulk;
227  }
228  m_Names.insert(name);
229 
230  if (loader == "UNK")
231  m_NameAttr[name].has_unknown = true;
232  else
233  m_NameAttr[name].has_special = true;
234 
235  // TIME (or status, if not OK)
236  string stime;
237  if ( CRegexpUtil(line).Extract("^OK").empty() ) {
238  stime = NStr::TruncateSpaces_Unsafe(string(line).substr(0,3));
239  }
240  else {
241  re.Reset(line);
242  re.Reset( re.Extract("real *[0-9]\\.*[0-9]*") );
243  string xtime = re.Extract("[^ ]*$");
244  //cout << "TIME: '" << time << "'" << endl;
245  double dtime = NStr::StringToDouble(xtime, NStr::fConvErr_NoThrow);
246  ostringstream os;
247  os << setprecision(dtime < 100.0 ? 2 : 3) << dtime;
248  stime = os.str();
249  //cout << "XTIME: '" << xtime << "'" << endl;
250  }
251 
252  m_Results[file_ext][loader][name] = stime;
253 }
254 
255 
256 string CPsgPerfApplication::ColorMe(const string& value, const TSetup& color,
257  bool is_header)
258 {
259  if ( GetArgs()["nocolor"] )
260  return value;
261 
262  static map<TSetup, string> m_Colors;
263 
264  if ( m_Colors[color].empty() ) {
265  static size_t color_counter = 0;
266  static const vector<string> kColors =
267  { "black", "red", "blue", "green",
268  "orange", "pink", "brown", "purple" };
269 
270  m_Colors[color] = kColors[color_counter++ % kColors.size()];
271  }
272 
273  if (value == "abs")
274  return "_abs_";
275 
276  string x_value = value;
277  if (!is_header
278  && x_value.find_first_not_of("0123456789. *") != string::npos)
279  x_value = "*" + x_value + "*";
280 
281  return "{color:" + m_Colors[color] + "}" + x_value + "{color}";
282 }
283 
284 
286 {
287  // Legend
288  if ( !print_unk ) {
289  if ( GetArgs()["nocolor"] ) {
290  cout << R"delimiter(
291 CXYZ = client "C" against server + "PSG-X.Y.Z"
292 where "C" is:
293 * 'T' - contemporary TRUNK
294 * 'S' - latest SC in SVN
295 * 'P' - contemporary full production build
296 * 'O' - previous full production build
297 )delimiter";
298  }
299  else {
300  cout << R"delimiter(
301 {{{color:blue}*C*{color}{color:red}*XYZ*{color}}} = client {color:blue}{{*C*}}{color} against server + {{PSG-{color:red}*X.Y.Z*{color}}}, _where_ {color:blue}{{*C*}}{color}:
302 * '{color:blue}{{T}}{color}' - contemporary TRUNK
303 * '{color:blue}{{S}}{color}' - latest SC in SVN
304 * '{color:blue}{{P}}{color}' - contemporary full production build
305 * '{color:blue}{{O}}{color}' - previous full production build
306 )delimiter";
307  }
308 
309  cout << R"delimiter(
310 
311 For the special case of OBJMGR_PERF_TEST the test name is decomposed and then
312 converted into a shorter form "IDs/Threads/Parameters" -- where:
313 * *IDs*: file of seq-ids
314 * *Thr*: number of threads (if specified)
315 * *Par*: N = No-Split, S = Split; BD = Bulk-Data, BB = Bulk-Bioseq
316 
317 Minimum time/s for each run are highlighted in *bold*.
318 
319 Average time for each run is shown as "= _*avg*_".
320 
321 )delimiter";
322  }
323 
324  vector<TType> loaders = { "PSG", "OSG", "PSOS", "UNK" };
325 
326  // Header
327  if ( print_unk )
328  cout << " || Name || ";
329  else
330  cout << " || IDs/Thr/Par || ";
331 
332  size_t loader_idx = 0;
333  for (const string& loader : loaders) {
334  if ( ( print_unk && loader != "UNK")
335  || (!print_unk && loader == "UNK"))
336  continue;
337 
338  bool loader_empty = true;
339  for (const auto& x_run : m_Results) {
340  for (const auto& x_loader : x_run.second) {
341  if (x_loader.first != loader || x_loader.second.empty())
342  continue;
343  loader_empty = false;
344  break;
345  }
346  if ( !loader_empty )
347  break;
348  }
349  if ( loader_empty ) {
350  loaders[loader_idx].erase();
351  loader_idx++;
352  continue;
353  }
354  loader_idx++;
355 
356  if ( !print_unk )
357  cout << "*_" << loader << "_*\n";
358  for (const auto& run : m_Results) {
359  cout << ColorMe(run.first, run.first, true) << " ";
360  }
361  cout << " || ";
362  }
363  cout << endl;
364 
365  // Values
366  for (const TName& name : m_Names) {
367  if ( ( print_unk && !m_NameAttr[name].has_unknown)
368  || (!print_unk && !m_NameAttr[name].has_special) )
369  continue;
370 
371  cout << " | " << name << " | ";
372  for (const string& loader : loaders) {
373  if ( ( print_unk && loader != "UNK")
374  || (!print_unk && loader == "UNK"))
375  continue;
376 
377  if ( loader.empty() )
378  continue;
379 
380  // Calk min and average time values
381  string min_stime = " ";
382  double min_dtime = numeric_limits<double>::max();
383  unsigned n_time = 0;
384  double avg_time = {};
385  for (const auto& run : m_Results) {
386  double dtime = NStr::StringToDouble
387  (m_Results[run.first][loader][name],
389  if ( dtime ) {
390  if (dtime < min_dtime) {
391  min_dtime = dtime;
392  min_stime = m_Results[run.first][loader][name];
393  }
394  n_time++;
395  avg_time += dtime;
396  }
397  }
398 
399  // Print time values
400  ostringstream ostr;
401  bool has_non_abs_values = false;
402  bool first = true;
403  for (const auto& run : m_Results) {
404  if (!first)
405  ostr << " - ";
406  first = false;
407  string& time = m_Results[run.first][loader][name];
408  if ( time.empty() )
409  time = "abs";
410  else {
411  has_non_abs_values = true;
412  if (n_time > 1 && time == min_stime)
413  time = "*" + time + "*";
414  }
415  ostr << ColorMe(time, run.first);
416  }
417  if ( has_non_abs_values ) {
418  cout << ostr.str();
419  if (n_time > 1 && avg_time) {
420  avg_time /= n_time;
421  ostr.str("");
422  ostr << setprecision(avg_time < 100.0 ? 2 : 3) << avg_time;
423  cout << " = _*" << ColorMe(ostr.str(), "AVERAGE") << "*_";
424  }
425  } else
426  cout << "_abs_";
427  cout << " | ";
428  }
429  cout << endl;
430  }
431 }
432 
433 
434 /////////////////////////////////////////////////////////////////////////////
435 // MAIN
436 
437 int NcbiSys_main(int argc, ncbi::TXChar* argv[])
438 {
439  return CPsgPerfApplication().AppMain(argc, argv);
440 }
CArgDescriptions –.
Definition: ncbiargs.hpp:541
CArgs –.
Definition: ncbiargs.hpp:379
void ProcessFile(const string &file_ext)
Definition: perf_view.cpp:131
map< TName, TTime > TNameTime
Definition: perf_view.cpp:53
virtual void Init(void)
Initialize the application.
Definition: perf_view.cpp:81
void PrintResult(bool print_unk)
Definition: perf_view.cpp:285
map< TName, SNameAttr > m_NameAttr
Definition: perf_view.cpp:70
virtual int Run(void)
Run the application.
Definition: perf_view.cpp:111
void ProcessLine(const char *line, const string &file_ext)
Definition: perf_view.cpp:150
set< TName > m_Names
Definition: perf_view.cpp:65
string ColorMe(const string &value, const TSetup &color, bool is_header=false)
Definition: perf_view.cpp:256
map< TSetup, TRun > TResults
Definition: perf_view.cpp:59
map< TType, TNameTime > TRun
Definition: perf_view.cpp:54
CRegexpUtil –.
Definition: regexp.hpp:312
Definition: map.hpp:338
iterator_bool insert(const value_type &val)
Definition: set.hpp:149
char value[7]
Definition: config.c:431
static DLIST_TYPE *DLIST_NAME() first(DLIST_LIST_TYPE *list)
Definition: dlist.tmpl.h:46
virtual const CArgs & GetArgs(void) const
Get parsed command line arguments.
Definition: ncbiapp.cpp:285
int AppMain(int argc, const char *const *argv, const char *const *envp=0, EAppDiagStream diag=eDS_Default, const char *conf=NcbiEmptyCStr, const string &name=NcbiEmptyString)
Main function (entry point) for the NCBI application.
Definition: ncbiapp.cpp:799
virtual void SetupArgDescriptions(CArgDescriptions *arg_desc)
Setup the command line argument descriptions.
Definition: ncbiapp.cpp:1175
const CNcbiArguments & GetArguments(void) const
Get the application's cached unprocessed command-line arguments.
size_t GetNExtra(void) const
Get the number of unnamed positional (a.k.a. extra) args.
Definition: ncbiargs.hpp:422
@ eString
An arbitrary string.
Definition: ncbiargs.hpp:589
string Extract(CTempStringEx pattern, CRegexp::TCompile compile_flags=CRegexp::fCompile_default, CRegexp::TMatch match_flags=CRegexp::fMatch_default, size_t pattern_idx=0)
Get matching pattern/subpattern from string.
Definition: regexp.hpp:631
void Reset(CTempString str)
Reset the content of the string to process.
Definition: regexp.hpp:591
static CTempString TruncateSpaces_Unsafe(const CTempString str, ETrunc where=eTrunc_Both)
Truncate spaces in a string.
Definition: ncbistr.cpp:3187
char TXChar
Definition: ncbistr.hpp:172
static bool IsBlank(const CTempString str, SIZE_TYPE pos=0)
Check if a string is blank (has no text).
Definition: ncbistr.cpp:106
static double StringToDouble(const CTempStringEx str, TStringToNumFlags flags=0)
Convert string to double.
Definition: ncbistr.cpp:1387
static void TruncateSpacesInPlace(string &str, ETrunc where=eTrunc_Both)
Truncate spaces in a string (in-place)
Definition: ncbistr.cpp:3197
@ fConvErr_NoThrow
Do not throw an exception on error.
Definition: ncbistr.hpp:285
n background color
FILE * file
constexpr bool empty(list< Ts... >) noexcept
Defines the CNcbiApplication and CAppException classes for creating NCBI applications.
Defines command line argument related classes.
Defines unified interface to application:
T max(T x_, T y_)
void split(std::vector< std::string > *strVec, const std::string &str_, const std::string &split_)
int NcbiSys_main(int argc, ncbi::TXChar *argv[])
Definition: perf_view.cpp:412
USING_NCBI_SCOPE
Definition: perf_view.cpp:40
static const char * str(char *buf, int n)
Definition: stats.c:84
C++ wrappers for the Perl-compatible regular expression (PCRE) library.
Modified on Sat Dec 09 04:47:07 2023 by modify_doxy.py rev. 669887