[Kde-imaging] On-demand-loading of KIPI plugins

Aurelien Gateau aurelien.gateau at free.fr
Wed Oct 12 00:04:04 CEST 2005


Hello,

We (Gwenview developers) have been receiving a few complaints lately about 
Gwenview memory usage and startup speed.

One way to improve on this would be to implement on-demand-loading of KIPI 
plugins. That is: instead of dlopening all plugins on application startup, we 
would dlopen them only when a plugin action is triggered from the menu. I 
decided to have a look at what it would take to implement this and came up 
with a simple proof-of-concept implementation, which you will find attached to 
this message.

kipi2-libkipi.diff contains all changes to libkipi. kipi2-kipi-plugins.diff 
contains the changes to the kipi-plugins I ported to (helloworld, calendar, 
printwizard and jpeglossless).

I hopefully didn't break source compatibility (I did break binary 
compatibility), so if you fill adventurous you can try to rebuild your app 
with this new lib (I must confess I only tried it with Gwenview)


Some numbers
------------

Since measuring memory usage is a difficult art, only mastered by a few wise 
people :-) I restricted myself to measuring improvements on startup time. I 
hope you will all agree that not dlopening files leads to less memory usage 
(I would be interested to get some reliable numbers on memory usage, 
nevertheless).

My machine is a Duron 1200, with 768Mb of RAM and a decent UltraDMA IDE disk.
Here is how I ran my tests:
* Uninstalled my packaged version of kipi-plugins

* Patched the plugin-loading code of Gwenview like this:
+ QTime chrono;
+ chrono.start();
 // Sets up the plugin interface, and load the plugins
 KIPIInterface* interface = new KIPIInterface(this, mFileViewStack);
 mPluginLoader = new KIPI::PluginLoader(QStringList(), interface );
 connect( mPluginLoader, SIGNAL( replug() ), this, SLOT( slotReplug() ) );
 mPluginLoader->loadPlugins();
+ kdDebug() << "Load plugins: " << chrono.elapsed() << endl;

* Rebuild the current libkipi,kipi-plugins and installed them as well as 
Gwenview to $HOME/opt/debug (my usual debug install dir).

* Ran "export KDEDIRS=$HOME/opt/debug:/usr", then "kbuildsycoca"

* Started Gwenview three times, results (in milliseconds) are:
    1175 1042 1030 (Mean: 1082.3)


Second set of tests, using only the KIPI2 ported plugins:
* cd'ed into share/services/ and rm'ed the .desktop files for the plugins 
which was not ported to KIPI2.

* run kbuildsycoca

* Started Gwenview three times, results are:
    264 238 308 (Mean: 270)


Third set of tests, this time using KIPI2:
* Build KIPI2 version of libkipi,kipi-plugins,gwenview and installed them to 
$HOME/opt/kipi2

* Ran "export KDEDIRS=$HOME/opt/kipi2", then "kbuildsycoca"

* Started Gwenview three times, results are:
    74 56 55 (Mean: 61.6)


As you can see, it's more than 4 times faster.


How does it work?
-----------------

KIPI::Plugin acts as a proxy for the real plugin, which inherits from 
KIPI::RealPlugin. KIPI::Plugin objects are instanciated on application startup 
and create KActions based on the action definitions of the plugin desktop 
files.

The desktop file looks like this:

[Desktop]
...
Actions=action1;action2

[Desktop Action action1]
Name=Action1
Name[fr]=Action1 FR
Icon=back
Category=1

[Desktop Action action2]
Name=Action2
Name[fr]=Action2 FR
Icon=forward
Category=2


All actions of a plugin are connected to the same KIPI::Plugin slot 
(slotActionActivated() ) which will first load and init the plugin if 
necessary, then execute the corresponding plugin slot. To execute the right 
slot It uses a little trick like this:

-------
 // Get the action name: this is the name of the plugin slot to call
 const QObject* obj=sender();
 Q_ASSERT(obj);
 QCString name=obj->name();

 // Create the slot based on the action name, then call it
 QSignal signaler;
 signaler.connect(d->m_realPlugin, "1" + name + "()");
 signaler.activate();
-------

On the plugin side there are no KActions, only slots. In my example, there are 
two slots named action1() and action2().


Limitations
-----------

There is no support for enabling or disabling actions. This can be easily 
implemented through another key in the action group which would specify what 
is necessary for the plugin. Some plugins want to have some images selected, 
others will work without any selection, letting the user choose between the 
application image collections.

There is also no support for submenus. Again, this could be implemented using 
keys in the action group, but from a usability point of view I'm not sure 
it's a good idea: deep nested menus are not very handy to use.

The current code is just a proof of concept, and I tried to avoid breaking 
source compatibility so that it's easier to test, but if we go for this a few 
things should probably be written in a different way (for example the 
PluginInfo and Plugin class should be merged).


What do you think about this?

Regards,
  Aurélien
-------------- next part --------------
A non-text attachment was scrubbed...
Name: kipi2-kipi-plugins.diff
Type: text/x-diff
Size: 27434 bytes
Desc: not available
Url : http://mail.kde.org/pipermail/kde-imaging/attachments/20051013/b39101d9/kipi2-kipi-plugins-0001.bin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: kipi2-libkipi.diff
Type: text/x-diff
Size: 13543 bytes
Desc: not available
Url : http://mail.kde.org/pipermail/kde-imaging/attachments/20051013/b39101d9/kipi2-libkipi-0001.bin


More information about the Kde-imaging mailing list