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 >>> >>> >>> >>>  >>> >>> >>> 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
