///////////////////////////////////////////////////////////////////////////// // Name: tests/benchmarks/bench.cpp // Purpose: Main file of the benchmarking suite // Author: Vadim Zeitlin // Created: 2008-07-19 // Copyright: (c) 2008 Vadim Zeitlin // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // ============================================================================ // declarations // ============================================================================ // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- #include "wx/app.h" #include "wx/cmdline.h" #include "wx/stopwatch.h" #if wxUSE_GUI #include "wx/frame.h" #endif #include "bench.h" // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- static const char OPTION_LIST = 'l'; static const char OPTION_SINGLE = '1'; static const char OPTION_AVG_COUNT = 'a'; static const char OPTION_NUM_RUNS = 'n'; static const char OPTION_NUMERIC_PARAM = 'p'; static const char OPTION_STRING_PARAM = 's'; // ---------------------------------------------------------------------------- // BenchApp declaration // ---------------------------------------------------------------------------- #if wxUSE_GUI typedef wxApp BenchAppBase; #else typedef wxAppConsole BenchAppBase; #endif class BenchApp : public BenchAppBase { public: BenchApp(); // standard overrides virtual void OnInitCmdLine(wxCmdLineParser& parser); virtual bool OnCmdLineParsed(wxCmdLineParser& parser); virtual bool OnInit(); virtual int OnRun(); virtual int OnExit(); // accessors int GetNumericParameter() const { return m_numParam; } const wxString& GetStringParameter() const { return m_strParam; } private: // list all registered benchmarks void ListBenchmarks(); // command lines options/parameters wxSortedArrayString m_toRun; long m_numRuns, m_avgCount, m_numParam; wxString m_strParam; }; IMPLEMENT_APP_CONSOLE(BenchApp) // ============================================================================ // Bench namespace symbols implementation // ============================================================================ Bench::Function *Bench::Function::ms_head = NULL; long Bench::GetNumericParameter() { return wxGetApp().GetNumericParameter(); } wxString Bench::GetStringParameter() { return wxGetApp().GetStringParameter(); } // ============================================================================ // BenchApp implementation // ============================================================================ BenchApp::BenchApp() { m_avgCount = 10; m_numRuns = 10000; // just some default (TODO: switch to time-based one) m_numParam = 0; } bool BenchApp::OnInit() { if ( !BenchAppBase::OnInit() ) return false; wxPrintf("wxWidgets benchmarking program\n" "Build: %s\n", WX_BUILD_OPTIONS_SIGNATURE); #if wxUSE_GUI // create a hidden parent window to be used as parent for the GUI controls new wxFrame(NULL, wxID_ANY, "Hidden wx benchmark frame"); #endif // wxUSE_GUI return true; } void BenchApp::OnInitCmdLine(wxCmdLineParser& parser) { BenchAppBase::OnInitCmdLine(parser); parser.AddSwitch(OPTION_LIST, "list", "list all the existing benchmarks"); parser.AddSwitch(OPTION_SINGLE, "single", "run the benchmark once only"); parser.AddOption(OPTION_AVG_COUNT, "avg-count", wxString::Format ( "number of times to run benchmarking loop (default: %ld)", m_avgCount ), wxCMD_LINE_VAL_NUMBER); parser.AddOption(OPTION_NUM_RUNS, "num-runs", wxString::Format ( "number of times to run each benchmark in a loop " "(default: %ld)", m_numRuns ), wxCMD_LINE_VAL_NUMBER); parser.AddOption(OPTION_NUMERIC_PARAM, "num-param", wxString::Format ( "numeric parameter used by some benchmark functions " "(default: %ld)", m_numParam ), wxCMD_LINE_VAL_NUMBER); parser.AddOption(OPTION_STRING_PARAM, "str-param", "string parameter used by some benchmark functions " "(default: empty)", wxCMD_LINE_VAL_STRING); parser.AddParam("benchmark name", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE); } bool BenchApp::OnCmdLineParsed(wxCmdLineParser& parser) { if ( parser.Found(OPTION_LIST) ) { ListBenchmarks(); return false; } const size_t count = parser.GetParamCount(); if ( !count ) { parser.Usage(); ListBenchmarks(); return false; } bool numRunsSpecified = false; if ( parser.Found(OPTION_AVG_COUNT, &m_avgCount) ) numRunsSpecified = true; if ( parser.Found(OPTION_NUM_RUNS, &m_numRuns) ) numRunsSpecified = true; parser.Found(OPTION_NUMERIC_PARAM, &m_numParam); parser.Found(OPTION_STRING_PARAM, &m_strParam); if ( parser.Found(OPTION_SINGLE) ) { if ( numRunsSpecified ) { wxFprintf(stderr, "Incompatible options specified.\n"); return false; } m_avgCount = m_numRuns = 1; } // construct sorted array for quick verification of benchmark names wxSortedArrayString benchmarks; for ( Bench::Function *func = Bench::Function::GetFirst(); func; func = func->GetNext() ) { benchmarks.push_back(func->GetName()); } for ( size_t n = 0; n < count; n++ ) { const wxString name = parser.GetParam(n); if ( benchmarks.Index(name) == wxNOT_FOUND ) { wxFprintf(stderr, "No benchmark named \"%s\".\n", name); return false; } m_toRun.push_back(name); } return BenchAppBase::OnCmdLineParsed(parser); } int BenchApp::OnRun() { int rc = EXIT_SUCCESS; for ( Bench::Function *func = Bench::Function::GetFirst(); func; func = func->GetNext() ) { if ( m_toRun.Index(func->GetName()) == wxNOT_FOUND ) continue; wxString params; if ( m_numParam ) params += wxString::Format(" with N=%ld", m_numParam); if ( !m_strParam.empty() ) { if ( !params.empty() ) params += " and"; params += wxString::Format(" with s=\"%s\"", m_strParam); } wxPrintf("Benchmarking %s%s: ", func->GetName(), params); long timeMin = LONG_MAX, timeMax = 0, timeTotal = 0; bool ok = func->Init(); for ( long a = 0; ok && a < m_avgCount; a++ ) { wxStopWatch sw; for ( long n = 0; n < m_numRuns && ok; n++ ) { ok = func->Run(); } sw.Pause(); const long t = sw.Time(); if ( t < timeMin ) timeMin = t; if ( t > timeMax ) timeMax = t; timeTotal += t; } func->Done(); if ( !ok ) { wxPrintf("ERROR\n"); rc = EXIT_FAILURE; } else { wxPrintf("%ldms total, ", timeTotal); long times = m_avgCount; if ( m_avgCount > 2 ) { timeTotal -= timeMin + timeMax; times -= 2; } wxPrintf("%.2f avg (min=%ld, max=%ld)\n", (float)timeTotal / times, timeMin, timeMax); } fflush(stdout); } return rc; } int BenchApp::OnExit() { #if wxUSE_GUI delete GetTopWindow(); #endif // wxUSE_GUI return 0; } /* static */ void BenchApp::ListBenchmarks() { wxPrintf("Available benchmarks:\n"); for ( Bench::Function *func = Bench::Function::GetFirst(); func; func = func->GetNext() ) { wxPrintf("\t%s\n", func->GetName()); } }