diff options
Diffstat (limited to 'benchmark/source/EASTLBenchmark.cpp')
-rw-r--r-- | benchmark/source/EASTLBenchmark.cpp | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/benchmark/source/EASTLBenchmark.cpp b/benchmark/source/EASTLBenchmark.cpp new file mode 100644 index 0000000..8e4d3ae --- /dev/null +++ b/benchmark/source/EASTLBenchmark.cpp @@ -0,0 +1,291 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include <EASTL/string.h> +#include <EAMain/EAMain.h> + +#ifdef _MSC_VER + #pragma warning(push, 0) +#endif +#include <stdio.h> +#include <math.h> +#include <float.h> +#ifdef _MSC_VER + #pragma warning(pop) +#endif + + + +namespace Benchmark +{ + static int64_t ConvertStopwatchUnits(EA::StdC::Stopwatch::Units unitsSource, int64_t valueSource, EA::StdC::Stopwatch::Units unitsDest) + { + using namespace EA::StdC; + + int64_t valueDest = valueSource; + + if(unitsSource != unitsDest) + { + double sourceMultiplier; + + switch (unitsSource) + { + case Stopwatch::kUnitsCPUCycles: + sourceMultiplier = Stopwatch::GetUnitsPerCPUCycle(unitsDest); // This will typically be a number less than 1. + valueDest = (int64_t)(valueSource * sourceMultiplier); + break; + + case Stopwatch::kUnitsCycles: + sourceMultiplier = Stopwatch::GetUnitsPerStopwatchCycle(unitsDest); // This will typically be a number less than 1. + valueDest = (int64_t)(valueSource * sourceMultiplier); + break; + + case Stopwatch::kUnitsNanoseconds: + case Stopwatch::kUnitsMicroseconds: + case Stopwatch::kUnitsMilliseconds: + case Stopwatch::kUnitsSeconds: + case Stopwatch::kUnitsMinutes: + case Stopwatch::kUnitsUserDefined: + // To do. Also, handle the case of unitsDest being Cycles or CPUCycles and unitsSource being a time. + break; + } + } + + return valueDest; + } + + void WriteTime(int64_t timeNS, eastl::string& sTime) + { + if(timeNS > 1000000000) + sTime.sprintf(" %6.2f s", (double)timeNS / 1000000000); + else if(timeNS > 1000000) + sTime.sprintf("%6.1f ms", (double)timeNS / 1000000); + else if(timeNS > 1000) + sTime.sprintf("%6.1f us", (double)timeNS / 1000); + else + sTime.sprintf("%6.1f ns", (double)timeNS / 1); + } + + + + Environment gEnvironment; + + Environment& GetEnvironment() + { + return gEnvironment; + } + + + + ResultSet gResultSet; + + ResultSet& GetResultSet() + { + return gResultSet; + } + + + + // Scratch sprintf buffer + char gScratchBuffer[1024]; + + + void DoNothing(...) + { + // Intentionally nothing. + } + + + void AddResult(const char* pName, int units, int64_t nTime1, int64_t nTime2, const char* pNotes) + { + Result result; + + result.msName = pName; + result.mUnits = units; + result.mTime1 = nTime1; + result.mTime1NS = ConvertStopwatchUnits((EA::StdC::Stopwatch::Units)units, nTime1, EA::StdC::Stopwatch::kUnitsNanoseconds); + result.mTime2 = nTime2; + result.mTime2NS = ConvertStopwatchUnits((EA::StdC::Stopwatch::Units)units, nTime2, EA::StdC::Stopwatch::kUnitsNanoseconds); + + if(pNotes) + result.msNotes = pNotes; + + gResultSet.insert(result); + } + + + void PrintResultLine(const Result& result) + { + const double fRatio = (double)result.mTime1 / (double)result.mTime2; + const double fRatioPrinted = (fRatio > 100) ? 100 : fRatio; + const double fPercentChange = fabs(((double)result.mTime1 - (double)result.mTime2) / (((double)result.mTime1 + (double)result.mTime2) / 2)); + const bool bDifference = (result.mTime1 > 10) && (result.mTime2 > 10) && (fPercentChange > 0.25); + const char* pDifference = (bDifference ? (result.mTime1 < result.mTime2 ? "-" : "+") : ""); + + eastl::string sClockTime1, sClockTime2; + + WriteTime(result.mTime1NS, sClockTime1); // This converts an integer in nanoseconds (e.g. 23400000) to a string (e.g. "23.4 ms") + WriteTime(result.mTime2NS, sClockTime2); + + EA::UnitTest::Report("%-43s | %13" PRIu64 " %s | %13" PRIu64 " %s | %10.2f%10s", result.msName.c_str(), result.mTime1, sClockTime1.c_str(), result.mTime2, sClockTime2.c_str(), fRatioPrinted, pDifference); + + if(result.msNotes.length()) // If there are any notes... + EA::UnitTest::Report(" %s", result.msNotes.c_str()); + EA::UnitTest::Report("\n"); + } + + + #if defined(EASTL_BENCHMARK_WRITE_FILE) && EASTL_BENCHMARK_WRITE_FILE + + #if !defined(EASTL_BENCHMARK_WRITE_FILE_PATH) + #define EASTL_BENCHMARK_WRITE_FILE_PATH "BenchmarkResults.txt" + #endif + + struct FileWriter + { + FILE* mpReportFile; + EA::EAMain::ReportFunction mpSavedReportFunction; + static FileWriter* gpFileWriter; + + static void StaticPrintfReportFunction(const char8_t* pText) + { + if(gpFileWriter) + gpFileWriter->PrintfReportFunction(pText); + } + + void PrintfReportFunction(const char8_t* pText) + { + fwrite(pText, strlen(pText), 1, mpReportFile); + EA::EAMain::ReportFunction gpReportFunction = EA::EAMain::GetDefaultReportFunction(); + gpReportFunction(pText); + } + + FileWriter() : mpReportFile(NULL), mpSavedReportFunction(NULL) + { + mpReportFile = fopen(EASTL_BENCHMARK_WRITE_FILE_PATH, "w+"); + + if(mpReportFile) + { + gpFileWriter = this; + mpSavedReportFunction = EA::EAMain::GetDefaultReportFunction(); + EA::EAMain::SetReportFunction(StaticPrintfReportFunction); + } + } + + ~FileWriter() + { + if(mpReportFile) + { + gpFileWriter = NULL; + EA::EAMain::SetReportFunction(mpSavedReportFunction); + fclose(mpReportFile); + } + } + }; + + FileWriter* FileWriter::gpFileWriter = NULL; + #endif + + + void PrintResults() + { + #if defined(EASTL_BENCHMARK_WRITE_FILE) && EASTL_BENCHMARK_WRITE_FILE + FileWriter fileWriter; // This will auto-execute. + #endif + + // Print the results + EA::UnitTest::Report("\n"); + EA::UnitTest::Report("****************************************************************************************\n"); + EA::UnitTest::Report("EASTL Benchmark test results\n"); + EA::UnitTest::Report("****************************************************************************************\n"); + EA::UnitTest::Report("\n"); + EA::UnitTest::Report("EASTL version: %s\n", EASTL_VERSION); + EA::UnitTest::Report("Platform: %s\n", gEnvironment.msPlatform.c_str()); + EA::UnitTest::Report("Compiler: %s\n", EA_COMPILER_STRING); + #if defined(EA_DEBUG) || defined(_DEBUG) + EA::UnitTest::Report("Allocator: PPMalloc::GeneralAllocatorDebug. Thread safety enabled.\n"); + EA::UnitTest::Report("Build: Debug. Inlining disabled. STL debug features disabled.\n"); + #else + EA::UnitTest::Report("Allocator: PPMalloc::GeneralAllocator. Thread safety enabled.\n"); + EA::UnitTest::Report("Build: Full optimization. Inlining enabled.\n"); + #endif + EA::UnitTest::Report("\n"); + EA::UnitTest::Report("Values are ticks and time to complete tests; smaller values are better.\n"); + EA::UnitTest::Report("\n"); + EA::UnitTest::Report("%-43s%26s%26s%13s%13s\n", "Test", gEnvironment.msSTLName1.c_str(), gEnvironment.msSTLName2.c_str(), "Ratio", "Difference?"); + EA::UnitTest::Report("---------------------------------------------------------------------------------------------------------------------\n"); + + eastl::string sTestTypeLast; + eastl::string sTestTypeTemp; + + for(ResultSet::iterator it = gResultSet.begin(); it != gResultSet.end(); ++it) + { + const Result& result = *it; + + eastl_size_t n = result.msName.find('/'); + if(n == eastl::string::npos) + n = result.msName.length(); + sTestTypeTemp.assign(result.msName, 0, n); + + if(sTestTypeTemp != sTestTypeLast) // If it looks like we are changing to a new test type... add an empty line to help readability. + { + if(it != gResultSet.begin()) + EA::UnitTest::Report("\n"); + sTestTypeLast = sTestTypeTemp; + } + + PrintResultLine(result); + } + + // We will print out a final line that has the sum of the rows printed above. + Result resultSum; + resultSum.msName = "sum"; + + for(ResultSet::iterator its = gResultSet.begin(); its != gResultSet.end(); ++its) + { + const Result& resultTemp = *its; + + EASTL_ASSERT(resultTemp.mUnits == EA::StdC::Stopwatch::kUnitsCPUCycles); // Our ConvertStopwatchUnits call below assumes that every measured time is CPUCycles. + resultSum.mTime1 += resultTemp.mTime1; + resultSum.mTime2 += resultTemp.mTime2; + } + + // We do this convert as a final step instead of the loop in order to avoid loss of precision. + resultSum.mTime1NS = ConvertStopwatchUnits(EA::StdC::Stopwatch::kUnitsCPUCycles, resultSum.mTime1, EA::StdC::Stopwatch::kUnitsNanoseconds); + resultSum.mTime2NS = ConvertStopwatchUnits(EA::StdC::Stopwatch::kUnitsCPUCycles, resultSum.mTime2, EA::StdC::Stopwatch::kUnitsNanoseconds); + EA::UnitTest::Report("\n"); + PrintResultLine(resultSum); + + EA::UnitTest::Report("\n"); + EA::UnitTest::Report("****************************************************************************************\n"); + EA::UnitTest::Report("\n"); + + // Clear the results + gResultSet.clear(); + gEnvironment.clear(); + } + +} // namespace Benchmark + + + + + + + + + + + + + + + + + + + |