From 6cbea20bf9f7a02aef404a49a56d0a0d895b0abd Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Mon, 19 Feb 2018 18:29:27 +0100 Subject: [PATCH] try to preload certain applets in a smart way Summary: preload popups of some applets after init in the background based on a value of X-Plasma-PreloadWeight in the desktop file if present, otherwise some default values based on the applet type (Provides) Save the weight in the config, if an applet is never opened, slowly decrease the weight, when it reaches 0 don't preload it next start, increase every time it gets opened, so at the moment it's quite aggressive about preloading, in order to not do it a lot of plasma startups without touching the applet are needed Applet with a very big weigth will be preloaded immediately, therefore having an impact on the time it will take to have a panel visible and usable, while lesser weigths will preload after a random number of seconds between 2 and 10, so will load in the background after everything is started Test Plan: Plasma starts up correctly, applets load correctly and can be added correctly both those expanded or collapsed. plasmashell appears correctly usable without too big hiccups even while it's loading things in the background some numbers: without preloading, plasma takes around 64 mb of memory after startup when preloading everything about 94, so it's a cost of about 30 mb which is not negligible. don't have precise timing, but if everything gets preloaded immediately, the time to get an usable desktop appears to be at least doubled, while the delayed preloading (except just a couple of applets) doesn't seem to have a big impact on the time needed to get an usable desktop Reviewers: #plasma Subscribers: davidedmundson, broulik, apol, ngraham, plasma-devel, #frameworks Tags: #plasma, #frameworks Differential Revision: https://phabricator.kde.org/D10512 --- .../data/servicetypes/plasma-applet.desktop | 3 + src/plasmaquick/CMakeLists.txt | 3 + src/plasmaquick/appletquickitem.cpp | 73 ++++++++++++++++++- src/plasmaquick/private/appletquickitem_p.h | 19 +++++ 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/src/plasma/data/servicetypes/plasma-applet.desktop b/src/plasma/data/servicetypes/plasma-applet.desktop index 3821b0c09..b0b19b690 100644 --- a/src/plasma/data/servicetypes/plasma-applet.desktop +++ b/src/plasma/data/servicetypes/plasma-applet.desktop @@ -83,6 +83,9 @@ Type=QString [PropertyDef::X-Plasma-Provides] Type=QStringList +[PropertyDef::X-Plasma-PreloadWeight] +Type=int + [PropertyDef::X-Plasma-ConfigPlugins] Type=QStringList diff --git a/src/plasmaquick/CMakeLists.txt b/src/plasmaquick/CMakeLists.txt index 5db6edbec..d0f1e3c51 100644 --- a/src/plasmaquick/CMakeLists.txt +++ b/src/plasmaquick/CMakeLists.txt @@ -6,6 +6,7 @@ endif() set(plasmaquick_LIB_SRC appletquickitem.cpp + debug_p.cpp dialog.cpp dialogshadows.cpp view.cpp @@ -20,6 +21,8 @@ set(plasmaquick_LIB_SRC ../declarativeimports/core/units.cpp ) +ecm_qt_declare_logging_category(PlasmaQuick_LIB_SRCS HEADER debug_p.h IDENTIFIER LOG_PLASMAQUICK CATEGORY_NAME org.kde.plasmaquick) + add_library(KF5PlasmaQuick SHARED ${plasmaquick_LIB_SRC}) add_library(KF5::PlasmaQuick ALIAS KF5PlasmaQuick) target_include_directories(KF5PlasmaQuick PUBLIC "$") diff --git a/src/plasmaquick/appletquickitem.cpp b/src/plasmaquick/appletquickitem.cpp index 85f5f3ea0..084abfb92 100644 --- a/src/plasmaquick/appletquickitem.cpp +++ b/src/plasmaquick/appletquickitem.cpp @@ -19,6 +19,7 @@ #include "appletquickitem.h" #include "private/appletquickitem_p.h" +#include "debug_p.h" #include #include @@ -43,6 +44,7 @@ namespace PlasmaQuick QHash AppletQuickItemPrivate::s_rootObjects = QHash(); +AppletQuickItemPrivate::PreloadPolicy AppletQuickItemPrivate::s_preloadPolicy = AppletQuickItemPrivate::Uninitialized; AppletQuickItemPrivate::AppletQuickItemPrivate(Plasma::Applet *a, AppletQuickItem *item) : q(item), @@ -52,6 +54,19 @@ AppletQuickItemPrivate::AppletQuickItemPrivate(Plasma::Applet *a, AppletQuickIte expanded(false), activationTogglesExpanded(false) { + if (s_preloadPolicy == Uninitialized) { + if (qEnvironmentVariableIsSet("PLASMA_PRELOAD_POLICY")) { + const QString policy = QString::fromUtf8(qgetenv("PLASMA_PRELOAD_POLICY")).toLower(); + if (policy == QStringLiteral("aggressive")) { + s_preloadPolicy = Aggressive; + } else if (policy == QStringLiteral("none")) { + s_preloadPolicy = None; + //default as Adaptive + } else { + s_preloadPolicy = Adaptive; + } + } + } } void AppletQuickItemPrivate::init() @@ -70,6 +85,21 @@ void AppletQuickItemPrivate::init() } } +int AppletQuickItemPrivate::preloadWeight() const +{ + int defaultWeight; + const QStringList provides(KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides"))); + + //some applet types we want a bigger weight + if (provides.contains(QStringLiteral("org.kde.plasma.launchermenu"))) { + defaultWeight = DefaultLauncherPreloadWeight; + } else { + defaultWeight = DefaultPreloadWeight; + } + //default widgets to be barely preloaded + return qBound(0, applet->config().readEntry(QStringLiteral("PreloadWeight"), qMax(defaultWeight, applet->pluginMetaData().rawData().value(QStringLiteral("X-Plasma-PreloadWeight")).toInt())), 100); +} + void AppletQuickItemPrivate::connectLayoutAttached(QObject *item) { QObject *layout = 0; @@ -218,7 +248,7 @@ QQuickItem *AppletQuickItemPrivate::createFullRepresentationItem() if (fullRepresentation && fullRepresentation != qmlObject->mainComponent()) { QVariantHash initialProperties; - initialProperties[QStringLiteral("parent")] = QVariant::fromValue(q); + initialProperties[QStringLiteral("parent")] = QVariant(); fullRepresentationItem = qobject_cast(qmlObject->createObjectFromComponent(fullRepresentation, QtQml::qmlContext(qmlObject->rootObject()), initialProperties)); } else { fullRepresentation = qmlObject->mainComponent(); @@ -437,6 +467,11 @@ AppletQuickItem::AppletQuickItem(Plasma::Applet *applet, QQuickItem *parent) AppletQuickItem::~AppletQuickItem() { + //decrease weight + if (d->s_preloadPolicy >= AppletQuickItemPrivate::Adaptive) { + d->applet->config().writeEntry(QStringLiteral("PreloadWeight"), qMax(0, d->preloadWeight() - AppletQuickItemPrivate::PreloadWeightDecrement)); + } + //Here the order is important delete d->compactRepresentationItem; delete d->fullRepresentationItem; @@ -598,6 +633,33 @@ void AppletQuickItem::init() d->compactRepresentationCheck(); qmlObject()->engine()->rootContext()->setBaseUrl(qmlObject()->source()); qmlObject()->engine()->setContextForObject(this, qmlObject()->engine()->rootContext()); + + //if we're expanded we don't care about preloading because it will already be the case + //as well as for containments + if (d->applet->isContainment() || + d->expanded || d->preferredRepresentation == d->fullRepresentation) { + return; + } + + if (d->s_preloadPolicy >= AppletQuickItemPrivate::Adaptive) { + const int preloadWeight = d->preloadWeight(); + qCInfo(LOG_PLASMAQUICK) << "New Applet " << d->applet->title() << "with a weight of" << preloadWeight; + + //don't preload applets less then a certain weigth + if (d->s_preloadPolicy >= AppletQuickItemPrivate::Aggressive || preloadWeight >= AppletQuickItemPrivate::DelayedPreloadWeight) { + //spread the creation over a random delay to make it look + //plasma started already, and load the popup in the background + //without big noticeable freezes, the bigger the weight the smaller is likely + //to be the delay, smaller minimum walue, smaller spread + const int min = (100 - preloadWeight) * 20; + const int max = (100 - preloadWeight) * 100; + const int delay = qrand() % ((max + 1) - min) + min; + QTimer::singleShot(delay, this, [this, delay]() { + qCInfo(LOG_PLASMAQUICK) << "Delayed preload of " << d->applet->title() << "after" << (qreal)delay/1000 << "seconds"; + d->createFullRepresentationItem(); + }); + } + } } @@ -726,6 +788,7 @@ void AppletQuickItem::setExpanded(bool expanded) } if (expanded) { + qint64 time = QDateTime::currentMSecsSinceEpoch(); d->createFullRepresentationItem(); if (!d->applet->isContainment() && (!d->preferredRepresentation || @@ -738,6 +801,14 @@ void AppletQuickItem::setExpanded(bool expanded) } else { d->fullRepresentationItem->setProperty("parent", QVariant::fromValue(this)); } + + //increase on open, ignore containments + if (d->s_preloadPolicy >= AppletQuickItemPrivate::Adaptive && !d->applet->isContainment()) { + const int newWeight = qMin(d->preloadWeight() + AppletQuickItemPrivate::PreloadWeightIncrement, 100); + d->applet->config().writeEntry(QStringLiteral("PreloadWeight"), newWeight); + qCInfo(LOG_PLASMAQUICK) << "Increasing score for" << d->applet->title() << "to" << newWeight; + } + qCInfo(LOG_PLASMAQUICK) << "Applet" << d->applet->title() << "opened after" << ( QDateTime::currentMSecsSinceEpoch() - time) << "msec"; } d->expanded = expanded; diff --git a/src/plasmaquick/private/appletquickitem_p.h b/src/plasmaquick/private/appletquickitem_p.h index 476eb25fe..a1056d597 100644 --- a/src/plasmaquick/private/appletquickitem_p.h +++ b/src/plasmaquick/private/appletquickitem_p.h @@ -55,10 +55,28 @@ class AppletQuickItem; class AppletQuickItemPrivate { public: + //weight values for the logic for when or if to preload + enum PreloadWeights { + DefaultPreloadWeight = 50, + DefaultLauncherPreloadWeight = 100, + DelayedPreloadWeight = 25, + PreloadWeightIncrement = 5, + PreloadWeightDecrement = 8 + }; + + enum PreloadPolicy { + Uninitialized = -1, + None = 0, + Adaptive = 1, + Aggressive = 2 + }; + AppletQuickItemPrivate(Plasma::Applet *a, AppletQuickItem *item); void init(); + int preloadWeight() const; + QQuickItem *createCompactRepresentationItem(); QQuickItem *createFullRepresentationItem(); QQuickItem *createCompactRepresentationExpanderItem(); @@ -80,6 +98,7 @@ public: AppletQuickItem *q; + static PreloadPolicy s_preloadPolicy; int switchWidth; int switchHeight;