Large timer inaccuracy in recent versions of Gargoyle

While trying to implement some precise real-time behavior, I discovered that newer versions of Gargoyle (since at least version 2022) have occasional inaccuracies of up to 30 ms or more in timer events. Gargoyle 2011 - the default on older versions of Linux - does not seem to have this problem, and all of its timer events are accurate within 1 or 2 milliseconds of the correct value. Lectrote, similarly, has a very precise timer system.

Tracing through the source code reveals that Gargoyle passes control of its timer events to the Qt library’s timer API, which features two main timer modes - Qt::CoarseTimer and Qt::PreciseTimer. Only the latter attempts to be accurate to the millisecond. Indeed, making a one-line change to the source code forcing timers to use Qt::PreciseTimer seems to alleviate the problem, reducing inaccuracies to Gargoyle 2011 values without consuming excessive CPU time.

I’d like to collect some additional data about which versions of the interpreter are inaccurate, especially as the delay seems to be system-dependent. I’ve provided this brief Glulx test file which measures inaccuracies and reports some statistics about timer event delays.

Timer Analysis.gblorb (574.7 KB)

Interpreting the results

The program requests timer events every 1000 ms, and runs for 20 cycles. At the end, the average delay between timer events (which should be 1000 ms) and the standard deviation of the delay distribution (which should ideally be 0) are printed, as well as a bar graph of the results. The bar printed with ‘@’ and ‘.’ characters represents 1000 ms, which should be the center point of the graph.

If any of you would like to run it briefly on your system and post your findings (as a screenshot or preformatted text), I would greatly appreciate it.

2 Likes

I averaged 999ms with a standard deviation of 4702 μs.

But really, there’s no reason not to be using a precise timer. When I switched over to Qt, I didn’t even think of modifying the timer type. If you want to create a pull request, I’ll merge it in (both sndqt.cpp and sysqt.cpp use QTimer).

Otherwise, I’ll just switch them over myself.

Average: 996 ms
Standard deviation: 20385 μs

The --version option says it’s Gargoyle 2023.1. I built it myself on FreeBSD 13.2-RELEASE.

For 2023.1 (AppImage version):

Average: 1002 ms
Standard deviation: 10346 μs

For 2023.1 (locally compiled version with SDL sound, modified to use Qt::PreciseTimer):

Average: 1000 ms
Standard deviation: 788 μs

This was the modification that I made, for file sysqt.cpp (from 2023.1 source):

Window::Window() :
	m_view(new View(this)),
	m_timer(new QTimer(this)),
	m_settings(new QSettings(GARGOYLE_ORGANIZATION, GARGOYLE_NAME, this))
{
	m_timer->setTimerType(Qt::PreciseTimer); // ADDED
	connect(m_timer, &QTimer::timeout, this, [&]() {
	    m_timed_out = true;
	});
}