From b0ec002323e2a8b17fe2a9eb13db97d7ca8c1471 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 27 Nov 2011 19:50:12 +0000 Subject: [PATCH] Added wxStopWatch::TimeInMicro() for better precision time measurement. Also simplify/streamline wxStopWatch implementation and replace confusingly named m_pause with more clear m_elapsedBeforePause. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@69835 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 1 + include/wx/stopwatch.h | 39 +++++++++++++------ interface/wx/stopwatch.h | 13 +++++++ src/common/stopwatch.cpp | 80 +++++++++++++++++++++++++------------- tests/events/stopwatch.cpp | 11 +++++- 5 files changed, 105 insertions(+), 39 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index bce8f8c7ae..a0a5e779af 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -459,6 +459,7 @@ All: - Fix crash in wxArray::insert() overload taking iterator range (wsu). - Added wxEventFilter class and wxEvtHandler::{Add,Remove}Filter(). - Added convenient wxCmdLineParser::AddLong{Option,Switch}() wrappers. +- Added wxStopWatch::TimeInMicro(). All (GUI): diff --git a/include/wx/stopwatch.h b/include/wx/stopwatch.h index 325722a892..189f0434cc 100644 --- a/include/wx/stopwatch.h +++ b/include/wx/stopwatch.h @@ -26,14 +26,16 @@ public: // ctor starts the stop watch wxStopWatch() { m_pauseCount = 0; Start(); } - // start the stop watch at the moment t0 + // Start the stop watch at the moment t0 expressed in milliseconds (i.e. + // calling Time() immediately afterwards returns t0). This can be used to + // restart an existing stopwatch. void Start(long t0 = 0); // pause the stop watch void Pause() { if ( m_pauseCount++ == 0 ) - m_pause = GetElapsedTime(); + m_elapsedBeforePause = GetCurrentClockValue() - m_t0; } // resume it @@ -43,22 +45,37 @@ public: wxT("Resuming stop watch which is not paused") ); if ( --m_pauseCount == 0 ) - Start(m_pause); + { + DoStart(); + m_t0 -= m_elapsedBeforePause; + } } - // get elapsed time since the last Start() in milliseconds - long Time() const; + // Get elapsed time since the last Start() in microseconds. + wxLongLong TimeInMicro() const; -protected: - // returns the elapsed time since t0 - long GetElapsedTime() const; + // get elapsed time since the last Start() in milliseconds + long Time() const { return (TimeInMicro()/1000).ToLong(); } private: - // the time of the last Start() + // Really starts the stop watch. The initial time is set to current clock + // value. + void DoStart(); + + // Returns the current clock value in its native units. + wxLongLong GetCurrentClockValue() const; + + // Return the frequency of the clock used in its ticks per second. + wxLongLong GetClockFreq() const; + + + // The clock value when the stop watch was last started. Its units vary + // depending on the platform. wxLongLong m_t0; - // the time of the last Pause() (only valid if m_pauseCount > 0) - long m_pause; + // The elapsed time as of last Pause() call (only valid if m_pauseCount > + // 0) in the same units as m_t0. + wxLongLong m_elapsedBeforePause; // if > 0, the stop watch is paused, otherwise it is running int m_pauseCount; diff --git a/interface/wx/stopwatch.h b/interface/wx/stopwatch.h index 632c876e0c..d10bbbe0a4 100644 --- a/interface/wx/stopwatch.h +++ b/interface/wx/stopwatch.h @@ -67,8 +67,21 @@ public: /** Returns the time in milliseconds since the start (or restart) or the last call of Pause(). + + @see TimeInMicro() */ long Time() const; + + /** + Returns elapsed time in microseconds. + + This method is similar to Time() but returns the elapsed time in + microseconds and not milliseconds. Notice that not all platforms really + can measure times with this precision. + + @since 2.9.3 + */ + wxLongLong TimeInMicro() const; }; diff --git a/src/common/stopwatch.cpp b/src/common/stopwatch.cpp index 922a5f0ed8..4ff9b7532d 100644 --- a/src/common/stopwatch.cpp +++ b/src/common/stopwatch.cpp @@ -95,11 +95,11 @@ #if wxUSE_STOPWATCH -#ifdef __WXMSW__ - namespace { +#ifdef __WXMSW__ + struct PerfCounter { PerfCounter() @@ -117,53 +117,79 @@ struct PerfCounter bool init; } gs_perfCounter; -} // anonymous namespace - #endif // __WXMSW__ -void wxStopWatch::Start(long t) +const int MILLISECONDS_PER_SECOND = 1000; +const int MICROSECONDS_PER_SECOND = 1000*1000; + +} // anonymous namespace + +void wxStopWatch::DoStart() { #ifdef __WXMSW__ if ( !gs_perfCounter.init ) { wxCriticalSectionLocker lock(gs_perfCounter.cs); ::QueryPerformanceFrequency(&gs_perfCounter.freq); + + // Just a sanity check: it's not supposed to happen but verify that + // ::QueryPerformanceCounter() succeeds so that we can really use it. + LARGE_INTEGER counter; + if ( !::QueryPerformanceCounter(&counter) ) + { + wxLogDebug("QueryPerformanceCounter() unexpected failed (%s), " + "will not use it.", wxSysErrorMsg()); + + gs_perfCounter.freq.QuadPart = 0; + } + gs_perfCounter.init = true; } +#endif // __WXMSW__ - LARGE_INTEGER counter; - if ( gs_perfCounter.CanBeUsed() && ::QueryPerformanceCounter(&counter) ) - { - m_t0 = counter.QuadPart - t*gs_perfCounter.freq.QuadPart/1000; - } - else // Fall back to the generic code below. + m_t0 = GetCurrentClockValue(); +} + +wxLongLong wxStopWatch::GetClockFreq() const +{ +#ifdef __WXMSW__ + // Under MSW we use the high resolution performance counter timer which has + // its own frequency (usually related to the CPU clock speed). + if ( gs_perfCounter.CanBeUsed() ) + return gs_perfCounter.freq.QuadPart; #endif // __WXMSW__ - { - m_t0 = wxGetLocalTimeMillis() - t; - } - m_pause = 0; - m_pauseCount = 0; + // Currently milliseconds are used everywhere else. + return MILLISECONDS_PER_SECOND; } -long wxStopWatch::GetElapsedTime() const +void wxStopWatch::Start(long t0) +{ + DoStart(); + + m_t0 -= (wxLongLong(t0)*GetClockFreq())/MILLISECONDS_PER_SECOND; +} + +wxLongLong wxStopWatch::GetCurrentClockValue() const { #ifdef __WXMSW__ - LARGE_INTEGER counter; - if ( gs_perfCounter.CanBeUsed() && ::QueryPerformanceCounter(&counter) ) + if ( gs_perfCounter.CanBeUsed() ) { - wxLongLong delta(counter.QuadPart); - delta -= m_t0; - - return ((delta*1000)/gs_perfCounter.freq.QuadPart).GetLo(); + LARGE_INTEGER counter; + ::QueryPerformanceCounter(&counter); + return counter.QuadPart; } -#endif - return (wxGetLocalTimeMillis() - m_t0).GetLo(); +#endif // __WXMSW__ + + return wxGetLocalTimeMillis(); } -long wxStopWatch::Time() const +wxLongLong wxStopWatch::TimeInMicro() const { - return m_pauseCount ? m_pause : GetElapsedTime(); + const wxLongLong elapsed(m_pauseCount ? m_elapsedBeforePause + : GetCurrentClockValue() - m_t0); + + return (elapsed*MICROSECONDS_PER_SECOND)/GetClockFreq(); } #endif // wxUSE_STOPWATCH diff --git a/tests/events/stopwatch.cpp b/tests/events/stopwatch.cpp index 251a859c63..f50da74306 100644 --- a/tests/events/stopwatch.cpp +++ b/tests/events/stopwatch.cpp @@ -55,13 +55,22 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( StopWatchTestCase, "StopWatchTestCase" ); void StopWatchTestCase::Misc() { - static const long tolerance = 100; // in ms + static const long tolerance = 10; // in ms wxStopWatch sw; long t; + wxLongLong usec; sw.Pause(); // pause it immediately + // verify that almost no time elapsed + usec = sw.TimeInMicro(); + WX_ASSERT_MESSAGE + ( + ("Elapsed time was %" wxLongLongFmtSpec "dus", usec), + usec < tolerance*1000 + ); + wxSleep(1); t = sw.Time(); -- 2.47.2