PDA

View Full Version : Multiple QNetworkRequest slows the GUI performance



deepikha
20th October 2020, 10:38
Hello,
I am writing an application and I am requesting RestApi for multiple data via different URLs.
From the Mainwindow, I set a timer and call the functions for every one second. I have many functions like getData and I get the reply properly. But the response time is slow and the application itself is slow now. Even clicked on GUI Menu response time is slow. Am I doing anything wrong here?




//In MainWindow.cpp
void MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),UI(new Ui::MainWindow)
{
ui->setupUi(this);
Api *ap = new Api(this);
}
void MainWindow::updateValues()
{
QTimer* timer=new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(findUpdate()));
timer->start(1000);
}
//I call multiple functions from api
void MainWindow::findUpdate()
{

api->getData(url);
api->getData1(url);
}

//In api.h file QNetworkAcessManager manager;
//In api.cpp
void Api::getData(QString url)
{
QNetworkRequest request(QUrl(url));
QNetworkReply *reply = manager.get(request);
connect(reply,SIGNAL(finished()),this,SLOT(replyda ta()));
}
void Api::replyData()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(reply)
{
QString response = (QString)reply.realAll();
QJsonDocument json = QJsonDocument::fromJson(response.toUtf8());
list = json.array();
delete reply;
}
else
{
qDebug() << "Failure" << reply->errorString();
delete reply;
}
emit listOfValues(list);
}

void Api::getData1(QString url)
{
QNetworkRequest request(QUrl(url));
QNetworkReply *reply = manager.get(request);
connect(reply,SIGNAL(finished()),this,SLOT(replyda ta1()));
}
void Api::replyData1()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(reply)
{
QString response = (QString)reply.realAll();
QJsonDocument json = QJsonDocument::fromJson(response.toUtf8());
list = json.object();
delete reply;
}
else
{
qDebug() << "Failure" << reply->errorString();
delete reply;
}
emit listOfValues(list);
}

d

Thanks in advance,
Deepikha

d_stranz
20th October 2020, 16:05
From the Mainwindow, I set a timer and call the functions for every one second. ... But the response time is slow

Why are you -polling- for new data every second? Sounds like you are bombarding the URLs with requests for updates before they have time to finish with the previous ones. So requests keep stacking up and you probably keep receiving the same data every second. Is there actually new data available once per second from every URL?

I think you should make a bit of a redesign. Instead of polling all URLs every second, you should ask for data, wait until you receive the reply, then wait another second before asking again. Something like this:



For each URL:
- create the request in the getData() slot
- connect the finished() signal to your replyData() slot
- in replyData(), retrieve the reply
- if the loop should keep running, then:
- create a one-shot QTimer with a 1 second timeout
- connect the timer's timeout() signal to the getData slot()
- otherwise, do nothing and the loop exits


Pay attention to the For each URL above. Because every server might take a different amount of time to reply, you have to treat each server independently. You can't use the same QTimer for all of them, you need a separate timer for each one, and that timer can only be started after the server has finished giving you the reply to the last request you sent.

It would be even better if there was a way to ask your servers if they have new data and only then go get it. If there is such a mechanism, then you can probably get rid of timers completely. In this case, you have two requests and two finished slots for each URL: first make the "is new data ready" request. That request will "hang" until the server returns its finished signal, at which point you can send the request for data. Instead of starting a QTimer at that point, you just ask to be notified when the data has changed again. With that mechanism, you are only asking for data when the server actually has something new to give you, not every second whether the data has changed or not.

deepikha
22nd October 2020, 16:43
Thank you for the reply. In my application I have multiple LEDs(144) and Plots(44). These LEDs and Plots should be update every one second. Plot data will be updated every second in the server. LED status should also be updated every one second to get the status of the application running on server, So I used single QTimer to trigger every second. Currently LEDs and Plots are getting updated in the GUI every 4 to 5 seconds. Clicking on the Menus on GUI will take around 7 seconds to open the menu.

One getData will give 3 LEDs value and for few getData1() will give 5 LEDs value.
Structure of my Application
I have 3 Classes
each class has LEDs and Plots
each class will call updateLED() and updatePlots() every second via QTimer in the Mainwindow




//In Mainwindow
void MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),UI(new Ui::MainWindow)
{
ui->setupUi(this);
Api *api = new Api(this);
Class1 *one = new Class1(this);
Class2 *two = new Class2(this);
Class3 *three = new Class3(this);
QTimer* timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(findUpdate()));
timer->start(1000);
}

