#include "ResultDatabase.h" #include #include #include #include using namespace std; bool ResultDatabase::Result::operator<(const Result& rhs) const { if (test < rhs.test) return true; if (test > rhs.test) return false; if (atts < rhs.atts) return true; if (atts > rhs.atts) return false; return false; // less-operator returns false on equal } double ResultDatabase::Result::GetMin() const { double r = FLT_MAX; for (int i = 0; i < value.size(); i++) { r = min(r, value[i]); } return r; } double ResultDatabase::Result::GetMax() const { double r = -FLT_MAX; for (int i = 0; i < value.size(); i++) { r = max(r, value[i]); } return r; } double ResultDatabase::Result::GetMedian() const { return GetPercentile(50); } double ResultDatabase::Result::GetPercentile(double q) const { int n = value.size(); if (n == 0) return FLT_MAX; if (n == 1) return value[0]; if (q <= 0) return value[0]; if (q >= 100) return value[n - 1]; double index = ((n + 1.) * q / 100.) - 1; vector sorted = value; sort(sorted.begin(), sorted.end()); if (n == 2) return (sorted[0] * (1 - q / 100.) + sorted[1] * (q / 100.)); int index_lo = int(index); double frac = index - index_lo; if (frac == 0) return sorted[index_lo]; double lo = sorted[index_lo]; double hi = sorted[index_lo + 1]; return lo + (hi - lo) * frac; } double ResultDatabase::Result::GetMean() const { double r = 0; for (int i = 0; i < value.size(); i++) { r += value[i]; } return r / double(value.size()); } double ResultDatabase::Result::GetStdDev() const { double r = 0; double u = GetMean(); if (u == FLT_MAX) return FLT_MAX; for (int i = 0; i < value.size(); i++) { r += (value[i] - u) * (value[i] - u); } r = sqrt(r / value.size()); return r; } void ResultDatabase::AddResults(const string& test, const string& atts, const string& unit, const vector& values) { for (int i = 0; i < values.size(); i++) { AddResult(test, atts, unit, values[i]); } } static string RemoveAllButLeadingSpaces(const string& a) { string b; int n = a.length(); int i = 0; while (i < n && a[i] == ' ') { b += a[i]; ++i; } for (; i < n; i++) { if (a[i] != ' ' && a[i] != '\t') b += a[i]; } return b; } void ResultDatabase::AddResult(const string& test_orig, const string& atts_orig, const string& unit_orig, double value) { string test = RemoveAllButLeadingSpaces(test_orig); string atts = RemoveAllButLeadingSpaces(atts_orig); string unit = RemoveAllButLeadingSpaces(unit_orig); int index; for (index = 0; index < results.size(); index++) { if (results[index].test == test && results[index].atts == atts) { if (results[index].unit != unit) throw "Internal error: mixed units"; break; } } if (index >= results.size()) { Result r; r.test = test; r.atts = atts; r.unit = unit; results.push_back(r); } results[index].value.push_back(value); } // **************************************************************************** // Method: ResultDatabase::DumpDetailed // // Purpose: // Writes the full results, including all trials. // // Arguments: // out where to print // // Programmer: Jeremy Meredith // Creation: August 14, 2009 // // Modifications: // Jeremy Meredith, Wed Nov 10 14:25:17 EST 2010 // Renamed to DumpDetailed to make room for a DumpSummary. // // Jeremy Meredith, Thu Nov 11 11:39:57 EST 2010 // Added note about (*) missing value tag. // // Jeremy Meredith, Tue Nov 23 13:57:02 EST 2010 // Changed note about missing values to be worded a little better. // // **************************************************************************** void ResultDatabase::DumpDetailed(ostream& out) { vector sorted(results); sort(sorted.begin(), sorted.end()); const int testNameW = 24; const int attW = 12; const int fieldW = 11; out << std::fixed << right << std::setprecision(4); int maxtrials = 1; for (int i = 0; i < sorted.size(); i++) { if (sorted[i].value.size() > maxtrials) maxtrials = sorted[i].value.size(); } // TODO: in big parallel runs, the "trials" are the procs // and we really don't want to print them all out.... out << setw(testNameW) << "test\t" << setw(attW) << "atts\t" << setw(fieldW) << "median\t" << "mean\t" << "stddev\t" << "min\t" << "max\t"; for (int i = 0; i < maxtrials; i++) out << "trial" << i << "\t"; out << endl; for (int i = 0; i < sorted.size(); i++) { Result& r = sorted[i]; out << setw(testNameW) << r.test + "\t"; out << setw(attW) << r.atts + "\t"; out << setw(fieldW) << r.unit + "\t"; if (r.GetMedian() == FLT_MAX) out << "N/A\t"; else out << r.GetMedian() << "\t"; if (r.GetMean() == FLT_MAX) out << "N/A\t"; else out << r.GetMean() << "\t"; if (r.GetStdDev() == FLT_MAX) out << "N/A\t"; else out << r.GetStdDev() << "\t"; if (r.GetMin() == FLT_MAX) out << "N/A\t"; else out << r.GetMin() << "\t"; if (r.GetMax() == FLT_MAX) out << "N/A\t"; else out << r.GetMax() << "\t"; for (int j = 0; j < r.value.size(); j++) { if (r.value[j] == FLT_MAX) out << "N/A\t"; else out << r.value[j] << "\t"; } out << endl; } out << endl << "Note: Any results marked with (*) had missing values." << endl << " This can occur on systems with a mixture of" << endl << " device types or architectural capabilities." << endl; } // **************************************************************************** // Method: ResultDatabase::DumpDetailed // // Purpose: // Writes the summary results (min/max/stddev/med/mean), but not // every individual trial. // // Arguments: // out where to print // // Programmer: Jeremy Meredith // Creation: November 10, 2010 // // Modifications: // Jeremy Meredith, Thu Nov 11 11:39:57 EST 2010 // Added note about (*) missing value tag. // // **************************************************************************** void ResultDatabase::DumpSummary(ostream& out) { vector sorted(results); sort(sorted.begin(), sorted.end()); const int testNameW = 24; const int attW = 12; const int fieldW = 9; out << std::fixed << right << std::setprecision(4); // TODO: in big parallel runs, the "trials" are the procs // and we really don't want to print them all out.... out << setw(testNameW) << "test\t" << setw(attW) << "atts\t" << setw(fieldW) << "units\t" << "median\t" << "mean\t" << "stddev\t" << "min\t" << "max\t"; out << endl; for (int i = 0; i < sorted.size(); i++) { Result& r = sorted[i]; out << setw(testNameW) << r.test + "\t"; out << setw(attW) << r.atts + "\t"; out << setw(fieldW) << r.unit + "\t"; if (r.GetMedian() == FLT_MAX) out << "N/A\t"; else out << r.GetMedian() << "\t"; if (r.GetMean() == FLT_MAX) out << "N/A\t"; else out << r.GetMean() << "\t"; if (r.GetStdDev() == FLT_MAX) out << "N/A\t"; else out << r.GetStdDev() << "\t"; if (r.GetMin() == FLT_MAX) out << "N/A\t"; else out << r.GetMin() << "\t"; if (r.GetMax() == FLT_MAX) out << "N/A\t"; else out << r.GetMax() << "\t"; out << endl; } out << endl << "Note: results marked with (*) had missing values such as" << endl << "might occur with a mixture of architectural capabilities." << endl; } // **************************************************************************** // Method: ResultDatabase::ClearAllResults // // Purpose: // Clears all existing results from the ResultDatabase; used for multiple passes // of the same test or multiple tests. // // Arguments: // // Programmer: Jeffrey Young // Creation: September 10th, 2014 // // Modifications: // // // **************************************************************************** void ResultDatabase::ClearAllResults() { results.clear(); } // **************************************************************************** // Method: ResultDatabase::DumpCsv // // Purpose: // Writes either detailed or summary results (min/max/stddev/med/mean), but not // every individual trial. // // Arguments: // out file to print CSV results // // Programmer: Jeffrey Young // Creation: August 28th, 2014 // // Modifications: // // **************************************************************************** void ResultDatabase::DumpCsv(string fileName) { bool emptyFile; vector sorted(results); sort(sorted.begin(), sorted.end()); // Check to see if the file is empty - if so, add the headers emptyFile = this->IsFileEmpty(fileName); // Open file and append by default ofstream out; out.open(fileName.c_str(), std::ofstream::out | std::ofstream::app); // Add headers only for empty files if (emptyFile) { // TODO: in big parallel runs, the "trials" are the procs // and we really don't want to print them all out.... out << "test, " << "atts, " << "units, " << "median, " << "mean, " << "stddev, " << "min, " << "max, "; out << endl; } for (int i = 0; i < sorted.size(); i++) { Result& r = sorted[i]; out << r.test << ", "; out << r.atts << ", "; out << r.unit << ", "; if (r.GetMedian() == FLT_MAX) out << "N/A, "; else out << r.GetMedian() << ", "; if (r.GetMean() == FLT_MAX) out << "N/A, "; else out << r.GetMean() << ", "; if (r.GetStdDev() == FLT_MAX) out << "N/A, "; else out << r.GetStdDev() << ", "; if (r.GetMin() == FLT_MAX) out << "N/A, "; else out << r.GetMin() << ", "; if (r.GetMax() == FLT_MAX) out << "N/A, "; else out << r.GetMax() << ", "; out << endl; } out << endl; out.close(); } // **************************************************************************** // Method: ResultDatabase::IsFileEmpty // // Purpose: // Returns whether a file is empty - used as a helper for CSV printing // // Arguments: // file The input file to check for emptiness // // Programmer: Jeffrey Young // Creation: August 28th, 2014 // // Modifications: // // **************************************************************************** bool ResultDatabase::IsFileEmpty(string fileName) { bool fileEmpty; ifstream file(fileName.c_str()); // If the file doesn't exist it is by definition empty if (!file.good()) { return true; } else { fileEmpty = (bool)(file.peek() == ifstream::traits_type::eof()); file.close(); return fileEmpty; } // Otherwise, return false return false; } // **************************************************************************** // Method: ResultDatabase::GetResultsForTest // // Purpose: // Returns a vector of results for just one test name. // // Arguments: // test the name of the test results to search for // // Programmer: Jeremy Meredith // Creation: December 3, 2010 // // Modifications: // // **************************************************************************** vector ResultDatabase::GetResultsForTest(const string& test) { // get only the given test results vector retval; for (int i = 0; i < results.size(); i++) { Result& r = results[i]; if (r.test == test) retval.push_back(r); } return retval; } // **************************************************************************** // Method: ResultDatabase::GetResults // // Purpose: // Returns all the results. // // Arguments: // // Programmer: Jeremy Meredith // Creation: December 3, 2010 // // Modifications: // // **************************************************************************** const vector& ResultDatabase::GetResults() const { return results; }