Introducing the OO style plugins
As shown in another topic (look for it), could be very practical to have a common Base plugin and make new plugins to extends from this base plugin. This could remove the need for the TWiki::Func module altogether, or at least put all the code referencing it in one place. Also it can make initiatives like the TWiki::Plugins.CodeContribution easier to do (every common stuff will be in the base plugin, for example).
So, this research has 2 focus:
- To provide a OO framework for plugins.
- Keep the performance at least as good as Cairo.
Benchmarking
I created 2 twiki installations : One base instalation to serve as a reference point for the benchmarks, and a OO instalation for the development.
The most representative script of the TWiki operation is the view script. In Cairo, it is basicaly 2 calls:
- a call to TWiki::initialize
- a call to TWiki::UI::View
I measured each call using the attached script and fixtures.
Time is the CPU time reported by the Benchmark module. 10 iteration where used. The average time is easily calculated (that's why I choose 10 iterations), so the total time of the 10 runs is shown.
Pages used for testing:
Result
If you want to see the step-by-step process, check
PluginOOApiJournal
With both the
TWiki:Plugins.TablePlugin
and
TWiki:Plugins.DefaultPlugin
converted to OO style, these are the numbers
| Page |
initialize |
view |
Total |
| Main.PerformanceBlankPage |
0.73 |
1.98 |
2.71 |
| Main.WebHome |
0.75 |
2.91 |
3.67 |
| Main.PerformanceTestPage |
0.75 |
2.22 |
2.97 |
| TWiki.GoodStyle |
0.77 |
2.37 |
3.14 |
| TWiki.TextFormattingRules |
0.82 |
6.90 |
7.72 |
| TWiki.DefaultPlugin |
0.75 |
2.59 |
3.34 |
| TWiki.TablePlugin |
0.78 |
5.57 |
6.35 |
OO Installation
| Page |
initialize |
view |
Total |
| Main.PerformanceBlankPage |
0.75 |
2.01 |
2.76 |
| Main.WebHome |
0.76 |
2.92 |
3.67 |
| Main.PerformanceTestPage |
0.77 |
2.25 |
3.02 |
| TWiki.GoodStyle |
0.74 |
2.37 |
3.11 |
| TWiki.TextFormattingRules |
0.90 |
6.74 |
7.64 |
| TWiki.DefaultPlugin |
0.78 |
2.53 |
3.31 |
| TWiki.TablePlugin |
0.86 |
5.52 |
6.38 |
The numbers are very close. I can't figure out why there is such a big difference in the initialization code between calls (the variation is more evident seeing the step-by-step process in
PluginOOApiJournal)
Additional Findings
- Relying on the AUTOLOAD to ignore calls to undefined subs, lazyload the preferences into the object hash and other stuff has a severe impact on the performance.
- With 8 plugins installed, using the DISABLEDPLUGINS preference to disable all but two (TWiki:Plugins.DefaultPlugin
and TWiki:Plugins,TablePlugin
), the initialization time for plugis was about 0.240 secs per call (2.40 secs for 10 calls), but uninstalling 6 of those plugins (remove or rename the .pm file) gives an initialization time of 0.077 secs per call!. The rendering process for all the plugins is only 22% more than the rendering for two plugins..
- Turning On the DEBUG flag and having a lot of debug statements during benchmark is BAD
(see PluginOOApiJournal)
- Both OO plugins and traditional plugins can coexist without a severe performance penalty.
- If there are some plugins listed in the preference INSTALLEDPLUGINS, the Plugins::initialize1 will register those plugins twice (thus performing the whole operation up until the question
if (!$user)
). This is not an issue if only 1 or 2 plugins are listed, but it can be a severe penalty if more than 10 are listed.
- The Plugin::registerPlugin sub does three things: check if the plugin can be called (has a valid topic), retrieve the $user if the $user is unknown (when called from Plugin::initialize1), register the plugin when the $user is known (when called from Plugin::initialize2)
- In some cases, the OO plugin perform faster than the traditional plugin.
Further Researchs and questions
- Futher study is needed to test how the performance is impacted by adding another plugin of each kind (00 and traditional)
- This may sound weird, but Plugins::applyHandlers is being called 7 times before the plugins initialization. Further research is needed.
- During the rendering process, why is there a need to cicle line by line and then call outsidePREHandler and insidePREHandler for each line as apropiate?
--
RafaelAlvarez - 08 Sep 2004
Content of the attached file
| File |
Description |
/data/PerformanceBlankPage.txt |
Blank Page (with only a -) used for performance tests |
/data/PerformanceTestPage.txt |
A page that don't use any plugin used for performance tests |
/data/TWikiPerformance.txt |
The page with the Journal of this experiments |
/data/WebHome.txt |
The webhome found in the control installation |
/lib/TWiki/Plugins.pm |
The modified Plugin.pm |
/lib/TWiki/Contrib/Test/Mock/MockCGI.pm |
A Mock of the CGI module |
/lib/TWiki/Plugins/DefaultPlugin.pm |
The OO version of TWiki:Plugins.DefaultPlugin |
/lib/TWiki/Plugins/PluginSkel.pm |
The base OO plugin. All OO-styled plugins must extends this |
/lib/TWiki/Plugins/TablePlugin.pm |
The OO version of TWiki:Plugins.TablePlugin |
/tools/bench |
The shellscript used to collect the results |
/tools/*.fix |
All the fixtures used |
/tool/timeview.pl |
The Perl script used to test performance. Uses the Benchmark module |
Comments
Interesting work. Very much reflects my own experience when optimising the Plugins discovery mechanism.
I find it very difficult to theorise why there is such sensitivity in the initialisation process. I have benchmark programs that when run 5 times in succession have up to 20% variance in initialisation time, and it's different for each set of 5 runs. I suspect the problem is that the benchmarks run so quickly that the sampling just isn't frequent enough to give a true picture.
The
applyHandlers calls before initialisation are coming from
getSessionValue, which is in turn called from
getPreferencesValue. Using
caller I tracked it down to here:
Plugins::initialize1 start
TWiki::Prefs,../lib/TWiki/Prefs.pm,777 INSTALLEDPLUGINS
TWiki::Prefs,../lib/TWiki/Prefs.pm,777 DISABLEDPLUGINS
Plugins::initialize1 end
So it's actually only the first two that are done before handler registration, so I guess neither of them can be session variables. Which seems OK, though to me they are constants that should be in TWiki.cfg, not TWiki variables, especially given the cost of expanding them all the time.
--
CrawfordCurrie - 09 Sep 2004
There should be another mechanism to specify which plugins are disabled (something like a disable/enable button in the plugin page or in some admin panel or something) and to specify the rendering order of the plugins (the sole purpose of INSTALLEDPLUGINS, AFAIK). I notice that in the attached implementation the ordering is not being respected, because I treated INSTALLEDPLUGINS and the discovered plugins as the same thing.
--
RafaelAlvarez - 09 Sep 2004