Teuchos - Trilinos Tools Package Version of the Day
Loading...
Searching...
No Matches
Teuchos_XMLPerfTestArchive.cpp
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
23namespace Teuchos {
24
25ValueTolerance::ValueTolerance() {
26 value = 0;
27 lower = 0;
28 upper = 0;
29 tolerance = 0;
30 use_tolerance = true;
31}
32
33ValueTolerance::ValueTolerance(double val, double tol) {
34 value = val;
35 lower = 0;
36 upper = 0;
37 tolerance = tol;
38 use_tolerance = true;
39}
40
41ValueTolerance::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
49ValueTolerance::ValueTolerance(std::string str) {
50 from_string(str);
51}
52
53bool ValueTolerance::operator ==(ValueTolerance& rhs) {
54 return (value == rhs.value) &&
55 (tolerance == rhs.tolerance) &&
56 (lower == rhs.lower) &&
57 (upper == rhs.upper) &&
58 (use_tolerance == rhs.use_tolerance);
59}
60
61std::string ValueTolerance::as_string(){
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
70void 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
86 XMLTestNode::XMLTestNode():XMLObject() {}
87
88 XMLTestNode::XMLTestNode(const std::string &tag):XMLObject(tag) {}
89
90 XMLTestNode::XMLTestNode(XMLObjectImplem *ptr):XMLObject(ptr) {}
91
92 XMLTestNode::XMLTestNode(XMLObject obj):XMLObject(obj) {}
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
142 const XMLObject* XMLTestNode::xml_object() const {
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
204PerfTestResult
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}
Definition of XMLInputSource derived class for reading XML from a file.
An object representation of a subset of XML data.
Tools for an XML-based performance test archive.
Instantiation of XMLInputSource class for reading XML from a file.
Ptr< T > ptr(T *p)
Create a pointer to an object from a raw pointer.
XMLObject getObject() const
Get an object by invoking the TreeBuildingXMLHandler on the input data.
Representation of an XML data tree. XMLObject is a ref-counted handle to a XMLObjectImplem object,...
void addChild(const XMLObject &child)
Add a child node to the node.
const std::string & getTag() const
Return the tag of the current node.
const std::string & getContentLine(int i) const
Return the i-th line of character content stored in this node.
bool isEmpty() const
Find out if a node is empty.
int numChildren() const
Return the number of child nodes owned by this node.
Subclass of XMLObject used by the performance archive.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
The Teuchos namespace contains all of the classes, structs and enums used by Teuchos,...
XMLTestNode PerfTest_MachineConfig()
PerfTest_MachineConfig generates a basic machine configuration XMLTestNode.
PerfTestResult
ReturnValues for PerfTest_CheckOrAdd_Test.
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.
ValueTolerance is a struct to keep a tuple of value and a tolerance. The tolerance can be either expr...