void MainWindow::findUpdate()
{
QStringList listone = listOfdevice1;
QStringList listtwo = listOfdevice2;
QStringList listthree = listOfdevice3;

for(int i = 0; i < listone.size(); i++)
{
one->update(i);
one->updateLED(i);
}

for(int i = 0; i < listtwo.size(); i++)
{
two->updatePlot(i);
two->updateLED(i);
}

for(int i = 0; i < listthree.size(); i++)
{
three->updatePlot(i);
three->updateLED(i);
}
}

//In ClassOne
void ClassOne::update()
{
updatePlot();
updateLED();
}

void ClassOne::updatePlot()
{
api->getDataplotOne(url);
connect(api,&Api::getDataPlotReplyOne,this,&ClassOne::updatePlotValue);
}

void ClassOne::updateLED()
{
api->getDataLEDOne(url);
connect(api,&Api::getDataLEDOneReply,this,&ClassOne::updateLEDValue);
}

//In ClassTwo
void ClassTwo::update()
{
updatePlot();
updateLED();
}

void ClassTwo::updatePlot()
{
api->getDataplotTwo(url);
connect(api,&Api::getDataPlotTwoReply,this,&ClassTwo::updatePlotValue);
}

void ClassTwo::updateLED()
{
api->getDataLEDTwo(url);
connect(api,&Api::getDataLEDTwoReply,this,&ClassTwo::updateLEDValue);
}

void ClassThree::update()
{
updatePlot();
updateLED();
}

void ClassThree::updatePlot()
{
api->getDataplotThree(url);
connect(api,&Api::getDataPlotThreeReply,this,&ClassThree::updatePlotValue);
}



void ClassThree::updateLED()
{
api->getDataLEDThree(url);
connect(api,&Api::getDataLEDThreeReply,this,&ClassThree::updateLEDValue);
}

// In Api Class
void Api::getDataPlotOne(QString url)
{
QNetworkRequest request(QUrl(url));
QNetworkReply *reply = manager.get(request);
connect(reply,SIGNAL(finished()),this,SLOT(replyda ta1plot));
}
void Api::replyData1plot()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(reply)
{
QString response = (QString)reply.realAll();
QJsonDocument json = QJsonDocument::fromJson(response.toUtf8());
list = json.object();
delete reply;
}
else
{
qDebug() << "Failure" << reply->errorString();
delete reply;
}
emit getDataPlotReplyOne(list);
}

void Api::getDataLEDTwo(QString url)
{
QNetworkRequest request(QUrl(url));
QNetworkReply *reply = manager.get(request);
connect(reply,SIGNAL(finished()),this,SLOT(replyda ta2led));
}
void Api::replyData1led()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(reply)
{
QString response = (QString)reply.realAll();
QJsonDocument json = QJsonDocument::fromJson(response.toUtf8());
list = json.object();
delete reply;
}
else
{
qDebug() << "Failure" << reply->errorString();
delete reply;
}
emit getDataLEDReplyTwo(list);
}

void Api::replyData2plot()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(reply)
{
QString response = (QString)reply.realAll();
QJsonDocument json = QJsonDocument::fromJson(response.toUtf8());
list = json.object();
delete reply;
}
else
{
qDebug() << "Failure" << reply->errorString();
delete reply;
}
emit getDataPlotReplyTwo(list);
}

void Api::getDataLEDTwo(QString url)
{
QNetworkRequest request(QUrl(url));
QNetworkReply *reply = manager.get(request);
connect(reply,SIGNAL(finished()),this,SLOT(replyda ta2led));
}
void Api::replyData2led()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(reply)
{
QString response = (QString)reply.realAll();
QJsonDocument json = QJsonDocument::fromJson(response.toUtf8());
list = json.object();
delete reply;
}
else
{
qDebug() << "Failure" << reply->errorString();
delete reply;
}
emit getDataLEDReplyTwo(list);
}

//As per your suggestion I need to use multiple QTimer.
QTimer* timerOnePlot=new QTimer(this);
connect(timerOnePlot, SIGNAL(timeout()), this, SLOT(findUpdateOnePlot()));

//Now can you help me with a solution which wont effect my performance.

Thank you,
Deepikha

d_stranz
22nd October 2020, 17:25
//Now can you help me with a solution which wont effect my performance.

I gave you one. Read my post again. You cannot poll every LED every second if it takes more than one second for the LED to respond. You have to treat every LED (or group of LEDs) independently:

