]> git.saurik.com Git - wxWidgets.git/commitdiff
Added wxStopWatch::TimeInMicro() for better precision time measurement.
authorVadim Zeitlin <vadim@wxwidgets.org>
Sun, 27 Nov 2011 19:50:12 +0000 (19:50 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sun, 27 Nov 2011 19:50:12 +0000 (19:50 +0000)
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
include/wx/stopwatch.h
interface/wx/stopwatch.h
src/common/stopwatch.cpp
tests/events/stopwatch.cpp

index bce8f8c7ae17e241eb9cadc6d8ed2043593fde32..a0a5e779afc3df2f851e4e9e99f1ef7f8b4c3611 100644 (file)
@@ -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):
 
index 325722a8921cd0f18fe866bea3d5bc4d2b99e861..189f0434cc389c434dab5b4abba85c6ba892ce48 100644 (file)
@@ -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;
index 632c876e0c71aba96d67ab3fb49faf70680f64b7..d10bbbe0a4fdc5a6ba95b758ea94158bcf513c07 100644 (file)
@@ -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;
 };
 
 
index 922a5f0ed8b937848f10c00df4bdfe599ccd2beb..4ff9b7532d648b86a854c360ed46aa1a26e8479b 100644 (file)
 
 #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
index 251a859c63170094950dcb451182635d1811b4e0..f50da74306beaf553ccbbaea4ee9732f8b6a5d04 100644 (file)
@@ -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();