How to improve Qt's QSS/CSS performance on embedded devices?
-
Hi community,
I ran into performance problems with Qt for embedded Linux 4.8.3 on custom hardware. The styling of the UI is done with QSS and no custom painting code. There is various information on stackoverflow and in this forum about the performance of QSS. A few things can be summerized:
- A single file of QSS set for the whole application is used in almost all samples. Some people suggest that the performance of multiple files could be slightly better.
- The QSS file should be set a single time and dynamic styling should happen through dynamic style sheet properties. So it's basically a special selector in the already set stylesheet.
- The performance of QSS is known to be worse than custom painting code and some even suggest to avoid the use of QSS comepletely.
In our project we use dynamic styling, a single large QSS file and the performance during run-time is actually fine. Even compared to not using a stylesheet and letting the default style draw itself. The problem is the startup time. It is vastly different when we use the QSS. I have created the following measurements:
Interesting observations:
- Original: Single, large QSS file which is set for the whole application at startup before the widgets are instatiated
- Empty QSS: Using a empty QSS file (removing all the styling information) results in a startup time reduction of about 4 seconds. The UI is then drawn with the default styling and looks exactly the same as if no QSS file was set in the application at all.
- No QSS at all: This shows how much impact the styling has on the startup time. Not setting any QSS file for any widget results in a huge performance gain.
- Single example widget in global QSS: In this scenario the application-wide QSS contained only styling information for a single widget. You can see that the single widget already increases the startup time a bit (compared to an empty QSS file). This is understandable and fine.
- Single example widget directly in C++: In this scenario the same widget as before is styled but this time the styling string is read and set directly in the constructor of the mentioned widget. So there was no application-wide QSS file set. This seems to work extremely fast (only at the first glance, see below!)
- No "global" QWidget styles: I thought that I could maybe improve the performance by being more specific in the QSS file, for example, by not setting the font in a global QWidget selector but for each of the widgets separately. I just visualized the impact of commenting out the global rule. So the performance improvement could also be a font difference. But the point is that the impact of such "global" styling selectors is rather small.
- Split files: In this scenario I added separate styling information to 7 different, heavily used widgets. So again no application-wide stylesheet. You can see that the startup time is already high. I did not try to set the styling for all my widgets because this graph already shows that there is no promising performance gain anymore.
- app.setStylesheet() after widget construction: Then I had the idea to first construct all the widgets and apply the stylesheet afterwards. This actually improved the startup time BUT I did not account for such updates in some of the widgets. In order to get this approach running I would need to update some of the widgets which would reduce the gain again. But 2 seconds are not that impressive anyways.
So:
- Why has even an empty QSS file such a huge impact on the performance and can I do anything about that?
- Does that mean that the whole styling framework has bad performance and there is no way to get it running faster?
- I would like to somehow pre-load/bake the styling information. This does not have to be done at every startup. All the file parsing etc. is done on every startup although this is not necessary. Any ideas on that?
- Do I really need to re-write all the styling using custom painting code? Does anybody know if the performance is actually near the "default" style. So does custom painting code still has impact on the performance or is it more or less "for free" compared to the default style?
- Does anyone has experience with single/multiple QSS files? Using specific styling per widget seems to improve the performance immensely at the first glance but adding all the stlying step by step seems to result in more or less the same startup time in the end?!
Thank you for any hints!
-
Hi,
Pretty interesting analysis.
One thing I'd do (might not be possible though) is to re-run this against a more recent version of Qt 4. That latest (and last one) is 4.8.7.
You might also want to bring this to the interest mailing list. You'll find there Qt's developers/maintainers.
Note that Qt 4 has reached end of life, so if possible, you should consider updating to Qt 5.
-
Hi community,
I ran into performance problems with Qt for embedded Linux 4.8.3 on custom hardware. The styling of the UI is done with QSS and no custom painting code. There is various information on stackoverflow and in this forum about the performance of QSS. A few things can be summerized:
- A single file of QSS set for the whole application is used in almost all samples. Some people suggest that the performance of multiple files could be slightly better.
- The QSS file should be set a single time and dynamic styling should happen through dynamic style sheet properties. So it's basically a special selector in the already set stylesheet.
- The performance of QSS is known to be worse than custom painting code and some even suggest to avoid the use of QSS comepletely.
In our project we use dynamic styling, a single large QSS file and the performance during run-time is actually fine. Even compared to not using a stylesheet and letting the default style draw itself. The problem is the startup time. It is vastly different when we use the QSS. I have created the following measurements:
Interesting observations:
- Original: Single, large QSS file which is set for the whole application at startup before the widgets are instatiated
- Empty QSS: Using a empty QSS file (removing all the styling information) results in a startup time reduction of about 4 seconds. The UI is then drawn with the default styling and looks exactly the same as if no QSS file was set in the application at all.
- No QSS at all: This shows how much impact the styling has on the startup time. Not setting any QSS file for any widget results in a huge performance gain.
- Single example widget in global QSS: In this scenario the application-wide QSS contained only styling information for a single widget. You can see that the single widget already increases the startup time a bit (compared to an empty QSS file). This is understandable and fine.
- Single example widget directly in C++: In this scenario the same widget as before is styled but this time the styling string is read and set directly in the constructor of the mentioned widget. So there was no application-wide QSS file set. This seems to work extremely fast (only at the first glance, see below!)
- No "global" QWidget styles: I thought that I could maybe improve the performance by being more specific in the QSS file, for example, by not setting the font in a global QWidget selector but for each of the widgets separately. I just visualized the impact of commenting out the global rule. So the performance improvement could also be a font difference. But the point is that the impact of such "global" styling selectors is rather small.
- Split files: In this scenario I added separate styling information to 7 different, heavily used widgets. So again no application-wide stylesheet. You can see that the startup time is already high. I did not try to set the styling for all my widgets because this graph already shows that there is no promising performance gain anymore.
- app.setStylesheet() after widget construction: Then I had the idea to first construct all the widgets and apply the stylesheet afterwards. This actually improved the startup time BUT I did not account for such updates in some of the widgets. In order to get this approach running I would need to update some of the widgets which would reduce the gain again. But 2 seconds are not that impressive anyways.
So:
- Why has even an empty QSS file such a huge impact on the performance and can I do anything about that?
- Does that mean that the whole styling framework has bad performance and there is no way to get it running faster?
- I would like to somehow pre-load/bake the styling information. This does not have to be done at every startup. All the file parsing etc. is done on every startup although this is not necessary. Any ideas on that?
- Do I really need to re-write all the styling using custom painting code? Does anybody know if the performance is actually near the "default" style. So does custom painting code still has impact on the performance or is it more or less "for free" compared to the default style?
- Does anyone has experience with single/multiple QSS files? Using specific styling per widget seems to improve the performance immensely at the first glance but adding all the stlying step by step seems to result in more or less the same startup time in the end?!
Thank you for any hints!
@FrozenTarzan said in How to improve Qt's QSS/CSS performance on embedded devices?:
Why has even an empty QSS file such a huge impact on the performance and can I do anything about that?
Does that mean that the whole styling framework has bad performance and there is no way to get it running faster?i guess it's hard to get it any faster than with an empty QSS. Since the QStylesheetStyle instances need to be created and propagated down the child hierarchy. Which implies generating some events which lead to additional actions.
I would like to somehow pre-load/bake the styling information. This does not have to be done at every startup. All the file parsing etc. is done on every startup although this is not necessary. Any ideas on that?
This is simply not supported (yet - but probably never will be?)
Unfortunately the parsing has to be done every time again. This involves also the creation of the internal structures in the QStyle instance.Do I really need to re-write all the styling using custom painting code? Does anybody know if the performance is actually near the "default" style. So does custom painting code still has impact on the performance or is it more or less "for free" compared to the default style?
Instead of custom painting you would need to write a custom QStyle implementation IMO. Since you would still have all the native QStyles instantiated.
Does anyone has experience with single/multiple QSS files? Using specific styling per widget seems to improve the performance immensely at the first glance but adding all the stlying step by step seems to result in more or less the same startup time in the end?!
I don't think so. See the answers above.
When using QSS i would rather keep the QSS as generic as possible. Means as less definitions as needed and share as much definitions for different widgets as possible. And only style the properties you really need to be changed.
This keeps the stylesheet parsing short and also the internal structures are less. So less definitions in the internal QStylesheetStructure structure means faster determination if the style rule applies for a specific widget or not. -
Hi,
Pretty interesting analysis.
One thing I'd do (might not be possible though) is to re-run this against a more recent version of Qt 4. That latest (and last one) is 4.8.7.
You might also want to bring this to the interest mailing list. You'll find there Qt's developers/maintainers.
Note that Qt 4 has reached end of life, so if possible, you should consider updating to Qt 5.
@SGaist Hi SGaist, well I took the time and checked all the change logs from 4.8.4 up to 4.8.7 and these minor updates did not touch any rendering/performance relevant topics.
There are multiple posts in the net where the QSS system seems not to have a high priority for the Qt developers due to the introduction of QML.
Thanks for the hint about the mailing list. Could you give me a rule of thumb how to formulate questions there? Is it ok to just give a short summary and post a link to this thread?
-
@FrozenTarzan said in How to improve Qt's QSS/CSS performance on embedded devices?:
Why has even an empty QSS file such a huge impact on the performance and can I do anything about that?
Does that mean that the whole styling framework has bad performance and there is no way to get it running faster?i guess it's hard to get it any faster than with an empty QSS. Since the QStylesheetStyle instances need to be created and propagated down the child hierarchy. Which implies generating some events which lead to additional actions.
I would like to somehow pre-load/bake the styling information. This does not have to be done at every startup. All the file parsing etc. is done on every startup although this is not necessary. Any ideas on that?
This is simply not supported (yet - but probably never will be?)
Unfortunately the parsing has to be done every time again. This involves also the creation of the internal structures in the QStyle instance.Do I really need to re-write all the styling using custom painting code? Does anybody know if the performance is actually near the "default" style. So does custom painting code still has impact on the performance or is it more or less "for free" compared to the default style?
Instead of custom painting you would need to write a custom QStyle implementation IMO. Since you would still have all the native QStyles instantiated.
Does anyone has experience with single/multiple QSS files? Using specific styling per widget seems to improve the performance immensely at the first glance but adding all the stlying step by step seems to result in more or less the same startup time in the end?!
I don't think so. See the answers above.
When using QSS i would rather keep the QSS as generic as possible. Means as less definitions as needed and share as much definitions for different widgets as possible. And only style the properties you really need to be changed.
This keeps the stylesheet parsing short and also the internal structures are less. So less definitions in the internal QStylesheetStructure structure means faster determination if the style rule applies for a specific widget or not.@raven-worx Hi raven-worx, thanks a lot for your answers!
Regarding the "baking" process I thought of something like this:
- Run the application and instantiate all the QSS styling, just as it would normally happen.
- On shutdown (or whenever all the styling is ready) the application somehow tries to serialize the styling (here I don't know what is relevant, maybe QStyle, QStyleOption,...?)
- From now on when the application starts the QSS styling is not touched anymore but instead the styling is deserialized and filled in the drawing/rendering pipeline of Qt directly. Again, I don't know how this would look like: Handing QStyles to widgets or settings QStyleOptions somehow, I have also no exerience with the QStylesheetStructure class you were talking about.
In the QStyle docu it says:
"As mentioned earlier, the information about what is to be drawn and how it should be drawn is specified by a QStyleOption object, so there is no need to ask the widget."
So maybe my idea is not that off and the "baking" could actually work somehow? Do you have any thoughts on this or do I misunderstand the whole styling framework?Instead of custom painting you would need to write a custom QStyle implementation IMO. Since you would still have all the native QStyles instantiated.
I think that I misunderstand a few things here. I thought that custom painting can happen via to mechanisms:
- Re-implementing virtual void paintEvent(QPaintEvent *); and drawing in there. I tried it and it seems to work quite nice. Focus, borders, text,...
- Subclassing QStyle, implementing all sorts of virtual functions (pixelMetric, drawControl,...) and then assigning it to my with QApplication::setStyle(new CustomStyle);
But I thought that those to mechanisms are actually the same in performance. The second approach just seems to be more generic and definitely the way to go to conveniently switch styles in the future. Am I right here?
-
@raven-worx Hi raven-worx, thanks a lot for your answers!
Regarding the "baking" process I thought of something like this:
- Run the application and instantiate all the QSS styling, just as it would normally happen.
- On shutdown (or whenever all the styling is ready) the application somehow tries to serialize the styling (here I don't know what is relevant, maybe QStyle, QStyleOption,...?)
- From now on when the application starts the QSS styling is not touched anymore but instead the styling is deserialized and filled in the drawing/rendering pipeline of Qt directly. Again, I don't know how this would look like: Handing QStyles to widgets or settings QStyleOptions somehow, I have also no exerience with the QStylesheetStructure class you were talking about.
In the QStyle docu it says:
"As mentioned earlier, the information about what is to be drawn and how it should be drawn is specified by a QStyleOption object, so there is no need to ask the widget."
So maybe my idea is not that off and the "baking" could actually work somehow? Do you have any thoughts on this or do I misunderstand the whole styling framework?Instead of custom painting you would need to write a custom QStyle implementation IMO. Since you would still have all the native QStyles instantiated.
I think that I misunderstand a few things here. I thought that custom painting can happen via to mechanisms:
- Re-implementing virtual void paintEvent(QPaintEvent *); and drawing in there. I tried it and it seems to work quite nice. Focus, borders, text,...
- Subclassing QStyle, implementing all sorts of virtual functions (pixelMetric, drawControl,...) and then assigning it to my with QApplication::setStyle(new CustomStyle);
But I thought that those to mechanisms are actually the same in performance. The second approach just seems to be more generic and definitely the way to go to conveniently switch styles in the future. Am I right here?
@FrozenTarzan
and how would you like to serialize/deserialize the internal QStyle data?! There is just no API for it.
QStyleSheetStyle class is a private class and thus shouldn't be accessed directly. Instances of this class are created as soon as you set a stylesheet string on a widget.@FrozenTarzan said in How to improve Qt's QSS/CSS performance on embedded devices?:
I think that I misunderstand a few things here. I thought that custom painting can happen via to mechanisms:
Re-implementing virtual void paintEvent(QPaintEvent *); and drawing in there. I tried it and it seems to work quite nice. Focus, borders, text,...
Subclassing QStyle, implementing all sorts of virtual functions (pixelMetric, drawControl,...) and then assigning it to my with QApplication::setStyle(new CustomStyle);But I thought that those to mechanisms are actually the same in performance.
Probably they will be. I just proposed the QStyle-way to avoid unnecessary default QStyle instances to be created. Also you could use your custom QStyle class in other projects.
But performance-wise they are probably nearly the same i guess. -
@FrozenTarzan
and how would you like to serialize/deserialize the internal QStyle data?! There is just no API for it.
QStyleSheetStyle class is a private class and thus shouldn't be accessed directly. Instances of this class are created as soon as you set a stylesheet string on a widget.@FrozenTarzan said in How to improve Qt's QSS/CSS performance on embedded devices?:
I think that I misunderstand a few things here. I thought that custom painting can happen via to mechanisms:
Re-implementing virtual void paintEvent(QPaintEvent *); and drawing in there. I tried it and it seems to work quite nice. Focus, borders, text,...
Subclassing QStyle, implementing all sorts of virtual functions (pixelMetric, drawControl,...) and then assigning it to my with QApplication::setStyle(new CustomStyle);But I thought that those to mechanisms are actually the same in performance.
Probably they will be. I just proposed the QStyle-way to avoid unnecessary default QStyle instances to be created. Also you could use your custom QStyle class in other projects.
But performance-wise they are probably nearly the same i guess.@raven-worx said in How to improve Qt's QSS/CSS performance on embedded devices?:
and how would you like to serialize/deserialize the internal QStyle data?! There is just no API for it.
Well, that might be a challenge or even impossible if nobody has tried that before. If all the drawing code of Qt's default style was based on a POD structure I could easily serialize/deserialize it. But I'm afraid that this is not the case :-(
Thanks for all the other useful information! As a moderator, do you think I should give the mailing list a try?
-
@raven-worx said in How to improve Qt's QSS/CSS performance on embedded devices?:
and how would you like to serialize/deserialize the internal QStyle data?! There is just no API for it.
Well, that might be a challenge or even impossible if nobody has tried that before. If all the drawing code of Qt's default style was based on a POD structure I could easily serialize/deserialize it. But I'm afraid that this is not the case :-(
Thanks for all the other useful information! As a moderator, do you think I should give the mailing list a try?
@FrozenTarzan said in How to improve Qt's QSS/CSS performance on embedded devices?:
do you think I should give the mailing list a try?
the worst that can happen is that you wont receive an answer or the same answer. ;)
Qt is open source. So you can change it to your needs. But as i said QStylesheetStyle is a private class, thus binary compatibility is not guaranteed.
So you would always need to ship your custom Qt binaries along with your application.