Real-Time Data Plotting Performance Issues on Jetson Orin NX: GPU Utilization Concern
-
I’m running a real-time data plotting application on a Jetson Orin NX 16GB with Qt 6.8.0 QML, where I update a chart with 10,000 data points every 30ms. This results in high CPU usage, ranging from 60-65%. When I remove the chart component from the application, the CPU usage drops to 2-3%, indicating that the chart rendering is likely causing the performance bottleneck. On clicking any button on the UI increase CPU load by 30% which looks not right.
I am using minimal image on the board so make sure the resources are not over-utilised. Below is the code which I am using to plot real-time data-
ChartView { id: chartView Layout.fillWidth: true Layout.fillHeight: true anchors.fill: parent legend.visible: false antialiasing: false theme: ChartView.ChartThemeLight property real xMarg: 0.2*chartView.width property real yMarg: 0.065*chartView.height ValuesAxis { id: axisX titleText: " IF Frequency (MHz)" labelFormat: "%.2f" min: 0 max: 100 } ValuesAxis { id: axisY titleText: "Magnitude (dB)" labelFormat: "%.1f" min: -50 max: 0 tickCount: 6 } LineSeries { id: series1 name: "Analysis" useOpenGL: true axisX: axisX axisY: axisY } axes: [axisX, axisY] } Connections { target: zmqSubscriber function onDataReceived(frequencyList, magnitudeList, maxMagnitude, maxMagnitudeFreq) { series1.clear(); // Append data to the series for (var i = 0; i < frequencyList.length; i+= 5) { series1.append(frequencyList[i], magnitudeList[i]); } } }
Appreciate any pointers.
-
@bridge12
I know nothing about QML. If you can do what @jeremy_k has said and access the series direct from C++ it is worth a try, at least to see if it makes any difference. You are adding a single point at a time. I believe it has been remarked in the past that this may be "slow". From C++, but apparently not from QML, there is a method void QXYSeries::append(const QList<QPointF> &points). Or better still since you are clearing and then adding points there is void QXYSeries::replace(const QList<QPointF> &points). And that saysNote: This is much faster than replacing data points one by one, or first clearing all data, and then appending the new data. Emits pointsReplaced when the points have been replaced.
Actually I see these functions state
Note: This function can be invoked via the meta-object system and from QML. See Q_INVOKABLE.
so maybe you can call them from QML directly.
I believe we have reports in the past that this approach behaves a lot better than individual
append()
s. Since you pick out every 5th point it is worth first building your own list from the pairs and then passing that toreplace()
. Let us know if that improves. -
@bridge12
I know nothing about QML. If you can do what @jeremy_k has said and access the series direct from C++ it is worth a try, at least to see if it makes any difference. You are adding a single point at a time. I believe it has been remarked in the past that this may be "slow". From C++, but apparently not from QML, there is a method void QXYSeries::append(const QList<QPointF> &points). Or better still since you are clearing and then adding points there is void QXYSeries::replace(const QList<QPointF> &points). And that saysNote: This is much faster than replacing data points one by one, or first clearing all data, and then appending the new data. Emits pointsReplaced when the points have been replaced.
Actually I see these functions state
Note: This function can be invoked via the meta-object system and from QML. See Q_INVOKABLE.
so maybe you can call them from QML directly.
I believe we have reports in the past that this approach behaves a lot better than individual
append()
s. Since you pick out every 5th point it is worth first building your own list from the pairs and then passing that toreplace()
. Let us know if that improves. -
@bridge12 said in Real-Time Data Plotting Performance Issues on Jetson Orin NX: GPU Utilization Concern:
I am using QLineSeries directly..
You aren't in the code you posted. You are using
LineSeries
in QML.The idea is to write a C++ function that accepts
QLineSeries*
as a parameter and appends to the series. You then expose that to QML and pass the id of yourLineSeries
object (series1
in your case) as the argument to the exposed C++ function. The point is that all of the appending logic now happens on the C++ side.In addition @JonB is absolutely correct that the
replace
function is much faster.I will check how i can do that with LineSeries from C++.. It sounds like a nice idea. Do you have any reference link?
Look at the oscilloscope example in the QML examples. Follow the pattern that it demonstrates.
-
@Bob64 @JonB @jeremy_k I realised I haven't updated the code under which I had updated data points in the backend. Below is the code for reference..
updateSeries gets called wherever set of datapoints are received over zmq, which is ypically every 50ms and approx 10,000 datapoints.
QList<QPointF> m_points; // Member Variable
void ZmqSubscriber::updateSeries(std::vector<double> frequencyArray, std::vector<double> relativeMagnitudeArray) { QThreadPool::globalInstance()->start([=]() { QMutexLocker locker(&m_mutex); int size = frequencyArray.size(); m_points.reserve(size / 5); // Append the new data depending on the type for (int i(0); i < size; i += 5) { m_points.append(QPointF(frequencyArray[i], relativeMagnitudeArray[i])); } emit seriesChanged(); }); } void ZmqSubscriber::update(QLineSeries *series) { // Ensure the series pointer is valid if (!series) return; // Use QThreadPool to offload work to a different thread QThreadPool::globalInstance()->start([this, series]() { QMutexLocker locker(&m_mutex); auto pointsCopy = m_points; // Use the main thread to update the UI QMetaObject::invokeMethod(series, [series, pointsCopy]() { if (series) { series->replace(pointsCopy); // Append all points in one go } }, Qt::QueuedConnection); // Ensure this runs on the main (UI) thread }); }
This is how my QML looks like
Connections { target: zmqSubscriber function onSeriesChanged() { zmqSubscriber.update(series1); chartView.update() } }
Issue still persists and this haven' helped me a bit.
It seems that the issue is that the user interface (UI) updates are constrained to the main thread, which shifts the load of updating the chart with 10,000 data points to the main thread, regardless of other optimizations. This likely results in performance bottlenecks, as the chart update logic cannot run in parallel with the rest of the application, placing a heavy burden on the main thread. Not sure if my understanding is correct.
-
@JoeCFD We are looking to capture a peak in the data, which can occur at any point across the dataset. While we applied a filter on the sender side, it seems we've optimized that as much as possible, and there’s little room for further improvement on that end.
-
@bridge12
Sorry thatreplace()
has not helped enough.FYI the solution at https://stackoverflow.com/a/78548419/489865 recommends using
replace()
. Look at the timings (admittedly from Python) reported:for 50000 values it takes only 126 ms, for 5000 values it takes 13 ms
At that rate it will only just keep up with your original "10,000 data points every 30ms". (Better with your newer "50ms and approx 1000 datapoints.)
Not leaving much time for anything else.
With the proviso that I know nothing about your hardware or QML. I don't know whether one should expect a graph to keep up with 300,000 points per second nor whether this many makes much sense for a user looking at the graph updates in real time. Nor do I know what "clicking any button on the UI increase CPU load by 30% which looks not right" is telling you.
What I will say is that now you are Qt 6.8 https://doc.qt.io/qt-6/qtcharts-index.html says
Note: The Qt Charts module is in the maintenance phase. For new projects, consider using the Qt Graphs module. To render 2D charts, QtGraphs uses Qt Quick Shapes, which is a more modern GUI technology compared to the outdated Qt Graphics View Framework used by the Qt Charts module.
If this can be used from QML (I do not know, ah https://doc.qt.io/qt-6/qtgraphs-index.html#qtgraphs-qml-api says it can) maybe as you say this would replace the apparent rendering issue of Qt Charts?
-
@JonB It appears the problem was in my testing after updating the latest code here (Also it was always 10,000 data points, I wrongly wrote it as 1000.. I over-worked this issue. I will mak an edit. ). CPU usage has come down to 18% from 60%. I had done following things-
-
Moved LineSeries update to backend
-
LineSeres clear + append is replaced with replace
-
Qt is upgraded to 6.8.0
I am not sure which one out of this helped but results look better now. Thank you so much everyone who spent their efforts to read my long question and provide amazing ideas. Appreciate it.
-
-