QCharts performance on Windows is abyssmal compared to Linux
-
I am running the same program on both Windows and Linux to test out QCharts. I am finding the Windows performance for the exact same code is terrible.
main.qml:import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 import QtCharts 2.15 import QtQml.Models 2.15 Window { id: window width: 800 height: 600 visible: true title: qsTr("Test sending lots of data to chart") property var model: [0,1,2,3] property var cdata property bool useOpenGL: true function genData(){ let data = [] for(let count=0; count<1024; ++count){ let val1 = Math.random() * 200 let val2 = Math.random() * 200 let val3 = Math.random() * 200 let val4 = Math.random() * 200 let vlist = [val1,val2,val3,val4] data.push(vlist) } window.cdata = data } Timer { id: datasimtimer interval: 1000 repeat: true running: true onTriggered: { genData() threshchart.updateData(cdata) } } ChartView { id: threshchart width: window.width height: window.height - 50 animationOptions: ChartView.NoAnimation property var seriesList: [] function updateData(cdata){ //console.log("updateData") // clear points for(let id in seriesList){ let series = seriesList[id] series.removePoints(0, series.count) } // add points) let xinc = xaxis.max/1024.0 let pointcount = 0 for(let count in cdata){ for(let subcount in cdata[count]){ let series = seriesList[subcount] series.append(xinc*count, cdata[count][subcount]) //series.insert(series.count, xinc*count, cdata[count][subcount]) pointcount += 1 } } //console.log("updateData", pointcount) } ValueAxis { id: xaxis min: 0 max: 200 } ValueAxis { id: yaxis min: 0 max: 200 } ValueAxis { id: xaxisthresh visible: false min: 0 max: 1 } LineSeries { id: thresholdseries name: "Thresh" axisX: xaxisthresh axisY: yaxis XYPoint {x:0; y: 100} XYPoint {x:1; y: 100} useOpenGL: window.useOpenGL } Instantiator { id: seriesinstancer model: window.model onObjectAdded: { let series = threshchart.createSeries(object.type, "Data %1".arg(index), xaxis, yaxis) series.useOpenGL = window.useOpenGL threshchart.seriesList.push(series) } QtObject { property var type: ChartView.SeriesTypeLine } } } Button { anchors.top: threshchart.bottom text: "Press" width: 100 height: 30 } }
main.cpp:
#include <QApplication> #include <QQmlApplicationEngine> int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication app(argc, argv); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }
It runs great on Linux and does not "pause" the user experience. It does pause and cause interface delays on Windows 10. I have run this on 2 different Windows 10 machines with updated NVidia graphics drivers. The Linux machine is running with default Linux drivers that come with Ubuntu 18.04. Without useOpenGL true the performance is much much worse on Windows and Linux.
I have tested this on 5.15.1, 5.15.2, and 5.15.3.
-
I restructured my data in qml to be linear point list and then updated via this function:
class PointUpdater : public QObject { Q_OBJECT public: PointUpdater(QObject* parent=nullptr) : QObject(parent) { } public slots: void update(QAbstractSeries *series, QVariantList points){ if(!series){ return; } QVector<QPointF> newpoints; for(QVariant point: points){ auto list = point.toList(); newpoints.push_back(QPointF(list.at(0).toDouble(), list.at(1).toDouble())); } QXYSeries *xySeries = static_cast<QXYSeries *>(series); xySeries->replace(newpoints); } };
I took this from the scope example. It updates very fast.
Edit:
We tested the upper bounds of this. Somewhere between 16K and 32K points updated every 100mS performance starts to degrade. So this is a vast improvement. -
I restructured my data in qml to be linear point list and then updated via this function:
class PointUpdater : public QObject { Q_OBJECT public: PointUpdater(QObject* parent=nullptr) : QObject(parent) { } public slots: void update(QAbstractSeries *series, QVariantList points){ if(!series){ return; } QVector<QPointF> newpoints; for(QVariant point: points){ auto list = point.toList(); newpoints.push_back(QPointF(list.at(0).toDouble(), list.at(1).toDouble())); } QXYSeries *xySeries = static_cast<QXYSeries *>(series); xySeries->replace(newpoints); } };
I took this from the scope example. It updates very fast.
Edit:
We tested the upper bounds of this. Somewhere between 16K and 32K points updated every 100mS performance starts to degrade. So this is a vast improvement. -
I believe you would have even better performance with
VXYModelMapper
, and your C++ could stay decoupled from your charting code (no manipulation of your series in your model code).