Improve signal/slot latency between threads
-
Hi,
to display rectangles, received from a subsystem, in the UI, I created a thread which receives and parses this data. After parsing, the thread emits a signal, which the main thread receives and calls the NOTIFY signal of a QProperty, so that the UI refreshes and redraws the rectangles with a repeater. The data will be sent from the subsystem about every 10-15ms. I have to show the updates as fast as possible in the UI. After doing some measurements, I noticed a big delay between emitting the signal in the thread and the slot starting in the main thread - about ~40ms on average. Drawing the rectangles in the UI takes about eight additional milliiseconds, depending on the count.I tried many things to improve this. I am using a QueuedConnection. Having parameters in the signal/slot seems to not really matter. I also set the thread priority to TimeCriticalPriority - no change. I also tried drawing the rectangles to a QImage with a QPainter in the thread, but once done, I have to emit a signal, so the QImage updates, which again has the same delay.
The UI refreshes at the display refresh rate, so 60Hz -> every ~17ms, but as the subsystem also needs some time to do calculations before sending it to QT, I need to display the rectangles as fast as possible to keep the overall time as near to real time as possible.
Every help on improving this is highly appreciated!
-
When a queued slot connection is invoked, it (roughly speaking) goes to the back of the target object's thread's event queue. The slot is invoked only after all the events that are in queue before it get handled - one at a time, of course, since it is a single thread's events.
If the time for the slot event to reach the front of the queue (also known as "event loop lag") is too high, it is because there are many events in the queue and/or handing them is slow. The reasons for that are practically infinite, and it is hard to say without much more information, but it might be a UI element that gets repainted too often (e.g. due to unnecessary or too wide calls to
update()
), or maybe a different widget that takes long to paint (re-doing text shaping is a not uncommon culprit), or maybe a O(n^2) algorithm hiding in a slot, or maybe large data being copied inadvertently (perhaps due to unnecessarily causing Qt's implicitly shared collections to detach) , or maybe god forbid blocking I/O or sleeps somewhere.The best tool to diagnose such things is an instrumenting profiler (e.g. callgrind), GammaRay can also provide some specific insight about event handling, and you can always improvise something with a application event filter.
Good luck!
-
Here are a few questions that I ask myself in similar sounding situations:
- Does the receive and parse operation really need an independent thread? Most of the threading questions on this board are rooted in a lack of familiarity with asynchronous programming.
- Do the events occur at a steady interval? If so, a timer in the UI thread plus a thread safe structure for communication may provide lower and more predictable latency.
- Does every update need to be displayed? If not, consider compression via averaging or throwing out samples.
-
Due to the persistence of human vision, a refresh rate above 23 frames per second is generally sufficient for animation. In a previous program, our approach was to set up a shared cache, where no matter how fast the cache refresh rate was (even 1000 times/second), it would only fetch 25 times per second on the UI.