13
0
livetrax/libs/pbd/test/timer_test.cc
Tim Mayberry 1da30faf7f Add tests that show the timing differences between the Glib/mm based timeouts
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
2015-01-01 19:04:09 +07:00

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