QtConcurrent run with a never ending while loop leads to high CPU usage
-
Building on this issue of updating GUI from another thread has led to high CPU usage issue.
To re-iterate, I have a
.mp4
file with 12000 frames. I loop through each frame and update the GUI. It looks something like this:DataBuffer Controller::read() { cv::Mat frameBgr; m_videoCapture.read(frameBgr); cv::Mat frameRgb; cv::cvtColor(frameBgr, frameRgb, cv::COLOR_BGR2RGB); m_dataBuffer.width = frameRgb.cols; m_dataBuffer.height = frameRgb.rows; m_dataBuffer.size = frameRgb.cols * frameRgb.rows; m_dataBuffer.data.assign(reinterpret_cast<const uchar*>(frameRgb.datastart), reinterpret_cast<const uchar*>(frameRgb.dataend)); return m_dataBuffer; }
void Controller::play() { while(m_isPlaying) { // get frames from file // update GUI with frame emit showFrameOnWidget(read()); // QueuedConnection to renderFrame() slot // update timestamp label emit updateTimestamp(m_timestamp); // QueuedConnection to updateTimestamp() slot } }
void MainController::renderFrame(Databuffer frame) { // process frame and extract all channels // lots of computation // emit render signals to be displayed on QLabel emit renderRGB(channel); emit renderConfidence(channel); emit renderDepth(channel); }
The function
play()
runs in a different thread created usingQtConcurrent::run
. When I start playing, my CPU usage hovers around~70%
.I have tried using
QThread::msleep(sleepTime)
inside the loop, but I do not feel this is the right approach. CPU usage with sleep time comes down to~40%
. I ran the same file on VLC mediaplayer (another Qt product) and the CPU consumption was almost0%
. How do they achieve this?Is using
QtConcurrent::run()
in this case wrong? Any other approach to deal with this? -
What's the size of the data being emitted by the showFrameOnWidget signal? This is going to be copied to the event queue for the receiving thread.
Rather than emitting the frame itself, presuming it isn't a trivial size, I would put it into a shared buffer and then emit something representing an index or pointer into the buffer. QSharedData and the associated pointer classes are an option. A ring buffer that avoids dynamic memory in the critical path is another.
If introducing some latency is acceptable, batching several frames per signal emission could also help smooth the interface.
-
@jeremy_k as you suggested I moved on to using a pointer to the buffer instead of the whole buffer. It is of course a better alternative to my existing code. But didn't improve the high CPU usage issue.
When I introduce
QThread::msleep(33)
for a 30 fps video, the CPU usage hovers around~30-40%
. As far as I understood, that means the receiver thread/slot (GUI) is overwhelmed by the worker thread so much so that the GUI thread is not able to keep up with the worker thread.I still believe thread sleep is a bad design. But how do developers in Qt world go around this issue?
-
Hi,
@MarKS said in QtConcurrent run with a never ending while loop leads to high CPU usage:
void MainController::renderFrame(Databuffer frame)
{
// process frame and extract all channels
// lots of computation// emit render signals to be displayed on QLabel emit renderRGB(channel); emit renderConfidence(channel); emit renderDepth(channel);
}
Why are you emitting three different signals with the exact same data ?
From the looks of it, you should rather have three slots connected to one single signal. -
void Controller::play() { while(m_isPlaying) { // get frames from file // update GUI with frame emit showFrameOnWidget(read()); // update timestamp label emit updateTimestamp(m_timestamp); } }
As play() function is part of while loop without sleep how do you ensure fps?
At present it looks like as soon as data read from file is ready signal is emitted, which is causing high cpu usage.
Do a comparison of how much time Same file taking to play in VLC and your application? -
@nagesh said in QtConcurrent run with a never ending while loop leads to high CPU usage:
As play() function is part of while loop without sleep how do you ensure fps?
I missed that the source is a file and not a camera producing frames at a fixed rate.
!
A timer or blocking IO of some sort is going to be necessary if the goal isn't to be gated by the io, cpu, or memory capacity of the system. Sleeping a fixed interval is a problematic solution because it fails to take into account variability in available capacity. Either set a high precision timer for the desired frame rate, or cause the processing thread to block between each frame while the ui thread deals with it.
edit: Or use an interface such as QVideoFilterRunnable that is invoked in a manner suitable for playback.
-
@nagesh I think you misunderstood my question. I know to ensure FPS and deal with high CPU usage, I need some way to slow down the thread. I asked for a better way to deal with it rather than using thread sleep which in my opinion, not a good design.
-
@jeremy_k You're right. So, I made a comparison between using a
QtConcurrent::run()
andQTimer
. Here are my observations:-
QtConcurrent::run()
is very fast. So I usedQThread::msleep(mstime)
. mstime is calculated from the native fps. For e.g. 30 fps native needs 33 mstime between the frames.CPU Usage: ~40-45%
-
Calling
play()
usingQTimer->start(mstime)
where mstime is 33 ms leads toCPU usage to ~30-35%
It looks like
QTimer
gives GUI a bit of breather to process events but runs slower than 30 fps playback speed. Whereas,QtConcurrent::run()
achieves 30 fps playback speed but drives higher CPU usage.It means
QTimer
sleep interval doesn't guarantee accuracy even though I am usingQt::PreciseTimer
. Please correct me if I am wrong. I do not actually understand how theQTimer
functions.But for now, I am stuck between 2 choices.
-
-
@MarKS Qt::PreciseTimer guarantees accuracy up to 1msec, but in that slot what operation is performed that matters.
Calling play() using QTimer->start(mstime) where mstime is 33 ms leads to CPU usage to ~30-35%
Does this timer is running in GUI/main context?
try to create the worker object having Signal/slot and move it to thread context using movetothread..
This might reduce the CPU usage
-
@MarKS said in QtConcurrent run with a never ending while loop leads to high CPU usage:
And yes each signal has its own slot.
This was not what @SGaist said - he said you should not emit three different signals with the same data but only one.
-
@Christian-Ehrlicher I understood. What I meant was 3 different signals are emitted with 3 different channels to 3 different slots. I put the same channel just as an example.