Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
Teuchos_XMLPerfTestArchive.cpp
Go to the documentation of this file.
1 // @HEADER
2 // ***********************************************************************
3 //
4 // Teuchos: Common Tools Package
5 // Copyright (2004) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
38 //
39 // ***********************************************************************
40 // @HEADER
41 
42 
43 #include <iostream>
44 #include <fstream>
45 #include <cstring>
46 #include <cstdlib>
47 #include <Teuchos_XMLObject.hpp>
50 #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
51 #include <Winsock2.h>
52 #pragma comment(lib, "ws2_32.lib")
53 #else
54 #include <unistd.h>
55 #endif
56 
57 namespace Teuchos {
58 
60  value = 0;
61  lower = 0;
62  upper = 0;
63  tolerance = 0;
64  use_tolerance = true;
65 }
66 
67 ValueTolerance::ValueTolerance(double val, double tol) {
68  value = val;
69  lower = 0;
70  upper = 0;
71  tolerance = tol;
72  use_tolerance = true;
73 }
74 
75 ValueTolerance::ValueTolerance(double val, double low, double up) {
76  value = val;
77  upper = up;
78  lower = low;
79  tolerance = 0;
80  use_tolerance = false;
81 }
82 
84  from_string(str);
85 }
86 
88  return (value == rhs.value) &&
89  (tolerance == rhs.tolerance) &&
90  (lower == rhs.lower) &&
91  (upper == rhs.upper) &&
93 }
94 
96  std::ostringstream strs;
97  if(use_tolerance)
98  strs << value << " , " << tolerance;
99  else
100  strs << value << " , " << lower << " , " << upper;
101  return strs.str();
102 }
103 
104 void ValueTolerance::from_string(const std::string& valtol_str) {
105  std::string value_str = valtol_str.substr(0,valtol_str.find(","));
106  value = atof(value_str.c_str());
107  std::string tol_str = valtol_str.substr(valtol_str.find(",")+1);
108  if(tol_str.find(",")<=tol_str.length()) {
109  use_tolerance = false;
110  std::string lower_str = tol_str.substr(0,tol_str.find(","));
111  lower = atof(lower_str.c_str());
112  std::string upper_str = tol_str.substr(tol_str.find(",")+1);
113  upper = atof(upper_str.c_str());
114  } else {
115  use_tolerance = true;
116  tolerance = atof(tol_str.c_str());
117  }
118 }
119 
121 
122  XMLTestNode::XMLTestNode(const std::string &tag):XMLObject(tag) {}
123 
125 
127 
128  void XMLTestNode::addDouble (const std::string &name, double val) {
129  addAttribute<double>(name,val);
130  }
131 
132  void XMLTestNode::addInt (const std::string &name, int val) {
133  addAttribute<int>(name,val);
134  }
135 
136  void XMLTestNode::addBool (const std::string &name, bool val) {
137  addAttribute<bool>(name,val);
138  }
139 
140  void XMLTestNode::addValueTolerance(const std::string &name, ValueTolerance val){
141  addAttribute<std::string>(name,val.as_string());
142  }
143 
144  void XMLTestNode::addString (const std::string &name, std::string val) {
145  addAttribute<std::string>(name,val);
146  }
147 
148  bool XMLTestNode::hasChild(const std::string &name) const {
149  bool found = false;
150  for(int i = 0; i < numChildren(); i++) {
151  if(name.compare(XMLObject::getChild(i).getTag()) == 0) {
152  found = true;
153  i = numChildren();
154  }
155  }
156  return found;
157  }
158 
159  void XMLTestNode::appendContentLine(const size_t& i, const std::string &str) {
160  ptr_->appendContentLine(i,str);
161  }
162 
163  XMLTestNode XMLTestNode::getChild(const std::string &name) const {
164  XMLTestNode child;
165  for(int i = 0; i < numChildren(); i++) {
166  if(name.compare(XMLObject::getChild(i).getTag()) == 0)
167  child = XMLObject::getChild(i);
168  }
169  return child;
170  }
171 
172  XMLTestNode XMLTestNode::getChild(const int &i) const {
173  return XMLObject::getChild(i);
174  }
175 
177  return (XMLObject*) this;
178  }
179 
180  bool XMLTestNode::hasSameElements(XMLTestNode const & lhs) const {
181 
182  if((numChildren()!=lhs.numChildren()) ||
183  (numContentLines()!= lhs.numContentLines()) ||
184  (getTag().compare(lhs.getTag())!=0)) return false;
185 
186  for(int i = 0; i<numChildren(); i++) {
187  const XMLTestNode child = XMLObject::getChild(i);
188  if( (!lhs.hasChild(child.getTag())) ||
189  (!child.hasSameElements(lhs.getChild(child.getTag()))) ) return false;
190  }
191 
192  for(int i = 0; i<numContentLines(); i++)
193  if(getContentLine(i).compare(lhs.getContentLine(i))!=0) return false;
194 
195  return true;
196  }
197 
199 
200  // Get CPUName, Number of Sockets, Number of Cores, Number of Hyperthreads
201  std::string cpuname("Undefined");
202  unsigned int threads = 0;
203  unsigned int cores_per_socket = 0;
204  unsigned int highest_socketid = 0;
205 
206  {
207  std::ifstream cpuinfo("/proc/cpuinfo");
208  std::string line;
209  if((cpuinfo.rdstate()&cpuinfo.failbit)) std::cout<<"Failed to open filen\n";
210  while (!cpuinfo.eof() && !(cpuinfo.rdstate()&cpuinfo.failbit)) {
211  getline (cpuinfo,line);
212  if (line.find("model name") < line.size()) {
213  cpuname = line.substr(line.find(":")+2);
214  threads++;
215  }
216  if (line.find("physical id") < line.size()) {
217  unsigned int socketid = atoi(line.substr(line.find(":")+2).c_str());
218  highest_socketid = highest_socketid>socketid?highest_socketid:socketid;
219  }
220  if (line.find("cpu cores") < line.size()) {
221  cores_per_socket = atoi(line.substr(line.find(":")+2).c_str());
222  }
223  }
224  }
225 
226 
227  XMLTestNode machine_config("MachineConfiguration");
228 
229  machine_config.addString("Compiler", TEUCHOS_COMPILER_NAME);
230  machine_config.addInt("Compiler_Version", TEUCHOS_COMPILER_VERSION);
231  machine_config.addString("CPU_Name", cpuname);
232  machine_config.addInt("CPU_Sockets", highest_socketid+1);
233  machine_config.addInt("CPU_Cores_Per_Socket", cores_per_socket);
234  machine_config.addInt("CPU_Total_HyperThreads", threads);
235  return machine_config;
236 }
237 
240  XMLTestNode new_test,
241  const std::string filename,
242  const std::string ext_hostname)
243 {
244  XMLTestNode database;
245  PerfTestResult return_value = PerfTestPassed;
246  bool is_new_config = true;
247 
248  // Open Database File
249  //
250  // FIXME (mfh 09 Apr 2014) This actually opens the file twice.
251  if (std::ifstream (filename.c_str ())) {
252  database = FileInputSource (filename).getObject ();
253  }
254 
255  // Get Current Hostname
256  char hostname[256];
257  memset (hostname, 0, 256);
258  if (ext_hostname.empty ()) {
259  gethostname (hostname, 255);
260  } else {
261  strncat (hostname, ext_hostname.c_str (), 255);
262  }
263 
264  XMLTestNode new_test_entry = new_test.getChild ("TestEntry");
265 
266  if (database.isEmpty ()) {
267  database = XMLTestNode ("PerfTests");
268  }
269  // Does hostname exist?
270  if (database.hasChild (hostname)) {
271  XMLTestNode machine = database.getChild (hostname);
272 
273  // Find matching machine configuration
274  for (int i = 0; i < machine.numChildren (); ++i) {
275  XMLTestNode configuration = machine.getChild (i);
277  configuration.getTag ().compare ("Configuration") != 0,
278  std::runtime_error, "Unexpected Tag \"" << configuration.getTag ()
279  << "\"; only children with Tag = \"Configuration\" are allowed in a "
280  "MachineEntry.");
281 
283  ! configuration.hasChild ("MachineConfiguration") ||
284  ! configuration.hasChild ("Tests"),
285  std::runtime_error,
286  "A Configuration needs to have a child \"MachineConfiguration\" and a "
287  "child \"Tests\".");
288 
289  XMLTestNode machine_configuration = configuration.getChild ("MachineConfiguration");
290  XMLTestNode old_tests = configuration.getChild ("Tests");
291 
292  if (machine_configuration.hasSameElements (machine_config)) {
293  is_new_config = false;
294 
295  // Find existing test with same tag as the new test
296  if (old_tests.hasChild (new_test.getTag ())) {
297 
298  XMLTestNode old_test = old_tests.getChild (new_test.getTag ());
299 
300  int new_test_config = -1;
301  for (int k = 0; k < old_test.numChildren (); ++k) {
302  XMLTestNode old_test_entry = old_test.getChild (k);
303 
305  ! old_test_entry.hasChild ("TestConfiguration") ||
306  ! new_test_entry.hasChild ("TestResults"),
307  std::runtime_error, "A TestEntry needs to have a child "
308  "\"TestConfiguration\" and a child \"TestResults\".");
309 
310  if (old_test_entry.getChild ("TestConfiguration").hasSameElements (new_test_entry.getChild ("TestConfiguration"))) {
311  new_test_config = k;
312  }
313  }
314 
315  if (new_test_config < 0) {
316  old_test.addChild (new_test_entry);
317  return_value = PerfTestNewTestConfiguration;
318  } else {
319  bool deviation = false;
320  XMLTestNode old_test_entry = old_test.getChild (new_test_config);
321  XMLTestNode old_results = old_test_entry.getChild ("TestResults");
322  XMLTestNode new_results = new_test_entry.getChild ("TestResults");
323 
324  // Compare all entries
325  for (int old_r = 0; old_r < old_results.numChildren (); ++old_r) {
326  XMLTestNode result_entry = old_results.getChild (old_r);
327 
328  // Finding entry with same name
329  bool exists = new_results.hasChild (result_entry.getTag ());
330 
331  if (exists) {
332  std::string oldv_str = result_entry.getContentLine (0);
333 
334  // If it is a time or result compare numeric values with tolerance
335  if((result_entry.getTag().find("Time")==0) || (result_entry.getTag().find("Result")==0)) {
336  ValueTolerance old_valtol(oldv_str);
337  ValueTolerance new_valtol(new_results.getChild(result_entry.getTag()).getContentLine(0));
338 
339  if(old_valtol.use_tolerance) {
340  double diff = old_valtol.value - new_valtol.value;
341  diff*=diff;
342 
343  double normalization = old_valtol.value;
344  normalization*=normalization;
345 
346  if(normalization==0?diff>0:diff/normalization>old_valtol.tolerance*old_valtol.tolerance) {
347  deviation = true;
348  std::cout << std::endl
349  << "DeviationA in Test: \"" << old_test.getTag()
350  << "\" for entry \"" << result_entry.getTag() << "\"" << std::endl;
351  std::cout << " Existing Value: \"" << oldv_str << "\"" << std::endl;
352  std::cout << " New Value: \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
353  }
354  } else {
355  if( (old_valtol.lower>new_valtol.value) || (old_valtol.upper<new_valtol.value)) {
356  deviation = true;
357  std::cout << std::endl
358  << "DeviationB in Test: \"" << old_test.getTag()
359  << "\" for entry \"" << result_entry.getTag() << "\"" << std::endl;
360  std::cout << " Existing Value: \"" << oldv_str << "\"" << std::endl;
361  std::cout << " New Value: \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
362  }
363  }
364  } else {
365  // Compare exact match for every other type of entry
366  if(oldv_str.compare(new_results.getChild(result_entry.getTag()).getContentLine(0))!=0) {
367  deviation = true;
368  std::cout << std::endl
369  << "DeviationC in Test: \"" << old_test.getTag()
370  << "\" for entry \"" << result_entry.getTag() << "\"" << std::endl;
371  std::cout << " Existing Value: \"" << oldv_str << "\"" << std::endl;
372  std::cout << " New Value: \"" << new_results.getChild(result_entry.getTag()).getContentLine(0) << "\"" << std::endl << std::endl;
373  }
374  }
375  }
376  // An old value was not given in the new test: this is an error;
377  if(!exists) {
378  std::cout << "Error New test has same name as an existing one, but one of the old entries is missing." << std::endl;
379  deviation = true;
380  }
381  }
382 
383  if(deviation) { return_value = PerfTestFailed; }
384  else {
385  // Did someone add new values to the test?
386  if(new_results.numChildren()!=old_results.numChildren()) {
387  for(int new_r = 0; new_r < new_results.numChildren() ; new_r++) {
388  if(!old_results.hasChild(new_results.getChild(new_r).getTag())) {
389  old_results.addChild(new_results.getChild(new_r));
390  }
391  }
392 
393  return_value = PerfTestUpdatedTest;
394  }
395  }
396  }
397  } else { // End Test Exists
398  // Add new test if no match was found
399  old_tests.addChild(new_test);
400  return_value = PerfTestNewTest;
401  }
402  } // End MachineConfiguration Exists
403  } // End loop over MachineConfigurations
404 
405  // Did not find matching MachineConfiguration
406  if(is_new_config) {
407  XMLTestNode config("Configuration");
408  config.addChild(machine_config);
409  XMLTestNode tests("Tests");
410  tests.addChild(new_test);
411 
412  config.addChild(tests);
413  machine.addChild(config);
414 
415  return_value = PerfTestNewConfiguration;
416  }
417  } else { // Machine Entry does not exist
418  XMLTestNode machine(hostname);
419 
420  XMLTestNode config("Configuration");
421  config.addChild(machine_config);
422  XMLTestNode tests("Tests");
423  tests.addChild(new_test);
424  config.addChild(tests);
425 
426  machine.addChild(config);
427 
428  database.addChild(machine);
429 
430  return_value = PerfTestNewMachine;
431  }
432 
433 
434  if(return_value>PerfTestPassed) {
435  std::ofstream fout(filename.c_str());
436  fout << database << std::endl;
437  }
438 
439  return return_value;
440 }
441 }
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.
const XMLObject & getChild(int i) const
Return the i-th child node.
bool hasSameElements(XMLTestNode const &lhs) const
void addDouble(const std::string &name, double val)
const std::string & getContentLine(int i) const
Return the i-th line of character content stored in this node.
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)
bool isEmpty() const
Find out if a node is empty.
RCP< XMLObjectImplem > ptr_
Representation of an XML data tree. XMLObject is a ref-counted handle to a XMLObjectImplem object...
void appendContentLine(const size_t &i, const std::string &str)
const XMLObject * xml_object() const
#define TEUCHOS_COMPILER_NAME
Instantiation of XMLInputSource class for reading XML from a file.
int numContentLines() const
Return the number of lines of character content stored in this node.
const std::string & getTag() const
Return the tag of the current node.
void from_string(const std::string &valtol_str)
XMLObject getObject() const
Get an object by invoking the TreeBuildingXMLHandler on the input data.
PerfTestResult
ReturnValues for PerfTest_CheckOrAdd_Test.
bool hasChild(const std::string &name) const
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 numChildren() const
Return the number of child nodes owned by this node.
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)
#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.