Tuesday, February 28, 2017, 7:06:45 PM, Woonsan Ko wrote:

> On Sat, Feb 25, 2017 at 4:16 AM, Daniel Dekany <[email protected]> wrote:
>> Saturday, February 25, 2017, 7:59:55 AM, David E Jones wrote:
>>
>>> These all sound like good changes, and clarify what is really going on.
>>
>> (Basically, I'm decreasing code size and complexity before the more
>> interesting changes are started.)
>>
>>> Removing object wrapping IMO is more important than removing the
>>> *Template*Model* interfaces.
>>
>> (The two come hand in hand really. If we had no TemplateModel-s, we
>> had no ObjectWrappers either.)
>>
>>> These interfaces don't have the same runtime and
>>> GC overhead as object wrapping and they can be nice for type checking to 
>>> make
>>> sure the operation in the template can actually be done on the object. 
>>> Still,
>>> with those interfaces it would be nice to use more standard ones where they
>>> exist (Java collection interfaces for example).
>>>
>>> On the object wrapping topic there might be some useful things to learn from
>>> Groovy, though as FTL doesn't compile to a class not all of the same 
>>> applies.
>>> The biggest performance overhead in Groovy is all the type conversions that 
>>> it
>>> does to be a type optional scripting language. I've done a lot of work on
>>> optimizing Groovy code since I made a decision years ago to write a big
>>> framework mostly in Groovy.
>>>
>>>
>>>
>>> I don't know that this is directly applicable to the discussion, but here is
>>> an article I wrote last year on some stuff I learned about Groovy
>>> optimization, and it may be that we can avoid some of these issues:
>>>
>>>
>>>
>>> https://dzone.com/articles/how-to-make-groovy-as-fast-as-java
>>>
>>>
>>>
>>> Even Groovy has a sort of object wrapper, ie its meta class. It's one thing
>>> that would be great to disable when you're not using the Groovy features 
>>> that
>>> rely on it... maybe someday I'll even look into this in Groovy itself or 
>>> open
>>> the discussion in that community (for now I'm up to my neck in work :) ).
>>
>> Luckily, I don't think this kind of performance is priority for a
>> template engine like FreeMarker, or at least that it's a high enough
>> priority to trump easy extendibility and such. That's because it's not
>> a programming language. Its a kind of domain specific language
>> basically. For something that generates output, hopefully you don't
>> have lines that are executed for millions and millions of times,
>> unless you also generate heck of a lot of output, which has to be
>> stored or sent, which will then (hopefully...) dominate your
>> performance anyway.
>>
>> Also, for us, a key difficulty is that in general we can't have
>> assumptions about the data-model content. It's outside the template. I
>> just don't know that in ${user.name}, what `user` will be. Sometimes
>> though, the data-model is a bean, so if that's declared inside the
>> template, then you have better chances. But... what if User has no
>> fullName property, but some User subclass has, and you write
>> `${user.fullName!'unknown'}`? Should now I also require the user to do
>> an explicit type cast? Anyway, one thing is sure, templates must be
>> able to operate in non-static mode. Adding an alternative static mode
>> is way out of reach (given the resources we can burn). And without
>> that... our performance options are rather limited. Well, we can pull
>> some tricks, like with indy, even if you don't know what the type of
>> `user` will be, you can during execution learn that it's in practice
>> always an User, and then you can link User.getName() as if it was a
>> "static" invocation (with a guard condition so if it's not an User
>> after all, you can take the slow route).
>>
>>> A good step in the right direction might be to start with the use of 
>>> standard
>>> Java interfaces. For example when you do a #list it could be required that 
>>> the
>>> object you are iterating over implements either the List or Iterator
>>> interfaces (perhaps with optimization to detect RandomAccess lists like
>>> ArrayList to avoid creating an Iterator).
>>> This doesn't get us all the way down
>>> the road, but is a start.
>>
>> What I consider instead of ObjectWrapper+TemplateModel-s is the
>> following. We don't wrap values into anything. Instead we will have an
>> interface called MOPImplementation (where MOP stands for Meta Object
>> Protocol), which declares methods for each *operations* that FTL is
>> interested in, such as `int getSize(env, v)`, `boolean isEmpty(v,
>> env)`, `Iterator<Object> getListItemIterator(v, env)`, `Object
>> getParent(v, env)` (for XML DOM-s and such), etc. So if you, for
>> example, need to #list a value, you get the MOPImplementation instance
>> based on the class of the value (it's the class of the "raw" value,
>> not a TemplateMode), and then call getListItemIterator on it, which
>> either gives you the Iterator or throws an
>> TemplateOperationNotSupportedException. A key idea behind this is that
>> you do not have a MOPImplementation per value instance. Like if you
>> have two List-s, they all will use the same shared MOPImplementation
>> instance (something like
>> o.a.f.core.mop.impl.ListMOPImplementation.INSTANCE) to access those
>> lists. The MOPImplementation isn't bound to a value, it's only bound
>> to the class of the value (i.e. you have different MOPImplementation
>> instance for List-s and another for arrays, another for DOM node sets,
>> etc.). In comparison, with ObjectWrapper-s you have to create at least
>> as many TemplateSequenceModel instances as many distinct List
>> instances you will touch, because TemplateModel-s are bound to a
>> certain value instance. (It's "at least" as many, because if you
>> access the same List instance for the second time, you often has
>> re-create the TemplateModel, because caching the TemplateModel-s
>> doesn't worth it usually. If you assign the list to an FTL variable,
>> and then access the variable for multiple times, then no such
>> re-wrapping will happen of course, because you store the TemplateModel
>> in the variable, not the List directly.)
>>
>> So, as you see, it's practically as flexible as ObjectWrapper-s. We
>> don't hard-wire what's listable, etc., because it all depends on the
>> set of MOPInterface classes added to the Configuration. So you can
>> still make classes that we have never seen to be listable, or change
>> the standard behavior (without changing the source code), etc.
>>
>> But, I don't think it's a huge win in performance... it's hopefully
>> some though:
>>
>> - You will generate less garbage to be GC-ed for sure, and that's good.
>>
>> - If you just pass through values the template, like in
>>   `obj.m(theValuePassedThrough)`, you don't have to do anything extra
>>   with it. (Currently, you have to wrap `theValuePassedThrough`, only
>>   to unwrap later... ouch!)
>>
>> - But... if you assign a value to an FTL variable, it won't keep the result
>>   of the "introspection" anymore (because you just store the original
>>   object, not a TemplateModel that already knows all about it), so
>>   each time you try to do something with it, the MOPImplementation
>>   has to be chosen based on the class of the value. There are tricks
>>   to mitigate this effect, such as side-storing MOPImplementation next
>>   to the variable, but it brings code complexity, and some constant
>>   overhead.
>>
>> - Using indy might becomes feasible, as now what to do only depends on
>>   the class of the value, not on the value itself.
>>
>> But my real agenda with this is to make the code base less daunting
>> (because, we need attract contributors, not only users... this is
>> marked like that), and to make the API more user friendly.
>> TemplateModel-s, if you have them, are all over the place. For
>> example, when a user wants to implement a directive in Java, he gets
>> TemplateModel-s. The first thing they want to do is, quite invariable
>> I pressume, unwrapping them to the classes they know. But because of
>> the freedom ObjectWrapper-s and TemplateModel-s have, it's not even
>> guaranteed that that's possible. Basically, I don't want FTL to have
>> its own type system (as it has with TemplateModel-s). I just want it
>> to define a set operations (listing something, getting the size of
>> something, etc.) instead. That's much easier to grasp. And if you want
>> your Java code to behave like FTL, you can get a MOPImplementation.
>
> +1 on considering MOP as an alternative. Sounds interesting and more
> easily understandable and usable to me. Could you give a reference
> link to indy? I'm curious about how it can be used and something
> specific like how it handles a dynamic proxy object for instance.

I don't know which article is the best. Of course, there's quite a few
hits if you search for invokedynamic. (I guess this was good, but I
don't really remember: https://www.youtube.com/watch?v=qaf8sHjeQ2g)

What I was referring to are polymorphic call sites. Form a quick
glance on the Java API, I guess they could be implemented with
MethodHandles.guardWithTest-s and a MutableCallSite. But, I think we
should look into the Dynalink source code. In this case, starting out
from ChainedCallSite. Here's an illustration of what it does:
https://youtu.be/UIv86fUSORM?t=824

> Regards,
>
> Woonsan
>
>>
>>> -David
>>>
>>>
>>>
>>> ![](https://link.nylas.com/open/5xm8m568zhx9qyloglsg31huz/local-
>>> 178b2039-1688?r=ZGV2QGZyZWVtYXJrZXIuaW5jdWJhdG9yLmFwYWNoZS5vcmc=)
>>>
>>>
>>> On Feb 24 2017, at 9:18 pm, Daniel Dekany <[email protected]> wrote:
>>>
>>>> In FM3, I propose removing some legacy complexity:
>>>
>>>>
>>>
>>>> \- Remove BeansWrapper, which is the super class of
>>>   DefaultObjectWrapper, and add its functionality directly to
>>>   DeafultObjectWrapper. That we have this class hierarchy has purely
>>>   historical reasons. (Even if FM2, using pure BeansWrapper is
>>>   deprecated, and causes lot of pain and confusion because it's still
>>>   possible.)
>>>
>>>>
>>>
>>>> \- Remove the following BeansWrapper/DefaultObjectWrapper
>>>   settings, because they just exist in FM2 so that you can chose
>>>   between FM 2.3.0 compatibility and a better functionality:
>>>
>>>>
>>>
>>>>   - BeansWrapper.simpleMapWrapper (DefaultObjectWrapper ignores it)
>>>   - DefaultObjectWrapper.forceLegacyNonListCollections (will be in
>>>     effect false)
>>>   - DefaultObjectWrapper.iterableSupport (will be in effect true)
>>>>
>>>
>>>> PS I know that there's an idea of removing
>>> ObjectWrapper-s/TemplateModel-s altogether, and using a MOP instead,
>>> but that's a fairly brutal change and who knows if it will be
>>> affordable. So while I try not to invest much into
>>> ObjectWrapper-s/TemplateModel-s until we figure that out, I do at
>>> least want to remove stuff that we won't need for sure. And also, baby
>>> steps, easy things first... keep FM3 working all the time.
>>>
>>>>
>>>
>>>> \--
>>> Thanks,
>>>  Daniel Dekany
>>>
>>
>> --
>> Thanks,
>>  Daniel Dekany
>>
>

-- 
Thanks,
 Daniel Dekany

Reply via email to