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

Achim Bohnet ach at mpe.mpg.de
Thu Oct 13 15:05:16 CEST 2005


On Wednesday 12 October 2005 23:51, Aurelien Gateau wrote:
> [Resending with bzipped attachements to pass the message size limit. List 
> admin please cancel my pending message]
> 
> Hello,

Hi,

FWIW (maybe nothing) Back in June Lubos Lunak created a speedup
patch too.  '[Kde-imaging] KIPI initialization'.  What I remember
is that it depends on a 3.5 feature to explore it's full power.

I'm in a hurry so, maybe it's all already applied. No time to
check.

Achim
> 
> 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
> 

-- 
  To me vi is Zen.  To use vi is to practice zen. Every command is
  a koan. Profound to the user, unintelligible to the uninitiated.
  You discover truth everytime you use it.
                                      -- reddy at lion.austin.ibm.com


More information about the Kde-imaging mailing list