Tags:
performance1Add my vote for this tag create new tag
, view all tags

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

Edit | Attach | Watch | Print version | History: r19 < r18 < r17 < r16 < r15 | Backlinks | Raw View | Raw edit | More topic actions
Topic revision: r19 - 2006-04-24 - MeredithLesly
 
  • Learn about TWiki  
  • Download TWiki
This site is powered by the TWiki collaboration platform Powered by Perl Hosted by OICcam.com Ideas, requests, problems regarding TWiki? Send feedback. Ask community in the support forum.
Copyright © 1999-2017 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.