QCustomPlot Discussion and Comments

How to use multiple cores of CPU to plot graphsReturn to overview

Hi,

I need to plot two realtime graphs with more than 10 thousand points each. As soon as data crosses 10 thousand points my CPU core precentage is reaching 100% and the GUI is not responding.

I'm using
QVector<double> a[1],b[1];

a[0]=recent value; //receives in realtime
ui->Graph1->graph(0)->addData(a[0],b[0]);
ui->Graph2->graph(0)->addData(a[0],b[0]);
ui->Graph2->replot();
ui->Graph1->replot();

Please suggest me how to enhance my GUI replot(),
1.) Can I run my GUI on multiple Cores, as one core reaches 100% why GUI is not Using 2nd CPU core?
2.) Can we use Qt threads to plot different graphs?


Thank You.

Hi if you really add points one by one and refresh after each added point, then forget about CPU parallelization and try to reduce replots. You also use the addData() method which isn't really efficient for your use case.
You should try to group your calls to addData() and replot() to work with packets of 100 of more points.

The best way would be rewriting QCustomPlot to use OpenGL rendering, so your CPU can chill out.

In case it's of interest: OpenGL rendering is already implemented again for the upcoming QCP2.0. (The old OpenGL rendering was broken with Qt5 because the Qt devs changed a lot with regard to OGL). With Qt 4, OpenGL is available as it has always been for QCustomPlot (see performance improvement page in documentation).

As was mentioned earlier, realtime data acquisition shouldn't be done like in your code, i.e. "add one point and immediately replot". You should decouple adding points (preferably add them in batches of 10, 100 or more) and call replot e.g. in a timer slot at regular intervals depending on your target frame rate. Also watch out for common pitfalls for performance. Especially avoid transparent brushes, thick lines (see performance improvement documentation). With this in mind I have a data acquisition software using QCustomPlot which easily handles acquisition rates beyond 10kHz with hundred thousands to millions of data points in the plot. Finally, if nothing else helps and the hardware is the limit, restrict your x axis to show fewer of your acquired data. QCP can very efficiently optimize away data points that are outside the visible key axis range.

Hello, DerManu
did i understand correctly that in your software there are millions of points in the plot, and you replot all of them 10000 times per sec?
or just acquire them with this speed?

@ba4o: No, your screen only updates 60 times a second so it wouldn't make any sense to replot at 10kHz (apart from the fact that probably no PC hardware right now would achieve that kind of rate). That's what I was actually trying to explain above: Decouple data acquisition (10kHz) from replotting (60Hz).

Hi,
I would also add as DerManu said that even your brain wouldn't see anything. Instruments like oscilloscope have refresh rate above 50Hz even with sub GHz sample rate. Your CPU usage will be almost divided in the same way you divide the number of screen refresh when you go from 1 refresh per point to 1 refresh each 100-1000 points.
Alexis.

I had the same problem. I have four plots in one window what needs update every 500ms and each call to replot takes about 600ms on my xeon server at 3 ghz (!!!).


but i found a solution. it requires modifying the source code, and is not truely running from a secondary thread. The thing is, only calls made to QWidget to paint the plot requires to me made from the Gui thread. other processing can be made in different thread. So here is the mod based on the beta release of v2.0:

qcustomplot.h: create a overload of the replot() function in line 3698. this new overload has a bool variable which when false, will draw the plot in the buffer, and when true will draw the already buffered plot onto the window by calling repaint() function in QWidget.

Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint); //orginal code
Q_SLOT void replot(bool step, QCustomPlot::RefreshPriority refreshPriority = QCustomPlot::rpRefreshHint); //added code

qcustomplot.cpp: define the new overload
void QCustomPlot::replot(bool step, QCustomPlot::RefreshPriority refreshPriority)
{
    if (mReplotting) // incase signals loop back to replot slot
        return;
    mReplotting = true;
    if (!step)
    {
        emit beforeReplot();
 
        updateLayout();
        // draw all layered objects (grid, axes, plottables, items, legend,...) into their buffers:
        setupPaintBuffers();
        foreach(QCPLayer *layer, mLayers)
            layer->drawToPaintBuffer();
 
 
        for (int i = 0; i < mPaintBuffers.size(); ++i)
            mPaintBuffers.at(i)->setInvalidated(false);
    }
    else
    {
        if ((refreshPriority == rpRefreshHint && mPlottingHints.testFlag(QCP::phImmediateRefresh)) || refreshPriority == rpImmediateRefresh)
            repaint();
        else
            update();
 
        emit afterReplot();
    }
    mReplotting = false;
}

call from GUI thread will look like this:

void Class::CUpdatePlots(mca_plots plot_data, Class *pt)
{
    QMutexLocker ml(&pt->mutex);
    QFuture<void> future[4];
    for (int ch = 0; ch < 4; ch++)
    {
        future[ch] = QtConcurrent::run(Class::CPlots, ch,
            QVector<double>::fromStdVector(v1), QVector<double>::fromStdVector(v2), pt);
    }
    future[3].waitForFinished();
    return;
}
void Class::CPlots(int ch, QVector<double> va1, QVector<double> va2, Class *pt)
{
    pt->ptype[ch].at(0)->setData(pt->xData[ch], va1);
    pt->ptype[ch].at(1)->setData(pt->xData[ch], va2);
    pt->ptype[ch].at(2)->setData(pt->xData[ch], va1);
    pt->ptype[ch].at(3)->setData(pt->xData[ch], va2);
    /*if (ch == 3)
        pt->cbType(pt->ui.cmbType->currentIndex());*/
    pt->plots->at(ch)->replot(false);
    return;
}

at this point all the layers of the plot have been made in the buffer. all you have to do is call onto the
pt->plots->at(ch)->replot(true)
; from your GUI thread to make the plot reapear magically. takes about 50ms to put out the image. no more stuck GUI!

@Rashed thank you from 2018!!!!!!!!