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
This commit is contained in:
Vadim Zeitlin
2011-11-27 19:50:12 +00:00
parent 54647bb750
commit b0ec002323
5 changed files with 107 additions and 41 deletions

View File

@@ -459,6 +459,7 @@ All:
- Fix crash in wxArray::insert() overload taking iterator range (wsu). - Fix crash in wxArray::insert() overload taking iterator range (wsu).
- Added wxEventFilter class and wxEvtHandler::{Add,Remove}Filter(). - Added wxEventFilter class and wxEvtHandler::{Add,Remove}Filter().
- Added convenient wxCmdLineParser::AddLong{Option,Switch}() wrappers. - Added convenient wxCmdLineParser::AddLong{Option,Switch}() wrappers.
- Added wxStopWatch::TimeInMicro().
All (GUI): All (GUI):

View File

@@ -26,14 +26,16 @@ public:
// ctor starts the stop watch // ctor starts the stop watch
wxStopWatch() { m_pauseCount = 0; Start(); } 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); void Start(long t0 = 0);
// pause the stop watch // pause the stop watch
void Pause() void Pause()
{ {
if ( m_pauseCount++ == 0 ) if ( m_pauseCount++ == 0 )
m_pause = GetElapsedTime(); m_elapsedBeforePause = GetCurrentClockValue() - m_t0;
} }
// resume it // resume it
@@ -43,22 +45,37 @@ public:
wxT("Resuming stop watch which is not paused") ); wxT("Resuming stop watch which is not paused") );
if ( --m_pauseCount == 0 ) if ( --m_pauseCount == 0 )
Start(m_pause); {
DoStart();
m_t0 -= m_elapsedBeforePause;
}
} }
// get elapsed time since the last Start() in milliseconds // Get elapsed time since the last Start() in microseconds.
long Time() const; wxLongLong TimeInMicro() const;
protected: // get elapsed time since the last Start() in milliseconds
// returns the elapsed time since t0 long Time() const { return (TimeInMicro()/1000).ToLong(); }
long GetElapsedTime() const;
private: 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; wxLongLong m_t0;
// the time of the last Pause() (only valid if m_pauseCount > 0) // The elapsed time as of last Pause() call (only valid if m_pauseCount >
long m_pause; // 0) in the same units as m_t0.
wxLongLong m_elapsedBeforePause;
// if > 0, the stop watch is paused, otherwise it is running // if > 0, the stop watch is paused, otherwise it is running
int m_pauseCount; int m_pauseCount;

View File

@@ -67,8 +67,21 @@ public:
/** /**
Returns the time in milliseconds since the start (or restart) or the last Returns the time in milliseconds since the start (or restart) or the last
call of Pause(). call of Pause().
@see TimeInMicro()
*/ */
long Time() const; 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;
}; };

View File

@@ -95,11 +95,11 @@
#if wxUSE_STOPWATCH #if wxUSE_STOPWATCH
#ifdef __WXMSW__
namespace namespace
{ {
#ifdef __WXMSW__
struct PerfCounter struct PerfCounter
{ {
PerfCounter() PerfCounter()
@@ -117,53 +117,79 @@ struct PerfCounter
bool init; bool init;
} gs_perfCounter; } gs_perfCounter;
} // anonymous namespace
#endif // __WXMSW__ #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__ #ifdef __WXMSW__
if ( !gs_perfCounter.init ) if ( !gs_perfCounter.init )
{ {
wxCriticalSectionLocker lock(gs_perfCounter.cs); wxCriticalSectionLocker lock(gs_perfCounter.cs);
::QueryPerformanceFrequency(&gs_perfCounter.freq); ::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; gs_perfCounter.init = true;
} }
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.
#endif // __WXMSW__ #endif // __WXMSW__
{
m_t0 = wxGetLocalTimeMillis() - t;
}
m_pause = 0; m_t0 = GetCurrentClockValue();
m_pauseCount = 0;
} }
long wxStopWatch::GetElapsedTime() const wxLongLong wxStopWatch::GetClockFreq() const
{ {
#ifdef __WXMSW__ #ifdef __WXMSW__
LARGE_INTEGER counter; // Under MSW we use the high resolution performance counter timer which has
if ( gs_perfCounter.CanBeUsed() && ::QueryPerformanceCounter(&counter) ) // its own frequency (usually related to the CPU clock speed).
{ if ( gs_perfCounter.CanBeUsed() )
wxLongLong delta(counter.QuadPart); return gs_perfCounter.freq.QuadPart;
delta -= m_t0; #endif // __WXMSW__
return ((delta*1000)/gs_perfCounter.freq.QuadPart).GetLo(); // Currently milliseconds are used everywhere else.
} return MILLISECONDS_PER_SECOND;
#endif
return (wxGetLocalTimeMillis() - m_t0).GetLo();
} }
long wxStopWatch::Time() const void wxStopWatch::Start(long t0)
{ {
return m_pauseCount ? m_pause : GetElapsedTime(); DoStart();
m_t0 -= (wxLongLong(t0)*GetClockFreq())/MILLISECONDS_PER_SECOND;
}
wxLongLong wxStopWatch::GetCurrentClockValue() const
{
#ifdef __WXMSW__
if ( gs_perfCounter.CanBeUsed() )
{
LARGE_INTEGER counter;
::QueryPerformanceCounter(&counter);
return counter.QuadPart;
}
#endif // __WXMSW__
return wxGetLocalTimeMillis();
}
wxLongLong wxStopWatch::TimeInMicro() const
{
const wxLongLong elapsed(m_pauseCount ? m_elapsedBeforePause
: GetCurrentClockValue() - m_t0);
return (elapsed*MICROSECONDS_PER_SECOND)/GetClockFreq();
} }
#endif // wxUSE_STOPWATCH #endif // wxUSE_STOPWATCH

View File

@@ -55,13 +55,22 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( StopWatchTestCase, "StopWatchTestCase" );
void StopWatchTestCase::Misc() void StopWatchTestCase::Misc()
{ {
static const long tolerance = 100; // in ms static const long tolerance = 10; // in ms
wxStopWatch sw; wxStopWatch sw;
long t; long t;
wxLongLong usec;
sw.Pause(); // pause it immediately 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); wxSleep(1);
t = sw.Time(); t = sw.Time();