Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Real-Time Data Plotting Performance Issues on Jetson Orin NX: GPU Utilization Concern
QtWS25 Last Chance

Real-Time Data Plotting Performance Issues on Jetson Orin NX: GPU Utilization Concern

Scheduled Pinned Locked Moved Solved QML and Qt Quick
jetson orin nxperformanceqml
10 Posts 5 Posters 819 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    bridge12
    wrote on last edited by
    #1

    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.

    JonBJ 1 Reply Last reply
    0
    • B bridge12

      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.

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by
      #3

      @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 says

      Note: 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 to replace(). Let us know if that improves.

      1 Reply Last reply
      1
      • jeremy_kJ Offline
        jeremy_kJ Offline
        jeremy_k
        wrote on last edited by
        #2

        Have you tried manipulating the LineSeries from C++, or using QLineSeries directly?

        Asking a question about code? http://eel.is/iso-c++/testcase/

        B 1 Reply Last reply
        1
        • B bridge12

          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.

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by
          #3

          @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 says

          Note: 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 to replace(). Let us know if that improves.

          1 Reply Last reply
          1
          • jeremy_kJ jeremy_k

            Have you tried manipulating the LineSeries from C++, or using QLineSeries directly?

            B Offline
            B Offline
            bridge12
            wrote on last edited by
            #4

            @jeremy_k I am using QLineSeries directly.. I will check how i can do that with LineSeries from C++.. It sounds like a nice idea. Do you have any reference link?

            JoeCFDJ B 2 Replies Last reply
            0
            • B bridge12

              @jeremy_k I am using QLineSeries directly.. I will check how i can do that with LineSeries from C++.. It sounds like a nice idea. Do you have any reference link?

              JoeCFDJ Offline
              JoeCFDJ Offline
              JoeCFD
              wrote on last edited by
              #5

              @bridge12 Updating 10,000 data points every 30ms might not be a good idea. Your eyes can not see the changes in such quick update. It might be better to add some filters for update.

              B 1 Reply Last reply
              1
              • B bridge12

                @jeremy_k I am using QLineSeries directly.. I will check how i can do that with LineSeries from C++.. It sounds like a nice idea. Do you have any reference link?

                B Offline
                B Offline
                Bob64
                wrote on last edited by
                #6

                @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 your LineSeries 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.

                B 1 Reply Last reply
                2
                • B Bob64

                  @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 your LineSeries 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.

                  B Offline
                  B Offline
                  bridge12
                  wrote on last edited by bridge12
                  #7

                  @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.

                  JonBJ 1 Reply Last reply
                  0
                  • JoeCFDJ JoeCFD

                    @bridge12 Updating 10,000 data points every 30ms might not be a good idea. Your eyes can not see the changes in such quick update. It might be better to add some filters for update.

                    B Offline
                    B Offline
                    bridge12
                    wrote on last edited by
                    #8

                    @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.

                    1 Reply Last reply
                    0
                    • B bridge12

                      @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.

                      JonBJ Offline
                      JonBJ Offline
                      JonB
                      wrote on last edited by JonB
                      #9

                      @bridge12
                      Sorry that replace() 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?

                      B 1 Reply Last reply
                      1
                      • JonBJ JonB

                        @bridge12
                        Sorry that replace() 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?

                        B Offline
                        B Offline
                        bridge12
                        wrote on last edited by bridge12
                        #10

                        @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.

                        1 Reply Last reply
                        2
                        • B bridge12 has marked this topic as solved on

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved