Tim Mayberry
1da30faf7f
This shows that PBD::Timer is pretty much identical in terms of timing and CPU usage as Glib TimeoutSources. They also show the differences on Windows when setting the minimum Multimedia Timer resolution using timeBeginPeriod
551 lines
12 KiB
C++
551 lines
12 KiB
C++
#include "timer_test.h"
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
#include "pbd/timer.h"
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
CPPUNIT_TEST_SUITE_REGISTRATION (TimerTest);
|
|
|
|
using namespace std;
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
UINT&
|
|
min_timer_resolution ()
|
|
{
|
|
static UINT min_res_ms = 0;
|
|
return min_res_ms;
|
|
}
|
|
|
|
bool
|
|
set_min_timer_resolution ()
|
|
{
|
|
TIMECAPS caps;
|
|
|
|
if (timeGetDevCaps(&caps, sizeof(TIMECAPS)) != TIMERR_NOERROR) {
|
|
cerr << "Could not get timer device capabilities..." << endl;
|
|
} else {
|
|
if (timeBeginPeriod(caps.wPeriodMin) != TIMERR_NOERROR) {
|
|
cerr << "Could not set minimum timer resolution to: " << caps.wPeriodMin << "ms" << endl;
|
|
return false;
|
|
}
|
|
else {
|
|
cerr << "Multimedia timer resolution set to: " << caps.wPeriodMin << "ms" << endl;
|
|
min_timer_resolution() = caps.wPeriodMin;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
reset_timer_resolution ()
|
|
{
|
|
if (min_timer_resolution()) {
|
|
if (timeEndPeriod(min_timer_resolution()) != TIMERR_NOERROR) {
|
|
cerr << "Could not reset timer resolution" << endl;
|
|
return false;
|
|
} else {
|
|
cerr << "Multimedia timer resolution reset" << endl;
|
|
return true;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
void
|
|
TimerTest::simulate_load (const string& name, guint64 load_usecs)
|
|
{
|
|
PBD::Timing timing;
|
|
std::ostringstream oss;
|
|
oss << name << " Load.";
|
|
|
|
guint64 i = 0;
|
|
do {
|
|
timing.update ();
|
|
|
|
// totally arbitrary
|
|
if (i % 10000 == 0) {
|
|
oss << ".";
|
|
}
|
|
|
|
++i;
|
|
} while (timing.elapsed () < load_usecs);
|
|
|
|
oss << "Expected = " << load_usecs;
|
|
oss << ", Elapsed = " << timing.elapsed ();
|
|
oss << endl;
|
|
//cerr << oss.str();
|
|
}
|
|
|
|
void
|
|
TimerTest::on_second_timeout ()
|
|
{
|
|
cerr << endl;
|
|
cerr << "Timing Summary: " << m_current_test_name << endl;
|
|
|
|
if (m_idle_timing_data.size()) {
|
|
cerr << "Idle Timing: " << m_idle_timing_data.summary();
|
|
}
|
|
if (m_fast_timing_data.size()) {
|
|
cerr << "Fast Timing: " << m_fast_timing_data.summary();
|
|
}
|
|
if (m_rapid1_timing_data.size()) {
|
|
cerr << "Rapid1 Timing: " << m_rapid1_timing_data.summary();
|
|
}
|
|
if (m_rapid2_timing_data.size()) {
|
|
cerr << "Rapid2 Timing: " << m_rapid2_timing_data.summary();
|
|
}
|
|
reset_timing ();
|
|
}
|
|
|
|
bool
|
|
TimerTest::on_second_timeout_glibmm ()
|
|
{
|
|
TimerTest::on_second_timeout ();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
TimerTest::on_fast_timeout ()
|
|
{
|
|
m_fast_timing_data.add_interval ();
|
|
if (m_block_idle) {
|
|
// do nothing, handled in rapid timers
|
|
} else {
|
|
simulate_load ("Rapid1", 4000);
|
|
}
|
|
}
|
|
|
|
bool
|
|
TimerTest::on_fast_timeout_glibmm ()
|
|
{
|
|
on_fast_timeout ();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
TimerTest::on_rapid1_timeout ()
|
|
{
|
|
m_rapid1_timing_data.add_interval ();
|
|
if (m_block_idle) {
|
|
simulate_load ("Rapid1", rapid1_timer_usecs () * 0.5);
|
|
} else {
|
|
simulate_load ("Rapid1", 2000);
|
|
}
|
|
}
|
|
|
|
bool
|
|
TimerTest::on_rapid1_timeout_glibmm ()
|
|
{
|
|
on_rapid1_timeout ();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
TimerTest::on_rapid2_timeout ()
|
|
{
|
|
m_rapid2_timing_data.add_interval ();
|
|
if (m_block_idle) {
|
|
simulate_load ("Rapid2", rapid2_timer_usecs () * 0.5);
|
|
} else {
|
|
simulate_load ("Rapid2", 2000);
|
|
}
|
|
}
|
|
|
|
bool
|
|
TimerTest::on_rapid2_timeout_glibmm ()
|
|
{
|
|
on_rapid2_timeout ();
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
TimerTest::on_idle_handler ()
|
|
{
|
|
m_idle_timing_data.add_interval ();
|
|
if (m_block_idle) {
|
|
simulate_load ("Idle", rapid2_timer_usecs ());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
TimerTest::on_quit_handler ()
|
|
{
|
|
cerr << "Quit Handler" << endl;
|
|
m_main->quit ();
|
|
return false;
|
|
}
|
|
|
|
void
|
|
TimerTest::reset_timing ()
|
|
{
|
|
m_idle_timing_data.reset ();
|
|
m_fast_timing_data.reset ();
|
|
m_rapid1_timing_data.reset ();
|
|
m_rapid2_timing_data.reset ();
|
|
}
|
|
|
|
void
|
|
TimerTest::start_timing ()
|
|
{
|
|
m_idle_timing_data.start_timing ();
|
|
m_fast_timing_data.start_timing ();
|
|
m_rapid1_timing_data.start_timing ();
|
|
m_rapid2_timing_data.start_timing ();
|
|
}
|
|
|
|
gboolean
|
|
TimerTest::_second_timeout_handler (void *data)
|
|
{
|
|
TimerTest *const tt = static_cast<TimerTest*>(data);
|
|
tt->on_second_timeout ();
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
TimerTest::_fast_timeout_handler (void *data)
|
|
{
|
|
TimerTest *const tt = static_cast<TimerTest*>(data);
|
|
tt->on_fast_timeout ();
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
TimerTest::_rapid1_timeout_handler (void *data)
|
|
{
|
|
TimerTest *const tt = static_cast<TimerTest*>(data);
|
|
tt->on_rapid1_timeout ();
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
TimerTest::_rapid2_timeout_handler (void *data)
|
|
{
|
|
TimerTest *const tt = static_cast<TimerTest*>(data);
|
|
tt->on_rapid2_timeout ();
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
TimerTest::reset_timing_run_main ()
|
|
{
|
|
reset_timing ();
|
|
start_timing ();
|
|
|
|
connect_quit_timeout ();
|
|
|
|
m_main = Glib::MainLoop::create (m_context);
|
|
m_main->run ();
|
|
}
|
|
|
|
void
|
|
TimerTest::testGlibTimeoutSources ()
|
|
{
|
|
m_current_test_name = "testGlibTimeoutSources";
|
|
_testGlibTimeoutSources ();
|
|
}
|
|
|
|
void
|
|
TimerTest::_testGlibTimeoutSources ()
|
|
{
|
|
m_context = Glib::MainContext::create ();
|
|
|
|
GSource * second_timeout_source = g_timeout_source_new (second_timer_ms ());
|
|
|
|
g_source_set_callback (second_timeout_source , &TimerTest::_second_timeout_handler, this, NULL);
|
|
|
|
g_source_attach (second_timeout_source, m_context->gobj());
|
|
|
|
if (m_connect_idle) {
|
|
connect_idle_handler ();
|
|
reset_timing_run_main ();
|
|
}
|
|
|
|
GSource * fast_timeout_source = g_timeout_source_new (fast_timer_ms ());
|
|
|
|
g_source_set_callback (fast_timeout_source , &TimerTest::_fast_timeout_handler, this, NULL);
|
|
|
|
g_source_attach (fast_timeout_source, m_context->gobj());
|
|
|
|
// now run with fast timeout
|
|
reset_timing_run_main ();
|
|
|
|
GSource * rapid1_timeout_source = g_timeout_source_new (rapid1_timer_ms ());
|
|
|
|
g_source_set_callback (rapid1_timeout_source , &TimerTest::_rapid1_timeout_handler, this, NULL);
|
|
|
|
g_source_attach (rapid1_timeout_source, m_context->gobj());
|
|
|
|
// now run with fast and rapid1 timeouts
|
|
reset_timing_run_main ();
|
|
|
|
GSource * rapid2_timeout_source = g_timeout_source_new (rapid2_timer_ms ());
|
|
|
|
g_source_set_callback (rapid2_timeout_source , &TimerTest::_rapid2_timeout_handler, this, NULL);
|
|
|
|
g_source_attach (rapid2_timeout_source, m_context->gobj());
|
|
|
|
// now run with fast, rapid1 and rapid2 timeouts
|
|
reset_timing_run_main ();
|
|
|
|
// cleanup
|
|
g_source_destroy (second_timeout_source);
|
|
g_source_unref (second_timeout_source);
|
|
|
|
g_source_destroy (fast_timeout_source);
|
|
g_source_unref (fast_timeout_source);
|
|
|
|
g_source_destroy (rapid1_timeout_source);
|
|
g_source_unref (rapid1_timeout_source);
|
|
|
|
g_source_destroy (rapid2_timeout_source);
|
|
g_source_unref (rapid2_timeout_source);
|
|
}
|
|
|
|
void
|
|
TimerTest::testGlibmmSignalTimeouts ()
|
|
{
|
|
m_current_test_name = "testGlibmmSignalTimeouts";
|
|
_testGlibmmSignalTimeouts ();
|
|
}
|
|
|
|
void
|
|
TimerTest::_testGlibmmSignalTimeouts ()
|
|
{
|
|
m_context = Glib::MainContext::get_default ();
|
|
|
|
Glib::signal_timeout().connect(sigc::mem_fun(*this, &TimerTest::on_second_timeout_glibmm), second_timer_ms());
|
|
|
|
if (m_connect_idle) {
|
|
connect_idle_handler ();
|
|
reset_timing_run_main ();
|
|
}
|
|
|
|
Glib::signal_timeout().connect(sigc::mem_fun(*this, &TimerTest::on_fast_timeout_glibmm), fast_timer_ms());
|
|
|
|
reset_timing_run_main ();
|
|
|
|
Glib::signal_timeout().connect(sigc::mem_fun(*this, &TimerTest::on_rapid1_timeout_glibmm), rapid1_timer_ms());
|
|
|
|
reset_timing_run_main ();
|
|
|
|
Glib::signal_timeout().connect(sigc::mem_fun(*this, &TimerTest::on_rapid2_timeout_glibmm), rapid2_timer_ms());
|
|
|
|
reset_timing_run_main ();
|
|
}
|
|
|
|
void
|
|
TimerTest::testGlibmmTimeoutSources ()
|
|
{
|
|
m_current_test_name = "testGlibmmTimeoutSources";
|
|
_testGlibmmTimeoutSources ();
|
|
}
|
|
|
|
void
|
|
TimerTest::_testGlibmmTimeoutSources ()
|
|
{
|
|
m_context = Glib::MainContext::create ();
|
|
|
|
const Glib::RefPtr<Glib::TimeoutSource> second_source = Glib::TimeoutSource::create(second_timer_ms());
|
|
second_source->connect(sigc::mem_fun(*this, &TimerTest::on_second_timeout_glibmm));
|
|
|
|
second_source->attach(m_context);
|
|
|
|
if (m_connect_idle) {
|
|
connect_idle_handler ();
|
|
reset_timing_run_main ();
|
|
}
|
|
|
|
const Glib::RefPtr<Glib::TimeoutSource> fast_source = Glib::TimeoutSource::create(fast_timer_ms());
|
|
fast_source->connect(sigc::mem_fun(*this, &TimerTest::on_fast_timeout_glibmm));
|
|
|
|
fast_source->attach(m_context);
|
|
|
|
reset_timing_run_main ();
|
|
|
|
const Glib::RefPtr<Glib::TimeoutSource> rapid1_source = Glib::TimeoutSource::create(rapid1_timer_ms());
|
|
sigc::connection rapid1_connection = rapid1_source->connect(sigc::mem_fun(*this, &TimerTest::on_rapid1_timeout_glibmm));
|
|
|
|
rapid1_source->attach(m_context);
|
|
|
|
reset_timing_run_main ();
|
|
|
|
const Glib::RefPtr<Glib::TimeoutSource> rapid2_source = Glib::TimeoutSource::create(rapid2_timer_ms());
|
|
sigc::connection rapid2_connection = rapid2_source->connect(sigc::mem_fun(*this, &TimerTest::on_rapid2_timeout_glibmm));
|
|
|
|
rapid2_source->attach(m_context);
|
|
|
|
reset_timing_run_main ();
|
|
}
|
|
|
|
void
|
|
TimerTest::connect_idle_handler ()
|
|
{
|
|
const Glib::RefPtr<Glib::IdleSource> idle_source = Glib::IdleSource::create();
|
|
idle_source->connect(sigc::mem_fun(*this, &TimerTest::on_idle_handler));
|
|
|
|
idle_source->attach(m_context);
|
|
}
|
|
|
|
void
|
|
TimerTest::connect_quit_timeout ()
|
|
{
|
|
const Glib::RefPtr<Glib::TimeoutSource> quit_source = Glib::TimeoutSource::create(test_length_ms());
|
|
quit_source->connect(sigc::mem_fun(*this, &TimerTest::on_quit_handler));
|
|
|
|
quit_source->attach(m_context);
|
|
}
|
|
|
|
void
|
|
TimerTest::testTimers ()
|
|
{
|
|
m_current_test_name = "testTimers";
|
|
_testTimers ();
|
|
}
|
|
|
|
void
|
|
TimerTest::_testTimers ()
|
|
{
|
|
m_context = Glib::MainContext::create ();
|
|
|
|
PBD::StandardTimer second_timer (second_timer_ms (), m_context);
|
|
sigc::connection second_connection = second_timer.connect (sigc::mem_fun (this, &TimerTest::on_second_timeout));
|
|
|
|
if (m_connect_idle) {
|
|
connect_idle_handler ();
|
|
// let the idle handler run as fast as it can
|
|
reset_timing_run_main();
|
|
}
|
|
|
|
PBD::StandardTimer fast_timer (fast_timer_ms (), m_context);
|
|
sigc::connection fast_connection = fast_timer.connect (sigc::mem_fun (this, &TimerTest::on_fast_timeout));
|
|
|
|
reset_timing_run_main();
|
|
|
|
PBD::StandardTimer rapid1_timer (rapid1_timer_ms (), m_context);
|
|
sigc::connection rapid1_connection = rapid1_timer.connect (sigc::mem_fun (this, &TimerTest::on_rapid1_timeout));
|
|
|
|
reset_timing_run_main();
|
|
|
|
PBD::StandardTimer rapid2_timer (rapid2_timer_ms (), m_context);
|
|
sigc::connection rapid2_connection = rapid2_timer.connect (sigc::mem_fun (this, &TimerTest::on_rapid2_timeout));
|
|
|
|
reset_timing_run_main();
|
|
}
|
|
|
|
void
|
|
TimerTest::testTimersIdleFrequency ()
|
|
{
|
|
m_current_test_name = "testTimersIdleFrequency";
|
|
_testTimersIdleFrequency ();
|
|
}
|
|
|
|
void
|
|
TimerTest::_testTimersIdleFrequency ()
|
|
{
|
|
m_block_idle = false;
|
|
m_connect_idle = true;
|
|
|
|
_testTimers ();
|
|
|
|
m_block_idle = false;
|
|
m_connect_idle = false;
|
|
}
|
|
|
|
void
|
|
TimerTest::testTimersBlockIdle ()
|
|
{
|
|
m_current_test_name = "testTimersBlockIdle";
|
|
_testTimersBlockIdle ();
|
|
}
|
|
|
|
void
|
|
TimerTest::_testTimersBlockIdle ()
|
|
{
|
|
m_block_idle = true;
|
|
m_connect_idle = true;
|
|
|
|
_testTimers ();
|
|
|
|
m_block_idle = false;
|
|
m_connect_idle = false;
|
|
}
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
void
|
|
TimerTest::testGlibTimeoutSourcesHR ()
|
|
{
|
|
CPPUNIT_ASSERT(set_min_timer_resolution());
|
|
|
|
m_current_test_name = "testGlibTimeoutSourcesHR";
|
|
_testGlibTimeoutSources ();
|
|
|
|
CPPUNIT_ASSERT(reset_timer_resolution());
|
|
}
|
|
|
|
void
|
|
TimerTest::testGlibmmSignalTimeoutsHR ()
|
|
{
|
|
CPPUNIT_ASSERT(set_min_timer_resolution());
|
|
|
|
m_current_test_name = "testGlibmmSignalTimeoutsHR";
|
|
_testGlibmmSignalTimeouts ();
|
|
|
|
CPPUNIT_ASSERT(reset_timer_resolution());
|
|
}
|
|
|
|
void
|
|
TimerTest::testGlibmmTimeoutSourcesHR ()
|
|
{
|
|
CPPUNIT_ASSERT(set_min_timer_resolution());
|
|
|
|
m_current_test_name = "testGlibmmTimeoutSourcesHR";
|
|
_testGlibmmTimeoutSources ();
|
|
|
|
CPPUNIT_ASSERT(reset_timer_resolution());
|
|
}
|
|
|
|
void
|
|
TimerTest::testTimersHR ()
|
|
{
|
|
CPPUNIT_ASSERT(set_min_timer_resolution());
|
|
|
|
m_current_test_name = "testTimersHR";
|
|
_testTimers ();
|
|
|
|
CPPUNIT_ASSERT(reset_timer_resolution());
|
|
}
|
|
|
|
void
|
|
TimerTest::testTimersIdleFrequencyHR ()
|
|
{
|
|
CPPUNIT_ASSERT(set_min_timer_resolution());
|
|
|
|
m_current_test_name = "testTimersIdleFrequencyHR";
|
|
_testTimersIdleFrequency ();
|
|
|
|
CPPUNIT_ASSERT(reset_timer_resolution());
|
|
}
|
|
|
|
void
|
|
TimerTest::testTimersBlockIdleHR ()
|
|
{
|
|
CPPUNIT_ASSERT(set_min_timer_resolution());
|
|
|
|
m_current_test_name = "testTimersIdleFrequencyHR";
|
|
_testTimersBlockIdle ();
|
|
|
|
CPPUNIT_ASSERT(reset_timer_resolution());
|
|
}
|
|
|
|
#endif
|