{"id":383,"date":"2015-10-19T13:02:55","date_gmt":"2015-10-19T11:02:55","guid":{"rendered":"https:\/\/blog2.basyskom.com\/?p=383"},"modified":"2024-01-04T17:25:25","modified_gmt":"2024-01-04T16:25:25","slug":"storage-of-qml-defined-properties-explained-part-1","status":"publish","type":"post","link":"https:\/\/www.basyskom.de\/en\/storage-of-qml-defined-properties-explained-part-1\/","title":{"rendered":"Storage of QML defined properties explained (Part 1)"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"383\" class=\"elementor elementor-383\" data-elementor-post-type=\"post\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-2cf1cc47 elementor-section-boxed elementor-section-height-default elementor-section-height-default wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-column-slider-no wpr-equal-height-no\" data-id=\"2cf1cc47\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-31d07d40\" data-id=\"31d07d40\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-cfd2eb0 elementor-widget elementor-widget-heading\" data-id=\"cfd2eb0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">It all started with a simple question<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-183e5cc2 elementor-widget elementor-widget-text-editor\" data-id=\"183e5cc2\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>How much does a property you define in QML code cost in terms of memory?\u00a0<\/p><p>This led me down a merry chase into the <a href=\"http:\/\/code.qt.io\/cgit\/qt\/qtdeclarative.git\/tree\/src\/qml\" target=\"_blank\" rel=\"noopener noreferrer\">source<\/a> of the QML engine. The result is this article and in the end a contribution to Qt5.6.<\/p><p>Before we get started, let\u2019s do a quick recap of what we know about Qt properties on the C++ side. Qt has compile time properties which can be added to QObject derived classes. The various methods associated with such a property (read\/write\/reset\/notify\/\u2026) are specified using the Q_PROPERTY macro. The properties themselves are typically stored as C++ member variables. They integrate with the meta object system and are therefore also accessible from the QML-side.<\/p><p>Back to the initial question: how much memory is needed for a QML defined property? To answer this, one first needs to figure out how and where they arestored. The <a href=\"http:\/\/code.qt.io\/cgit\/qt\/qtdeclarative.git\/tree\/src\/qml\/qml\/qqmlvmemetaobject.cpp?h=5.5\" target=\"_blank\" rel=\"noopener noreferrer\">QQmlVMEMetaObject<\/a> class, which is instantiated for each QML object, takes care of that.<\/p><p>The constructor of QQmlVMEMetaObject contains this <a href=\"http:\/\/code.qt.io\/cgit\/qt\/qtdeclarative.git\/tree\/src\/qml\/qml\/qqmlvmemetaobject.cpp?h=5.5#n573\" target=\"_blank\" rel=\"noopener noreferrer\">line<\/a>.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-32f17be elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"32f17be\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-cpp'>data = new QQmlVMEVariant[metaData-&gt;propertyCount - metaData-&gt;varPropertyCount]; <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d2ee0d4 elementor-widget elementor-widget-text-editor\" data-id=\"d2ee0d4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>This gives a hint that we will probably have to differentiate between typed\u00a0(non-var) and var properties.<\/p><p>Let&#8217;s look at typed properties first. So for each typed property the QQmlVMEMetaObject allocates a <a href=\"http:\/\/code.qt.io\/cgit\/qt\/qtdeclarative.git\/tree\/src\/qml\/qml\/qqmlvmemetaobject.cpp?h=5.5#n89\" target=\"_blank\" rel=\"noopener noreferrer\">QQmlVMEVariant<\/a>. The QQmlVMEVariant class is local to the translation unit of the QQmlVMEMetaObject.<\/p><p>Let&#8217;s have a look there.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c73ac5a elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"c73ac5a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-cpp'>class QQmlVMEVariant\r\n{\r\npublic:\r\n    \/\/ [...]\r\n    inline QObject *asQObject();\r\n    inline const QVariant &amp;amp;asQVariant();\r\n    inline int asInt();\r\n    inline bool asBool();\r\n    inline double asDouble();\r\n    inline const QString &amp;amp;asQString();\r\n    inline const QUrl &amp;amp;asQUrl();\r\n    inline const QTime &amp;amp;asQTime();\r\n    \/\/ [...]\r\n&nbsp;\r\n    inline void setValue(QObject *v, QQmlVMEMetaObject *target, int index);\r\n    inline void setValue(const QVariant &amp;amp;);\r\n    inline void setValue(int);\r\n    inline void setValue(bool);\r\n    inline void setValue(double);\r\n    inline void setValue(const QString &amp;amp;);\r\n    inline void setValue(const QUrl &amp;amp;);\r\n    inline void setValue(const QTime &amp;amp;);\r\n    \/\/ [...]\r\n&nbsp;\r\nprivate:\r\n    int type;\r\n    void *data[8]; \/\/ Large enough to hold all types\r\n    \/\/ [...]\r\n}; <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-685721e elementor-widget elementor-widget-text-editor\" data-id=\"685721e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>This looks promising. The interface contains various methods to get\/set types which are also basic QML types. Let&#8217;s see where this is used by looking at the\u00a0<a style=\"font-family: var( --e-global-typography-text-font-family ), pt-sans-narrow; font-size: var( --e-global-typography-text-font-size ); font-weight: var( --e-global-typography-text-font-weight ); line-height: var( --e-global-typography-text-line-height );\" href=\"http:\/\/code.qt.io\/cgit\/qt\/qtdeclarative.git\/tree\/src\/qml\/qml\/qqmlvmemetaobject.cpp?h=5.5#n253\" target=\"_blank\" rel=\"noopener noreferrer\">asBool()<\/a>\/<a style=\"font-family: var( --e-global-typography-text-font-family ), pt-sans-narrow; font-size: var( --e-global-typography-text-font-size ); font-weight: var( --e-global-typography-text-font-weight ); line-height: var( --e-global-typography-text-line-height );\" href=\"http:\/\/code.qt.io\/cgit\/qt\/qtdeclarative.git\/tree\/src\/qml\/qml\/qqmlvmemetaobject.cpp?h=5.5#n371\" target=\"_blank\" rel=\"noopener noreferrer\">setValue(bool)<\/a>\u00a0methods.<\/p><p>This leads us to:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1e06d16 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"1e06d16\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-cpp'>int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6c4dbd0 elementor-widget elementor-widget-text-editor\" data-id=\"6c4dbd0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Assumption: To read for example a bool typed property, metaCall() is invoked with c == QMetaObject::ReadProperty. _id contains the index of the property in the array of QQmlVMEVariant we allocated earlier (and assigned to the data member variable). a is the destination where the property value should be read into.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-47c14f4 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"47c14f4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-cpp'>if (c == QMetaObject::ReadProperty) {\n    switch(t) { \/\/ [...] \n        case QVariant::Bool: *reinterpret_cast(a[0]) = data[id].asBool();\n             break; \/\/ \n[...] <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0590562 elementor-widget elementor-widget-text-editor\" data-id=\"0590562\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>By setting a breakpoint and loading a simple test QML we can quickly validate that this is actually the right place (the write case is very similar so I will leave it as an exercise for the reader).<\/p><p>For brevity I will leave out the implementation for asBool()\/setValue(bool) but if you have a look at it you will notice, that it stores the bool value in these two members of the QQmlVMEVariant:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c207fb1 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"c207fb1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre><code class='language-cpp'>int type; \nvoid *data[8]; \/\/ Large enough to hold all types <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0531e49 elementor-widget elementor-widget-text-editor\" data-id=\"0531e49\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>This allows us to already answer the initial question. How much memory does it take to store a simple QML defined property?<\/p><p>The answer is sizeof(void*)*8 + sizeof(int), which is 36 bytes on a 32bit system and 68 bytes on 64bit system. Taking into account structure padding at the end of the QQmlVMEVariant this will end up at 72 bytes on a 64bit system.<\/p><p>This is quite a bit more than we would have expected. The next part of this series will explain why the QQmlVMEVariant is that large, how var properties are handled and how the current situation can be improved.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>How much does a property you define in QML code cost in terms of memory?\u00a0<\/p>","protected":false},"author":2,"featured_media":1367,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[2,8],"tags":[15],"class_list":["post-383","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","category-qt","tag-qt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/383","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/comments?post=383"}],"version-history":[{"count":9,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/383\/revisions"}],"predecessor-version":[{"id":7483,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/383\/revisions\/7483"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media\/1367"}],"wp:attachment":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media?parent=383"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/categories?post=383"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/tags?post=383"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}