0

We are facing a performance issue in our project. We are using Qt/QML and C++ backend and we are working on the Grid Buttons. If we are changing or updating the data model the repeater section takes more delay to update all the buttons so UI is very slow. Based on the number of buttons the delay gets increased, if 64 buttons are updated means it took 6-sec to load.

when we try to update the data or copy an array of objects into the model container (data Model) of Repeater, it consumes more time to copy all items. For e.g., 30 objects consuming 2 sec. If object counts are increased, then the time consumed also increases proportionally.

As per console debug print, After executing this line console.log("GRID update data model"), it takes 2 sec to execute the next command.

How to avoid this delay in repeater?

  • Target Processor Name: IMX537
  • Qt version: Qt 5.15.2 (GCC 9.3.0, 64 bit)

Note: this delay issue happened in target device only. In local compilation did not see this delay.

Code:

 function handleModelChanged() {
        delegateModel.model = areaViewModel;
        const newUiType = areaViewModel.uiType
        if (newUiType !== uiType || !modelDataIsEqual(delegateModel, dataModel) ) {
            var buttons = [] 

            for (var row = 0; row < delegateModel.model.rowCount(); row++) {
                var item = delegateModel.items.get(row).model;
                var button = dataModelItemToButton(item);
                
                buttons.push(button);
            }
            console.log("GRID clear data model")
            dataModel = []
            console.log("GRID change uiType " + uiType + " -> " + newUiType)
            uiType = newUiType
            console.log("GRID update data model")
            dataModel = buttons;
            console.log("GRID buttons changed uiType=" + uiType + " cls=" + areaViewModel.callClass);
             
        }  
    }
    Repeater 
    {
        id: areaRepeater
        model: dataModel

        delegate: {
                gridButton;
            }
        onItemAdded: {              
            if (index == areaRepeater.count - 1) {
                console.log("GRID repeater added " + areaRepeater.count + " buttons")
                updateItems()
                }
            }
        }
    }


function modelDataIsEqual(modelData, data) {
        if (!modelData || !data || modelData.model.rowCount() !== data.length)
            return false;
        for (var i = 0; i < modelData.model.rowCount(); ++i) {
            const item = modelData.items.get(i).model;
            const button = data[i];
            if (button.dataAreaShortName !== item.dataAreaShortName ||
                    button.dataAreaName !== item.dataAreaName ||
                    button.dataIsExitArea !== item.dataIsExitArea ||
                    button.dataAreaGridCols !== item.dataAreaGridCols ||
                    button.dataIsGroupedButton !== item.dataIsGroupedButton ||
                    button.dataAreaLocked !== item.dataAreaLocked ||
                    button.dataAreaIcon !== item.dataAreaIcon ||
                    button.dataIsDopArea !== item.dataIsDopArea ||
                    button.dataIsSideGroup !== item.dataIsSideGroup ||
                    dlaButtonStyle !== areaModel.combineBtnStyle ||
                    oddEvenBtnAppearance !== areaModel.getLockedButtonAppearance() ||
                    button.dataButtonAppearance !== item.dataButtonAppearance ||
                    button.dataCrowdedArea !== item.dataCrowdedArea ||
                    button.dataOverCrowdedArea !== item.dataOverCrowdedArea ||
                    button.dataPeopleIconArea  !== item.dataPeopleIconArea ||
                    button.dataSideStateIconArea !== item.dataSideStateIconArea)
            {
                return false;
            }
        }
        return true;
    }
function dataModelItemToButton(item) {
        if (!item)
            return null;
        return {
            dataAreaShortName: item.dataAreaShortName,
            dataAreaName: item.dataAreaName,
            dataIsExitArea: item.dataIsExitArea,
            dataAreaGridCols: item.dataAreaGridCols,
            dataIsGroupedButton: item.dataIsGroupedButton,
            dataAreaLocked: item.dataAreaLocked,
            dataAreaIcon: item.dataAreaIcon,
            dataIsDopArea: item.dataIsDopArea,
            dataIsSideGroup: item.dataIsSideGroup,
            dataButtonAppearance : item.dataButtonAppearance,
            dataCrowdedArea: item.dataCrowdedArea,
            dataOverCrowdedArea: item.dataOverCrowdedArea,
            dataPeopleIconArea : item.dataPeopleIconArea,
            dataSideStateIconArea : item.dataSideStateIconArea
        }
    }

As I mentioned above, larger number of buttons means delay also increased. Like 100ms took for enter one button into model. Somewhere, mentioned use ListModel in Repeater for loading the dataitem. But i tried that one also, seems same delay was happened. I have checked in my target device most of the delay causing to execute this line only dataModel = buttons; I couldn't ignore this line, Since, once copied into the datamodel then only see all the buttons.Any other way to reduce this delay?

3
  • Please add a working example, but as @nikita-shrama also mentioned, the root of the problem is your for loop. I would suggest using a HashTable to achieve faster comparison and improve the time complexity.
    – smr
    Commented Oct 20, 2023 at 12:43
  • Read this part of the docs. When you change the model, the Repeater must recreate all instances of the delegates. If you can change your code to a ListView or GridView, it will only need to instantiate the delegates that are visible.
    – JarMan
    Commented Oct 20, 2023 at 13:50
  • use a proper QAbstractListModel and don't do all that logic in QML.
    – GrecKo
    Commented Oct 20, 2023 at 22:16

1 Answer 1

0

It appears that you're using a Javascript array as your model.

    delegateModel.model = areaViewModel;

This means every time you have a change, the model must be replaced with a new Javascript array that includes the change. However, the Repeater will have to redraw all records even those that it has seen before. The complexity of this pattern is O(N(N+1)/2) or, roughly O(N^2). The performance can be seen from this table:

Size Total Draw Time
1 1
2 1 + 2
3 1 + 2 + 3
N N ( N + 1 ) / 2

This is bad since it's an exponential delay.

On some platforms, you may not notice this because either N is low or you have a fast enough processor. So, what you do, is you deploy that as an acceptable solution for that platform. However, after a while, when that code becomes production code, your users will increase N beyond your testing limits and your users will raise it as an issue for you to solve.

The preferred way is to refactor and replace with either (1) ListModel, (2) QAbstractListModel, or (3) new the Qt6.4+ list syntax. These models work well because they implement change detection so that the Repeater only has to refresh the newer records not refresh all the records.

    // e.g.
    Repeater {
        model: ListModel {
            id: areaViewModel
        }
    }

In short, do not use a Javascript array as the model for your Repeater.

1
  • Thanks for answers, As you said that, our same project code works well that means couldn't see this delay issue in our high end processor (IMX 8) and Qt 6.4 used in our target device. Another one query, When will upgrade the Qt version 6.4, shall we reduce this delay in my IMX 537 processor Target device without changing code.
    – Mohan S
    Commented Nov 9, 2023 at 9:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.