Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Teuchos_XMLPerfTestArchive.cpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Teuchos: Common Tools Package
4 //
5 // Copyright 2004 NTESS and the Teuchos contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #include <iostream>
11 #include <fstream>
12 #include <cstring>
13 #include <cstdlib>
14 #include <Teuchos_XMLObject.hpp>
17 #ifdef _WIN32
18 #include <winsock2.h>
19 #else
20 #include <unistd.h>
21 #endif
22 
23 namespace Teuchos {
24 
26  value = 0;
27  lower = 0;
28  upper = 0;
29  tolerance = 0;
30  use_tolerance = true;
31 }
32 
33 ValueTolerance::ValueTolerance(double val, double tol) {
34  value = val;
35  lower = 0;
36  upper = 0;
37  tolerance = tol;
38  use_tolerance = true;
39 }
40 
41 ValueTolerance::ValueTolerance(double val, double low, double up) {
42  value = val;
43  upper = up;
44  lower = low;
45  tolerance = 0;
46  use_tolerance = false;
47 }
48 
50  from_string(str);
51 }
52 
54  return (value == rhs.value) &&
55  (tolerance == rhs.tolerance) &&
56  (lower == rhs.lower) &&
57  (upper == rhs.upper) &&
59 }
60 
62  std::ostringstream strs;
63  if(use_tolerance)
64  strs << value << " , " << tolerance;
65  else
66  strs << value << " , " << lower << " , " << upper;
67  return strs.str();
68 }
69 
70 void ValueTolerance::from_string(const std::string& valtol_str) {
71  std::string value_str = valtol_str.substr(0,valtol_str.find(","));
72  value = atof(value_str.c_str());
73  std::string tol_str = valtol_str.substr(valtol_str.find(",")+1);
74  if(tol_str.find(",")<=tol_str.length()) {
75  use_tolerance = false;
76  std::string lower_str = tol_str.substr(0,tol_str.find(","));
77  lower = atof(lower_str.c_str());
78  std::string upper_str = tol_str.substr(tol_str.find(",")+1);
79  upper = atof(upper_str.c_str());
80  } else {
81  use_tolerance = true;
82  tolerance = atof(tol_str.c_str());
83  }
84 }
85 
87 
88  XMLTestNode::XMLTestNode(const std::string &tag):XMLObject(tag) {}
89 
91 
93 
94  void XMLTestNode::addDouble (const std::string &name, double val) {
95  addAttribute<double>(name,val);
96  }
97 
98  void XMLTestNode::addInt (const std::string &name, int val) {
99  addAttribute<int>(name,val);
100  }
101 
102  void XMLTestNode::addBool (const std::string &name, bool val) {
103  addAttribute<bool>(name,val);
104  }
105 
106  void XMLTestNode::addValueTolerance(const std::string &name, ValueTolerance val){
107  addAttribute<std::string>(name,val.as_string());
108  }
109 
110  void XMLTestNode::addString (const std::string &name, std::string val) {
111  addAttribute<std::string>(name,val);
112  }
113 
114  bool XMLTestNode::hasChild(const std::string &name) const {
115  bool found = false;
116  for(int i = 0; i < numChildren(); i++) {
117  if(name.compare(XMLObject::getChild(i).getTag()) == 0) {
118  found = true;
119  i = numChildren();
120  }
121  }
122  return found;
123  }
124 
125  void XMLTestNode::appendContentLine(const size_t& i, const std::string &str) {
126  ptr_->appendContentLine(i,str);
127  }
128 
129  XMLTestNode XMLTestNode::getChild(const std::string &name) const {
130  XMLTestNode child;
131  for(int i = 0; i < numChildren(); i++) {
132  if(name.compare(XMLObject::getChild(i).getTag()) == 0)
133  child = XMLObject::getChild(i);
134  }
135  return child;
136  }
137 
138  XMLTestNode XMLTestNode::getChild(const int &i) const {
139  return XMLObject::getChild(i);
140  }
141 
143  return (XMLObject*) this;
144  }
145 
146  bool XMLTestNode::hasSameElements(XMLTestNode const & lhs) const {
147 
148  if((numChildren()!=lhs.numChildren()) ||
149  (numContentLines()!= lhs.numContentLines()) ||
150  (getTag().compare(lhs.getTag())!=0)) return false;
151 
152  for(int i = 0; i<numChildren(); i++) {
153  const XMLTestNode child = XMLObject::getChild(i);
154  if( (!lhs.hasChild(child.getTag())) ||
155  (!child.hasSameElements(lhs.getChild(child.getTag()))) ) return false;
156  }
157 
158  for(int i = 0; i<numContentLines(); i++)
159  if(getContentLine(i).compare(lhs.getContentLine(i))!=0) return false;
160 
161  return true;
162  }
163 
165 
166  // Get CPUName, Number of Sockets, Number of Cores, Number of Hyperthreads
167  std::string cpuname("Undefined");
168  unsigned int threads = 0;
169  unsigned int cores_per_socket = 0;
170  unsigned int highest_socketid = 0;
171 
172  {
173  std::ifstream cpuinfo("/proc/cpuinfo");
174  std::string line;
175  if((cpuinfo.rdstate()&cpuinfo.failbit)) std::cout<<"Failed to open filen\n";
176  while (!cpuinfo.eof() && !(cpuinfo.rdstate()&cpuinfo.failbit)) {
177  getline (cpuinfo,line);
178  if (line.find("model name") < line.size()) {
179  cpuname = line.substr(line.find(":")+2);
180  threads++;
181  }
182  if (line.find("physical id") < line.size()) {
183  unsigned int socketid = atoi(line.substr(line.find(":")+2).c_str());
184  highest_socketid = highest_socketid>socketid?highest_socketid:socketid;
185  }
186  if (line.find("cpu cores") < line.size()) {
187  cores_per_socket = atoi(line.substr(line.find(":")+2).c_str());
188  }
189  }
190  }
191 
192 
193  XMLTestNode machine_config("MachineConfiguration");
194 
195  machine_config.addString("Compiler", TEUCHOS_COMPILER_NAME);
196  machine_config.addInt("Compiler_Version", TEUCHOS_COMPILER_VERSION);
197  machine_config.addString("CPU_Name", cpuname);
198  machine_config.addInt("CPU_Sockets", highest_socketid+1);
199  machine_config.addInt("CPU_Cores_Per_Socket", cores_per_socket);
200  machine_config.addInt("CPU_Total_HyperThreads", threads);
201  return machine_config;
202 }
203 
206  XMLTestNode new_test,
207  const std::string filename,
208  const std::string ext_hostname)
209 {
210  XMLTestNode database;
211  PerfTestResult return_value = PerfTestPassed;
212  bool is_new_config = true;
213 
214  // Open Database File
215  //
216  // FIXME (mfh 09 Apr 2014) This actually opens the file twice.
217  if (std::ifstream (filename.c_str ())) {
218  database = FileInputSource (filename).getObject ();
219  }
220 
221  // Get Current Hostname
222  char hostname[256];
223  memset (hostname, 0, 256);
224  if (ext_hostname.empty ()) {
225  gethostname (hostname, 255);
226  } else {
227  strncat (hostname, ext_hostname.c_str (), 255);
228  }
229 
230  XMLTestNode new_test_entry = new_test.getChild ("TestEntry");
231 
232  if (database.isEmpty ()) {
233  database = XMLTestNode ("PerfTests");
234  }
235  // Does hostname exist?
236  if (database.hasChild (hostname)) {
237  XMLTestNode machine = database.getChild (hostname);
238 
239  // Find matching machine configuration
240  for (int i = 0; i < machine.numChildren (); ++i) {
241  XMLTestNode configuration = machine.getChild (i);
243  configuration.getTag ().compare ("Configuration") != 0,
244  std::runtime_error, "Unexpected Tag \"" << configuration.getTag ()
245  << "\"; only children with Tag = \"Configuration\" are allowed in a "
246  "MachineEntry.");
247 
249  ! configuration.hasChild ("MachineConfiguration") ||
250  ! configuration.hasChild ("Tests"),
251  std::runtime_error,
252  "A Configuration needs to have a child \"MachineConfiguration\" and a "
253  "child \"Tests\".");
254 
255  XMLTestNode machine_configuration = configuration.getChild ("MachineConfiguration");
256  XMLTestNode old_tests = configuration.getChild ("Tests");
257 
258  if (machine_configuration.hasSameElements (machine_config)) {
259  is_new_config = false;
260 
261  // Find existing test with same tag as the new test
262  if (old_tests.hasChild (new_test.getTag ())) {
263 
264  XMLTestNode old_test = old_tests.getChild (new_test.getTag ());
265 
266  int new_test_config = -1;
267  for (int k = 0; k < old_test.numChildren (); ++k) {
268  XMLTestNode old_test_entry = old_test.getChild (k);
269 
271  ! old_test_entry.hasChild ("TestConfiguration") ||
272  ! new_test_entry.hasChild ("TestResults"),
273  std::runtime_error, "A TestEntry needs to have a child "
274  "\"TestConfiguration\" and a child \"TestResults\".");
275 
276  if (old_test_entry.getChild ("TestConfiguration").hasSameElements (new_test_entry.getChild ("TestConfiguration"))) {
277  new_test_config = k;
278  }
279  }
280 
281  if (new_test_config < 0) {
282  old_test.addChild (new_test_entry);
283  return_value = PerfTestNewTestConfiguration;
284  } else {
285  bool deviation = false;
286  XMLTestNode old_test_entry = old_test.getChild (new_test_config);
287  XMLTestNode old_results = old_test_entry.getChild ("TestResults");
288  XMLTestNode new_results = new_test_entry.getChild ("TestResults");
289 
290  // Compare all entries
291  for (int old_r = 0; old_r < old_results.numChildren (); ++old_r) {
292  XMLTestNode result_entry = old_results.getChild (old_r);
293 
294  // Finding entry with same name
295  bool exists = new_results.hasChild (result_entry.getTag ());
296 
297  if (exists) {
298  std::string oldv_str = result_entry.getContentLine (0);
299 
300  // If it is a time or result compare numeric values with tolerance
301  if((result_entry.getTag().find("Time")==0) || (result_entry.getTag().find("Result")==0)) {
302  ValueTolerance old_valtol(oldv_str);
303  ValueTolerance new_valtol(new_results.getChild(result_entry.getTag()).getContentLine(0));
304 
305  if(old_valtol.use_tolerance) {
306  double diff = old_valtol.value - new_valtol.value;
307  diff*=diff;
308 
309  double normalization = old_valtol.value;
310  normalization*=normalization;
311 
312  if(normalization==0?diff>0:diff/normalization>old_valtol.tolerance*old_valtol.tolerance) {
313  deviation = true;
314  std::cout << std::endl
315  << "DeviationA in Test: \"" << old_test.getTag()
316  << "\" for entry \"" << result_entry.getTag() << "\"" << std::endl;
317  std::cout << " Existing Value: \"" << oldv_str << "\"" << std::endl;
318  std::cout << " New Value: \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
319  }
320  } else {
321  if( (old_valtol.lower>new_valtol.value) || (old_valtol.upper<new_valtol.value)) {
322  deviation = true;
323  std::cout << std::endl
324  << "DeviationB in Test: \"" << old_test.getTag()
325  << "\" for entry \"" << result_entry.getTag() << "\"" << std::endl;
326  std::cout << " Existing Value: \"" << oldv_str << "\"" << std::endl;
327  std::cout << " New Value: \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
328  }
329  }
330  } else {
331  // Compare exact match for every other type of entry
332  if(oldv_str.compare(new_results.getChild(result_entry.getTag()).getContentLine(0))!=0) {
333  deviation = true;
334  std::cout << std::endl
335  << "DeviationC in Test: \"" << old_test.getTag()
336  << "\" for entry \"" << result_entry.getTag() << "\"" << std::endl;
337  std::cout << " Existing Value: \"" << oldv_str << "\"" << std::endl;
338  std::cout << " New Value: \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
339  }
340  }
341  }
342  // An old value was not given in the new test: this is an error;
343  if(!exists) {
344  std::cout << "Error New test has same name as an existing one, but one of the old entries is missing." << std::endl;
345  deviation = true;
346  }
347  }
348 
349  if(deviation) { return_value = PerfTestFailed; }
350  else {
351  // Did someone add new values to the test?
352  if(new_results.numChildren()!=old_results.numChildren()) {
353  for(int new_r = 0; new_r < new_results.numChildren() ; new_r++) {
354  if(!old_results.hasChild(new_results.getChild(new_r).getTag())) {
355  old_results.addChild(new_results.getChild(new_r));
356  }
357  }
358 
359  return_value = PerfTestUpdatedTest;
360  }
361  }
362  }
363  } else { // End Test Exists
364  // Add new test if no match was found
365  old_tests.addChild(new_test);
366  return_value = PerfTestNewTest;
367  }
368  } // End MachineConfiguration Exists
369  } // End loop over MachineConfigurations
370 
371  // Did not find matching MachineConfiguration
372  if(is_new_config) {
373  XMLTestNode config("Configuration");
374  config.addChild(machine_config);
375  XMLTestNode tests("Tests");
376  tests.addChild(new_test);
377 
378  config.addChild(tests);
379  machine.addChild(config);
380 
381  return_value = PerfTestNewConfiguration;
382  }
383  } else { // Machine Entry does not exist
384  XMLTestNode machine(hostname);
385 
386  XMLTestNode config("Configuration");
387  config.addChild(machine_config);
388  XMLTestNode tests("Tests");
389  tests.addChild(new_test);
390  config.addChild(tests);
391 
392  machine.addChild(config);
393 
394  database.addChild(machine);
395 
396  return_value = PerfTestNewMachine;
397  }
398 
399 
400  if(return_value>PerfTestPassed) {
401  std::ofstream fout(filename.c_str());
402  fout << database << std::endl;
403  }
404 
405  return return_value;
406 }
407 }
const std::string & getTag() const
Return the tag of the current node.
XMLTestNode getChild(const std::string &name) const
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
void addChild(const XMLObject &child)
Add a child node to the node.
void addDouble(const std::string &name, double val)
bool hasChild(const std::string &name) const
bool isEmpty() const
Find out if a node is empty.
ValueTolerance is a struct to keep a tuple of value and a tolerance. The tolerance can be either expr...
void addValueTolerance(const std::string &name, ValueTolerance val)
Tools for an XML-based performance test archive.
void addInt(const std::string &name, int val)
RCP< XMLObjectImplem > ptr_
Representation of an XML data tree. XMLObject is a ref-counted handle to a XMLObjectImplem object...
XMLObject getObject() const
Get an object by invoking the TreeBuildingXMLHandler on the input data.
void appendContentLine(const size_t &i, const std::string &str)
#define TEUCHOS_COMPILER_NAME
Instantiation of XMLInputSource class for reading XML from a file.
void from_string(const std::string &valtol_str)
PerfTestResult
ReturnValues for PerfTest_CheckOrAdd_Test.
int numChildren() const
Return the number of child nodes owned by this node.
void addBool(const std::string &name, bool val)
Subclass of XMLObject used by the performance archive.
void addString(const std::string &name, std::string val)
int numContentLines() const
Return the number of lines of character content stored in this node.
bool hasSameElements(XMLTestNode const &lhs) const
The XMLObjectImplem class takes care of the low-level implementation details of XMLObject.
Definition of XMLInputSource derived class for reading XML from a file.
bool operator==(ValueTolerance &rhs)
const XMLObject & getChild(int i) const
Return the i-th child node.
const std::string & getContentLine(int i) const
Return the i-th line of character content stored in this node.
const XMLObject * xml_object() const
#define TEUCHOS_COMPILER_VERSION
PerfTestResult PerfTest_CheckOrAdd_Test(XMLTestNode machine_config, XMLTestNode new_test, const std::string filename, const std::string ext_hostname)
Check whether a test is present and match an existing test in an archive.
XMLTestNode PerfTest_MachineConfig()
PerfTest_MachineConfig generates a basic machine configuration XMLTestNode.
An object representation of a subset of XML data.