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).
- Added wxEventFilter class and wxEvtHandler::{Add,Remove}Filter().
- Added convenient wxCmdLineParser::AddLong{Option,Switch}() wrappers.
- Added wxStopWatch::TimeInMicro().
All (GUI):

View 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;

View 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;
};

View File

@@ -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;
}
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__
{
m_t0 = wxGetLocalTimeMillis() - t;
}
m_pause = 0;
m_pauseCount = 0;
m_t0 = GetCurrentClockValue();
}
long wxStopWatch::GetElapsedTime() const
wxLongLong wxStopWatch::GetClockFreq() const
{
#ifdef __WXMSW__
LARGE_INTEGER counter;
if ( gs_perfCounter.CanBeUsed() && ::QueryPerformanceCounter(&counter) )
{
wxLongLong delta(counter.QuadPart);
delta -= m_t0;
// 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__
return ((delta*1000)/gs_perfCounter.freq.QuadPart).GetLo();
}
#endif
return (wxGetLocalTimeMillis() - m_t0).GetLo();
// Currently milliseconds are used everywhere else.
return MILLISECONDS_PER_SECOND;
}
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

View 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();