Really Lazy Compilation
The Problem
Here and then it has been argued that one of the facts which make TWiki slow is the huge amount of code to be compiled for every request, whether or not it is actually needed.
However, to avoid superfluous compilation it is not sufficient to simply move code out of the core (say,
TWiki.pm) into a plugin, or its younger yet-to-be-well-established cousin, the lightweight tag handler. This is because both mechanisms still associate a TWikiVariable (i.e. something looking like
%VARNAME{"attrs"}%) with a code reference: a reference to code which has been
compiled in advance.
The only way to avoid compilation of plugin or tag handler code is to
disable the plugin, which is (1) a global operation affecting the whole TWiki installation and (2) a tedious job for a TWiki admin if many TWikiVariable handlers are to be extracted into external modules.
In my opinion a
really lazy compilation would be if a subroutine (say,
TWiki::_TOC) is compiled no earlier as when a
%TOC% has been found in the topic to be viewed. No
%TOC% - no compilation of
TWiki::_TOC this time.
The Solution (maybe)
Perl
does offer a mechanism dedicated to that problem: Autoloading, as documented in
perldoc perlsub
(
http://search.cpan.org/author/NWCLARK/perl-5.8.7/pod/perlsub.pod#Autoloading
). Autoloading works by defining a
sub AUTOLOAD which is called whenever Perl wants to call an undefined subroutine.
So, let's say that there is no subroutine
sub _TOC in TWiki.pm, but TWiki calls
_TOC when it finds a
%TOC% variable in a topic. With autoloading, this isn't a fatal error: Perl calls
AUTOLOAD and passes the information that, originally, it was about to call
_TOC. The
AUTOLOAD routine can then load and compile the code implementing
sub _TOC and involve
_TOC on the fly. All subsequent calls to
_TOC will bypass the autoloading mechanism since now
_TOC is defined. Nifty, eh? (Ok, it may be not a very frequent use case to have several
%TOC% invocations in one topic. But anyway.)
What I'd like to investigate/hack/measure against a vanilla Dakar installation:
- Add an API to allowing to associate class names (instead of coderefs) with tag names
- Add stubs which call e.g.
TWiki::Plugins::Core::_TOC for %TOC%
- Add
TWiki::Plugins::Core.pm which requires TWiki::Plugins::Core::TOC.pm in its AUTOLOAD routine
...and so on. A positive effect on performance can only be achieved if I manage to remove all those routines from
TWiki.pm, so don't hold your breath. I'm going to hack around this during the next days/weeks. So if someone has opinions about the concept, or especially if someone has already tried it and found that it doesn't work, please speak up!
Final Notes
TOC is special
As
PeterThoeny pointed out in
MoveTOCToAPlugin,
%TOC% is a bit special since it has to be processed as the very last step before conversion to
HTML. But hey, nobody says that a
AUTOLOAD routine for
%TOC% couldn't do things like register another handler to be called immediately before rendering (like
preRenderingHandler).
Persistent Interpreters
This feature would have a marginally negative overall performance impact on installations with persistent interpreters (
ModPerl,
PersistentPerl), since every persistent process is likely to stumble over a topic containing
%TOC% sooner or later.
Has anybody tried this?
While writing this, I've found some code in
TWiki.pm which seems to have been introduced as a preparation for autoloading with the "standard"
AutoLoader /
AutoSplit mechanism:
# Uncomment this and the __END__ to enable AutoLoader
#use AutoLoader 'AUTOLOAD';
# You then need to autosplit TWiki.pm:
# cd lib
# perl -e 'use AutoSplit; autosplit("TWiki.pm", "auto")'
Has anybody tried / benchmarked this?
Update 21 Apr 2006
This isn't very efficient, performance isn't much affected. This is only partially due to the not very sensible splitting: Autosplitting
TWiki.pm, as it is in
TWikiRelease04x00 SVN, yields 85 autoloadable modules of together approximately 2500 lines. But fifteen of them, with approximately 500 lines, are required for even the simplest possible topic view (no variables at all, skin=text). So we're paying with fifteen fallbacks through
AUTOLOAD plus fifteen file open calls (system interfaces aren't for free even if they are in perl's C code) for 2000 lines of compilation. But even if I move those fifteen routines out of the
AUTOLOAD area, the effect is minimal (about 30ms).
Definitely, more research needs to be done.
Update 23 April 2006
WhatIsWiki?skin=plain is 2% faster when autoloading, while
WhatIsWiki?skin=pattern is 1% slower. The left bar in pattern skin has enough tags
to make the autoload penalty heavier than the compilation of 2000 lines of code.
--
Contributors: HaraldJoerg
Discussion
--
HaraldJoerg - 20 Apr 2006
Most, but not all, of the TWikiVariables are candidates to be moved into TWikiTags. As you noted in
MoveTOCToAPlugin, tags won't achieve any speedups without figuring out how to avoid compiling them unless necessary. OTOH, they shouldn't cost any
more, so moving them out won't hurt and might help down the road.
--
MeredithLesly - 21 Apr 2006
IIRC,
CrawfordCurrie put somewhere the code to allow lazyload from the registered sub.... I think is something like this:
$twiki->registerTagHandler('TAG',sub {
eval 'use TWiki::Plugin::MyTag';
&'TWiki::Plugin::MyTag::execute'(@_);
}
The same trick could be used by a plugin:
sub preRenderingHandler {
eval 'use TWiki::Plugins:Tag::MySubModule';
&'TWiki::Plugins:Tag::MySubModule::preRenderingHandler'(@_)
}
Disclaimer: This is untested code, from the top of my head. My perl-fu is not that strong, so perhaps there is some detail missing, but I can't find the topic where the implementation was shown by
CrawfordCurrie.
--
RafaelAlvarez - 21 Apr 2006
BTW, I did some experiments with AUTOLOAD. It was in a different context (but around plugins, anyway)
Check out:
http://twiki.org/cgi-bin/view/Codev/PluginOOApiJournal
In summary, using AUTOLOAD to call "unimplemented" handlers have a significant performance hit.
--
RafaelAlvarez - 21 Apr 2006
Rafael, You write:
In summary, using AUTOLOAD to call "unimplemented" handlers have a significant performance hit. I agree, this is true for every call invoked via the
AUTOLOAD fallback. But only once per request, and only for those tags which are actually expanded.
Say, for example, you incur the performance penalty for calling
%TOC%, but on the other hand you save the time which would be needed to compile the code to handle
%INCLUDE%,
%SEARCH%, and maybe
%EDITTABLE%,
%ACTIONSEARCH% and
%XPSHOWALLPROJECTS% as well. I'd
guess this would give a positive net effect. But because it's only a guess, I'll have to try it.
Of course this needs care. There's no point in lazily compiling
any tag which is likely to occur, for example, in a
WebLeftBar. Divining which handlers are suitable candidates for
AUTOLOAD, and whether this needs to be configurable, is left as a next step.
And, btw: Thanks for the pointer to
PluginOOApiJournal. Your article shows that it is, among other things,
really important to do the benchmarks with a sensible choice of test topics.
--
HaraldJoerg - 21 Apr 2006
In the middle of last year I built a TWiki which used AUTOLOAD for all handler functions. Nothing smart, I just autoloaded all the relevant functions in TWiki.pm. I can't remember the detailed numbers, but it caused a
huge performance hit, something like 20-30% slowdown, IIRC. Though I was using
AthensMarks to benchmark, an approach which is being badly flamed at the moment, so you can doubt the numbers if you want.
--
CrawfordCurrie - 22 Apr 2006
Well, while I don't doubt the numbers, I feel that the significance of plain
AthensMarks figures is, erm, questionable enough to justify a closer look.
The performance hit depends on which methods are autoloaded, and a lot more on topic, and on skin. Pattern skin, for example, has lots of tags in the WebLeftBar which call for the slow process of autoloading:
%IF{}%,
%INCLUDE{}%,
%ICON{}%,
%MAKETEXT{}%, not to mention the tags which need to be expanded when processing the included topic.
Eventually I have been able to construct a testcase where autoloading stuff from
TWiki.pm was
faster:
WhatIsWiki?skin=plain, autoloading selected 70 methods from TWiki.pm (instead of the 85 as readily available from the code), gives a 2% gain. With the same topic and pattern skin, there's a 1% performance
decrease.
I
thought that I could experiment further with autoloading plugins, but this may be wrong: A couple of them already do lazy compilation, with various techiques (string eval, require in an init routine).
--
HaraldJoerg - 22 Apr 2006
You hit the nail on the head. Testcases that show the performance as better have to be contrived. Why? Well, my theory at the time was that because the skin was so complex, and used so many variables, then almost everything had to be loaded for almost every page. Your numbers tend to bear that out.
So, I'm once again going to call for simpler default templates. Give autoload a chance!
--
CrawfordCurrie - 23 Apr 2006
Calling for simpler default templates is pointless if those default templates don't provide the functionality that's wanted.
--
MeredithLesly - 23 Apr 2006
Discussion that wasn't about lazy compilation refactored to SpeedingUpTWiki
--
MeredithLesly - 24 Apr 2006