- Ask the LED group for its data
- Wait for the finished() signal
- Get the data and tell the plot to update
- Wait one second (for that LED group only)
- Repeat

What is the point of setting a performance goal (one second updates of everything) when your hardware cannot perform at that rate? Asking for data more often won't make it come in any faster.

deepikha
26th October 2020, 18:15
I gave you one. Read my post again. You cannot poll every LED every second if it takes more than one second for the LED to respond. You have to treat every LED (or group of LEDs) independently:

- Ask the LED group for its data
- Wait for the finished() signal
- Get the data and tell the plot to update
- Wait one second (for that LED group only)
- Repeat

What is the point of setting a performance goal (one second updates of everything) when your hardware cannot perform at that rate? Asking for data more often won't make it come in any faster.

Ok, I should send the next QNetworkRequest after I get a reply to the first request.

I have a single timer that will trigger the function findUpdate() every second in mainwindow. I have set an If condition, which will be set to 0 only when the reply is received. So is this efficient? Now my GUI performance is better but there is some response delay after 10mins. what would e the reason for the delay now?



//In Mainwindow
void MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),UI(new Ui::MainWindow)
{
ui->setupUi(this);
Api *api = new Api(this);
Class1 *one = new Class1(this);
Class2 *two = new Class2(this);
Class3 *three = new Class3(this);
QTimer* timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(findUpdate()));
timer->start(1000);
}

void MainWindow::findUpdate()
{
QStringList listone = listOfdevice1;
QStringList listtwo = listOfdevice2;
QStringList listthree = listOfdevice3;

for(int i = 0; i < listone.size(); i++)
{
one->update(i);
}

for(int i = 0; i < listtwo.size(); i++)
{
two->update(i);
}

for(int i = 0; i < listthree.size(); i++)
{
three->update(i);
}
}

//In ClassOne
void ClassOne::update()
{
if(updatePlotFinished==0)
{
updatePlot();
updatePlotFinished = 1;
}

if(updateLEDFinished==0)
{
updateLED();
updateLEDFinished = 1;
}
}

void ClassOne::updatePlot()
{
api->getDataplotOne(url);
connect(api,&Api::getDataPlotReplyOne,this,&ClassOne::updatePlotValue);
}

void ClassOne::updateLED()
{
api->getDataLEDOne(url);
connect(api,&Api::getDataLEDOneReply,this,&ClassOne::updateLEDValue);
}

void ClassOne::updatePlotValue(QJsonObject obj)
{
updatePlotFinished = 0;
//updating plot
}

void ClassOne::updateLEDValue(QJsonObject obj)
{
updateLEDFinished = 0;
//Updating LED status
}

//In ClassTwo
void ClassTwo::update()
{
if(updatePlotFinished==0)
{
updatePlot();
updatePlotFinished = 1;
}

if(updateLEDFinished==0)
{
updateLED();
updateLEDFinished = 1;
}
}

void ClassTwo::updatePlot()
{
api->getDataplotTwo(url);
connect(api,&Api::getDataPlotTwoReply,this,&ClassTwo::updatePlotValue);
}

void ClassTwo::updateLED()
{
api->getDataLEDTwo(url);
connect(api,&Api::getDataLEDTwoReply,this,&ClassTwo::updateLEDValue);
}


void ClassTwo::updatePlotValue(QJsonObject obj)
{
updatePlotFinished = 0;
//updating plot
}

void ClassTwo::updateLEDValue(QJsonObject obj)
{
updateLEDFinished = 0;
//Updating LED status
}
// In ClassThree
void ClassThree::update()
{
if(updatePlotFinished==0)
{
updatePlot();
updatePlotFinished = 1;
}

if(updateLEDFinished==0)
{
updateLED();
updateLEDFinished = 1;
}
}

void ClassThree::updatePlot()
{
api->getDataplotThree(url);
connect(api,&Api::getDataPlotThreeReply,this,&ClassThree::updatePlotValue);
}



void ClassThree::updateLED()
{
api->getDataLEDThree(url);
connect(api,&Api::getDataLEDThreeReply,this,&ClassThree::updateLEDValue);
}


void ClassTwo::updatePlotValue(QJsonObject obj)
{
updatePlotFinished = 0;
//updating plot
}

