Faster Screen/Component Generation while using Themes. Currently the generated code is ridiculously slow and fully building my system is taking over 100 minutes. Once generated, the theme swapping works great, but it is an unacceptable time sync to get to that point.
What have you tried so far?
Upgraded from SL 1.3.4 to SL 1.4.0
Speed Comparison
Screen
1.3.4 (ms)
1.4.0 (ms)
1
174
839
2
15
9608
3
206
50,934
4
1,428
301,029
Benchmark made using exact same system code, from 1.4.0, then replacing ‘ui_object_set_themeable_style_property’ lines with the direct assignments as 1.3.4 would have generated
Others
SquareLine Studio version: 1.4.0
This appears to apply to widgets/components generated directly in Squareline as well as widgets/components generated in code.
For every widget using a theme made (each ui_object_set_local_style_property call), the next widget made that has a theme takes longer. This has resulted in order of screen creation drastically increasing generation times and full system generations to take time measured in hours, when previously it would only be ~10 seconds.
When i stress tested by just creating a huge number of components using themes, i was able to watch the time per cycle drastically increase.
Each pass of ui_object_set_local_style_property took:
Pass
Time (ms)
1
10
100
20
200
35
300
45
400
70
600
100
800
125
1000
150
1500
200
2000
250
As it went, it would just take longer for each one
Thanks for the feedback. It would be nice if you give the platform where this drastic slowdown happens, because for example on a PC or a Raspberry Pi there are no noticeable slowdowns. I think in this case it’s related to dynamic memory reallocation when the theme style-property settings are registered for the widgets (into a 2-dimensional dynamic array). Some platforms make memory allocation in clever ways, some (your case) probably always move the whole array to an empty area/heap every time a new element is added. (Maybe LV_MEM_CUSTOM or other settings have an impact on this allocation/preallocation behaviour. We’re working on a solution, like occupying the area for the array in advance, or maybe we’ll switch to linked lists instead which take some more memory but will be faster on machines that have slower memory operations. And we’ll possibly make theme-handler code selectable if there are global colors in a project but there’s only a single default theme, that is, when no theme-handling is required.)
I ran into this as well and after diving into it, found that the main culprit are the calls to lv_obj_is_valid(). This function literally goes through every object (and it’s children recursively) on every screen looking for a matching object. In _ui_local_style_property_setting_create() it iterates through each property checking to see if the object is valid which is why this gets exponentially worse as more properties are added and more screens are created. Removing the object valid checks and changing nothing else, solves the problem.
Likewise, removing the object valid checks from _ui_theme_set_variable_styles() makes theme changes happen significantly faster as well.
The problem is that lv_obj_is_valid() is the current universal LVGL way to check whether an object really exists (e.g. in a temporary-screen scenario) before setting its properties, to avoid crashes. (And even lv_obj_is_valid() is not 100% sure, according to LVGL forum, rarely a new object can use the same memory deleted by other.)
So that’s why it’s there in the first place, but I think a new provisioning should be worked out in LVGL and/or SquareLine Studio for better object-validity check and added to the theme handler too, because you’re right, this recursive approach is far from ideal. Using NULL value of objects as a validity-signal could be made more stable/consistent for example, but any ideas are welcome, but we’ll check on the possibilities for the next release (or make this object-validity check behaviour in the export disablable).
Btw the 2nd dimension of the style-registry is linked list already which should already be an improvement over the previously used memory reallocation-techniques.
And the solution to the increasing amount of lv_obj_is_valid() check (needed for garbage collection of removed widgets in the style-registrator) is now to cache the ‘valid’ result of lv_obj_is_valid() for a certain amount of subsequent validity checks, and only check for the possible meantime-deletion of the object with lv_obj_is_valid() when the validity-check counter has expired. It will allow style-properties for the deleted objects live in memory a bit longer, but won’t increase RAM-footprint much. The next theme manager version will have this feature to support slower hardware even better.
Now that SquareLine Studio 1.4.2 is out you can try the new feature mentioned above. To enable the feature you need to define LV_SQUARELINE_THEME__OBJECT_VALIDITY_CACHE somewhere (e.g. in lv_conf.h) with an advised value of about 5…10. With this in place, there will be 5…10 times less checking by lv_obj_is_valid and if this is the slowdown reason on your hardware it should help. (Even better news: fortunately LVGL is coming up with a new, faster and more reliable method soon to check the validity of widgets.)
If your project has temporary screens, setting this to that high will delay cleaning up themed style-properties of deleted widgets much (compared to times of creating them), which could increase memory usage much gradually, while you’ll be switching between these screens.
With normal (non-temporary) screens that are created and initialized at the beginning, ui_init would finish faster with this setting I guess, and there should be no memory-leak problem because most objects remain in the memory for the run of your application, no need to create style-properties for them again and again, and to check their validity. (But if you create/delete some themed/styled objects dynamically they should still be kept an eye on, they could increase memory-footprint over time similarly to temporary screens. Memory monitor of LVGL can be enabled in lv_conf.h and used to see how much.)
An improvement to this scheme could be to use an explicit garbage collector, not an implicit one currently built into the style-property creator routine, but that would still need to do many slow LVGL object-validity checks on regular well-defined occasions automatically. We’ll check out that path soon as well.
The promised more linear garbage collection for the theme-handler is now added in the new SquareLine Studio 1.5 release. (The previous solutions are still available if you define LV_SQUARELINE_THEME__IMPLICIT_GARBAGE_COLLECTOR, but without this by default the new ‘explicit’ method is used, as seen in the ui_theme_manager.h file’s beginning.)
I tested it on a humble Raspberry Pi Pico + ILI9341 hardware with a fairly complex project having 2 themes, both with and without temporary screens. The former was OK mostly anyway, but speedup was significant for the latter, from about 3…4s the boot-up time went down to about 1.5s with the default ‘explicit’ garbage-collector setting (checking at every 10 theme style-property creations/settings), which was roundabout the bootup-time on this hardware without even using any themes.
The new LV_SQUARELINE_THEME__EXPLICIT_GARBAGE_COLLECTOR_PERIOD define’s value can be redefined from outside to affect its behavior but 10 seems to be a sweet spot. While this new explicit garbage-collector still takes LV_SQUARELINE_THEME__OBJECT_VALIDITY_CACHE into account I advise to remove this external define if it exists in your project as it would decrease the rate of finding garbage significantly.
With this new method hopefully this problem is solved for now and ever.
The LVGL-discussion which the other topic ( Not use the lv_obj_is_valid() in ui_theme_manager.c ) mentions was closed without an improvement for lv_obj_is_valid, as I see in LVGL-9.2 source-code. So lv_obj_is_valid can/should really be removed where it isn’t really necessary. I think it can be removed at least from ui_object_set_themeable_style_property() because SLS (and hopefully the user-code) won’t call this function on a non-existing widget. However, it can’t inevitably be removed from the ui_object_set_local_style_property() which the theme-changer code uses because theme changer shouldn’t set a local style on a widget that doesn’t exist, and garbage-collector is not guaranteed to have deleted every non-existent widget’s style-registry at the time of theme-switching. But anyway, boot-time of the GUI can still be made faster by removing the former, and only the on-the-fly theme change (when switching theme from the application) will take the really necessary time it needs.
It also seems that with big projects the explicit garbage collection’s period of 100 makes significant boot-time improvement, it’s preferable from now as a default value.