Teuchos - Trilinos Tools Package  Version of the Day
Teuchos_XMLPerfTestArchive.cpp
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 
59 ValueTolerance::ValueTolerance() {
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 
83 ValueTolerance::ValueTolerance(std::string str) {
84  from_string(str);
85 }
86 
87 bool ValueTolerance::operator ==(ValueTolerance& rhs) {
88  return (value == rhs.value) &&
89  (tolerance == rhs.tolerance) &&
90  (lower == rhs.lower) &&
91  (upper == rhs.upper) &&
92  (use_tolerance == rhs.use_tolerance);
93 }
94 
95 std::string ValueTolerance::as_string(){
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 
120  XMLTestNode::XMLTestNode():XMLObject() {}
121 
122  XMLTestNode::XMLTestNode(const std::string &tag):XMLObject(tag) {}
123 
124  XMLTestNode::XMLTestNode(XMLObjectImplem *ptr):XMLObject(ptr) {}
125 
126  XMLTestNode::XMLTestNode(XMLObject obj):XMLObject(obj) {}
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 
176  const XMLObject* XMLTestNode::xml_object() const {
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 }
const std::string & getTag() const
Return the tag of the current node.
#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.
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...
Tools for an XML-based performance test archive.
XMLObject getObject() const
Get an object by invoking the TreeBuildingXMLHandler on the input data.
Instantiation of XMLInputSource class for reading XML from a file.
PerfTestResult
ReturnValues for PerfTest_CheckOrAdd_Test.
The Teuchos namespace contains all of the classes, structs and enums used by Teuchos, as well as a number of utility routines.
int numChildren() const
Return the number of child nodes owned by this node.
Subclass of XMLObject used by the performance archive.
Definition of XMLInputSource derived class for reading XML from a file.
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.
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.