GROOVY-12023 invokednymic improvements maybe

Jochen Theodorou <[email protected]> Wed, 3 Jun 2026 19:46:48 +0200
Newsgroups gmane.comp.lang.groovy.devel
Message-ID <[email protected]>
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