void ClassTwo::updateLEDValue(QJsonObject obj)
{
updateLEDFinished = 0;
//Updating LED status
}
// In Api Class
void Api::getDataPlotOne(QString url)
{
QNetworkRequest request(QUrl(url));
QNetworkReply *reply = manager.get(request);
connect(reply,SIGNAL(finished()),this,SLOT(replyda ta1plot));
}
void Api::replyData1plot()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(reply)
{
QString response = (QString)reply.realAll();
QJsonDocument json = QJsonDocument::fromJson(response.toUtf8());
list = json.object();
delete reply;
}
else
{
qDebug() << "Failure" << reply->errorString();
delete reply;
}
emit getDataPlotReplyOne(list);
}

void Api::getDataLEDTwo(QString url)
{
QNetworkRequest request(QUrl(url));
QNetworkReply *reply = manager.get(request);
connect(reply,SIGNAL(finished()),this,SLOT(replyda ta2led));
}
void Api::replyData1led()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(reply)
{
QString response = (QString)reply.realAll();
QJsonDocument json = QJsonDocument::fromJson(response.toUtf8());
list = json.object();
delete reply;
}
else
{
qDebug() << "Failure" << reply->errorString();
delete reply;
}
emit getDataLEDReplyTwo(list);
}

void Api::replyData2plot()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(reply)
{
QString response = (QString)reply.realAll();
QJsonDocument json = QJsonDocument::fromJson(response.toUtf8());
list = json.object();
delete reply;
}
else
{
qDebug() << "Failure" << reply->errorString();
delete reply;
}
emit getDataPlotReplyTwo(list);
}

void Api::getDataLEDTwo(QString url)
{
QNetworkRequest request(QUrl(url));
QNetworkReply *reply = manager.get(request);
connect(reply,SIGNAL(finished()),this,SLOT(replyda ta2led));
}
void Api::replyData2led()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if(reply)
{
QString response = (QString)reply.realAll();
QJsonDocument json = QJsonDocument::fromJson(response.toUtf8());
list = json.object();
delete reply;
}
else
{
qDebug() << "Failure" << reply->errorString();
delete reply;
}
emit getDataLEDReplyTwo(list);
}

d_stranz
26th October 2020, 19:18
I have a single timer that will trigger the function findUpdate() every second in mainwindow. I have set an If condition, which will be set to 0 only when the reply is received. So is this efficient?

No. Since you see your program gradually start to slow down, this probably means that server requests are backing up. More slowly than before, but your code is probably still making requests faster than they can be answered.

And how can you have -one- flag that applies to -all- servers? If you set it to 0 when -any- reply is received from -any- server, what about all of the other servers who have not replied yet? If -any- server can set the flag to zero, then when your timer fires it will ask -all- of them for an update even if some have still not replied. Your findUpdate() rate will be set by the -fastest- server, not the slowest, and so requests to the slow servers will back up.

Why are you so resistant to getting rid of the 1-second QTimer? It is the cause of the slowdown. Get rid of it. Your servers cannot all keep up with a 1-second request rate. If someone has specified that your program must update every second, you need to point out to them that this cannot be done.

What you can do is to get rid of QTimers completely. In the algorithm I posted:

- Ask the LED group for its data
- Wait for the finished() signal
- Get the data and tell the plot to update
- Wait one second (for that LED group only)
- Repeat

Remove the "Wait one second" line and immediately ask the same LED group again for an update. If you do this for every LED group independently it means that your UI will receive updates at the fastest rate possible - as fast as each LED group is capable of responding. You cannot make it work any faster than this unless you make the servers faster.

Your program should be completely asynchronous - you can't force each LED group to respond at the same rate, and should not try. The Qt way is to ask for something and let Qt tell when it is ready. If each LED group answers at a different rate, well that's just the reality of the situation and you can't force it to run faster. If you are on a bicycle, you aren't going to make it as fast as a car no matter how hard you pedal.

Edit:


If you still insist on using a QTimer in MainWindow, then you can make your findUpdate method work if you do this:

- create a vector of flags, one entry in the vector for each server.
- initialize the vector to zero for every entry

In findUpdate():
- look at the entries in the vector
- for any zero entry, issue the get request to that server and set the flag for that server to 1

In the finished() slot
- when you receive the data for that server's request, set its flag to 0

In findUpdate(), if any flags are set to zero, then you can issue a get request for those servers only. Any flags still set to 1 mean the request is still waiting, so you can't issue another one.

This way, you can update the UI once per second with whatever new information has come in during the last second, but there will not be new information for every server. (And in the algorithm I posted above, you will also get rid of the "Repeat" line. Each server will now be under the control of findUpdate and will be asked for new data at most once per second. The slowest server will still be slow.)