From: Christoph Reck [mailto:[EMAIL PROTECTED]]
>
> I liked the WebMacro approach on the introspection. It did the
> job once for every class, placed the per-class data in a global
> map and reused this for every template. I know Geir has a concern
> of the single point cache bottleneck (have you measured the
> possible implication in a loaded system?).
Velocity does that. There is a central introspection class that does this
work, for the most part, and caches the information. So after the
introspector has seen a given class, the information is there.
And, once again, yes, there is an effect. It was discovered when I changed
from 'static' to dynamic introspection : by this I mean that the nodes
previously would introspect the first Context that came their way, and store
that information in class members. There were a few implications, including
threadsafety and what I call 'context safety'. Context safety is simply the
name I gave to the fundamental issue we are discussing here, namely that if
you had a reference
$foo.bar
then I thought it was a good thing if it was irrelevant what was in the
context : either a class with a getBar() method, or a Map with a 'bar' key,
or whatever. With that 'first context wins introspection', any subsequent
rendering attempts with a differently 'shaped' context elemnt would bomb -
because the method information was wrong.
However, it turned out that there was a tremendous performance hit (~100%
increase if a complicated enough template) for repeated code, like
#foreach() loops and VMs, because instead of having the introspection
information around and ready, the nodes then had to go to the introspection
cache every time. And that was with a single thread of execution. It would
only get worse once there was contention for the cache information. There
were solutions to that, but it would be making assumptions about velocity's
use, reducing the generalization, and I didn't think that was wise.
So the solution is the second-level 'context cache', where the context will
cache node-specific introspection information, and the nodes that care know
to look there. It's no problem - if the cache lookup fails, it turns to the
global cache. And then it caches the global introspector's results, meaning
that the global introspector is still the primary cache, so new class
introspection information is always available for other threads to use.
This means (and that is what the ContextSafetyTestCase shows) that you can
rely on Velocity's commitment to the 'bean-approach' to polymorphism rather
than the 'inheritance' approach. You can change the 'shape' of things in
the context from request to request, thread to thread, w/o any problems. I
think this is important when you have general use templates that are
#parse()-ed or VMs - for example a grid template that shows a dataset. All
you have to do is agree on the names of things....
> As far as I have looked at it, Velocity does a similar job but
> keeps the introspection cache on a per-request basis, thus only
> speeding-up foreach loops, and similar class accesses. I guess
> this means an performance impact if many different classes are
> put into the context and each is accessed only once.
Only the first time through, because the central introspection cache is
learning and caching. The next time through, on any thread, you will take
advantage of the centrally cached information. Just like WM.
With the context-local cacheing, you get a boost in 'same-merge' repeated
visits, i.e. #foreach() , #parse() and VMs.
It's like the L1 and L2 caching in general computer architecture - L1 is
smaller, faster and gets data from L2, and mostly useful for a single
thread/task, where L2 is larger, slower, gets data from regular memory, but
is able to contain information that spans multiple threads/processes.
> Velocity has a cache-enhancement that when parameters are sub-classes
> the additional access signature is also cached. Still this means
> dozens of java statements until the reflected method is executed.
Well, ignoring the fundamental Java penalty of interfaces and subclassing
(and there a substantial one, AFAIK), the context local bit takes care of
that.
> It seems that Geir has enhanced speed by doing a "try-the-obvious"
> getMethod() before entering the introspection mecanism. This makes
> the introspection mecanism/cache somewhat obsolete.
No. :)
> I may be wrong in my above understanding, since I have not done
> deep analysis after geir added the ica/icb stuff. Please correct
> me if I've made wrong statements.
I hope the information above clears up some of the mystery.
>
> Compiling (in memory please - to avoid being like JSP)
Of course :)
> means that
> the template is a class. References are simple Context.get method
> accesses. Identifiers are compiled into some type of method lookup.
> Since the classes in the context is not necesarily known at compile
> time (or may change thereafter), there must exist some type of
> post compliler and lookup tables for these accessor classes. This
> ends up in being something like a reflection cache - avoiding
> the Method.invoke().
Well, again, I dispute the fundamental assumption that we should switch from
'bean polymorphism' to 'inheritance polymorphism'. First, I love the
feature. It is one of the things that makes Velocity/WM so flexible and
easy. Second, I don't think that it's all or nothing.
> An implementation like this would be on the bleeding edge of
> technology and could possibly be a performance boost.
Interesting way to phrase it. I hope we don't go to the bleeding edge just
to be there. It's too easy to fall off (and bleed, I suppose.) I believe
that if there isn't a significant performance boost, then it's pointless.
I don't know what 'significant' is, but it has to be weighed against
complexity.
>
> To really improve performance we will need to assess
> different tradeofs:
> * central cache bottleneck
> * per-request cache overhead
> * compiled methods (e.g. SDK1.3 reflection proxy classes/templates?)
Tell me more about this...
> And a mixture of these...
>
> :) Christoph
geir