Hi all,
I spend a lot of time recently on trying to improve indy and well, the
current architecture makes it difficult to solve problems. Right now I
am stuck in a test that uses an in groovy written AST transform that
fails under certain conditions to fire the right exception... So I am
thinking of throwing away most of the branch. But there are some
take-aways that I will probably put into a new pull request.
* DefaultTypeTransformation can have some fastpaths by simply reducing
the method size by handling the common case and fall back to a new
method for more complex cases
* dgm helpers are strictly seen not needed in Indy actually. But I can
also not easily remove them from the build. I think static groovy
compiles directly against them. But what I will do is store a
MethodHandle in them, that I will then use in Indy, instead
* the cache in IndyInterface is in my eyes more a megamorphic cache. I
experimented with an alternate key mechanism, I think I will also keep that.
None of these change will make indy dramatically faster. The DTT change
will actually benefit everything that uses DTT.
There is more, more difficult things.
One is checking the metaclass for a change. So in the old callsite logic
if I change the metaclass of X then a callsite on Y is not affected by
it. If we change the ExpandoMetaClass, it will also not change the
callsite with Y as receiver. In indy we use the switchpoint logic, which
invalidates all callsites. That worked for getting things done, but is
too much for code that makes rapid changes to meta classes. So I am
considering giving up on the switchpoint and instead do a check similar
to what the old callsite caching does with checking for an identical
metaclass and its version. Or I start with the switchpoint, try to
recognize if there are too many changes and fall back to the old logic.
The switchpoint is cheap as check.
Another point is when we have to catch a GroovyRuntimeException to
unwrap it, because the GroovyRuntimeException is in some cases used to
control the MOP. That is what got me stuck on my branch, so it will try
again in a clean and isolated approach
I also tried to make a small inline cache where the fallback will just
forward to an older handle for the callsite to allow 3-4 changes to the
callsite before it needs different treatment. I may try this again in
the future, since I think this may help. I also tried in there to have
only one switchpoint for the whole PIC, but as I said, the switchpoint
is relatively fast. Having only one exception unwrap in there would
probably have better results. The less checks the handle have to perform
individually the better.
But frankly for this kind of information we would have to collect
something like a profile, which I consider doing, but that could result
in changing IndyInterface and Selector a lot. There is small gains, like
moving some information about the callsite, that is not changing on the
actual callsite instead of carrying them around in bound handles. That
looks small, but reduces method signatures.
in fact for the future I am considering having IndyInterface really only
as a entry point for the profile and then let different profiles handle
the details, including handing over to a different profile. No exact
plans here yet. But I think that can allow us doing some complex and
specialized strategies without loosing - it... really... the errors I
had to deal with where driving me mad.
Then there was also the idea of a slow path, where we can avoid more
checks and just invoke via meta class. I think here I will also have to
try again. I think having a simple path for highly mutating callsites
and also for the initial callsites might be of advantage.
Finally I think I have to add something that allows better debugging of
Indy. Maybe something where I can register a set of origin classes and
keep a structure for their callsites, that is accessible from a Groovy
script. Then a test could test its callsite state without writing tests
for invoking methods through IndyInterface. Not sure yet.
Will make any of this Grails faster? Maybe. The metaclass churn and the
exception handling overhead are both hot candidates for performance
improvements in a Grails environment.
Well, any thoughts and ideas are welcome
bye Jochen