I've moved the patch to 
[ https://github.com/forax/clojure/tree/indy-reflect | 
https://github.com/forax/clojure/tree/indy-reflect ] 

Rémi 

>> De: "Ghadi Shayban" <gshay...@gmail.com>
>> À: "clojure" <clojure@googlegroups.com>
>> Envoyé: Jeudi 23 Mai 2019 05:43:46
>> Objet: Re: Use invokedynamic instead of the reflection API when possible

>> Hi Rémi! What a pleasant surprise to see your name here. The whole community
>> owes you a great deal of gratitude for your work.

>> I'm hoping to be at JVMLS this summer to talk about some indy/condy 
>> experiments.
>> Alex summed up the philosophy well: we don't do stuff just because it's
>> possible to do, but because it satisfies some objective that seems worth 
>> doing.
>> There are others, but improving peak performance (which is already quite 
>> good,
>> thanks to Rich's design and HotSpot) and improving startup time are two
>> interesting objectives.

> I've sumitted a talk at the JVMLS about adding the support of lazy static 
> final
> in Java, which may interest you :)

>> One complaint often heard in the community is long startup time. Clojure can 
>> be
>> AOT compiled, which helps. We could also cache bytecode in a way that is
>> sensitive to Maven or git dependencies. The rest of this post assumes that we
>> have bytecode and not raw s-exprs. We could probably improve startup by 
>> 30-50%
>> with bytecode changes alone, but I don't think that is a big enough number to
>> assuage complaints, and personally I don't think this is one of the "large
>> challenges" that Clojure programmers face day-to-day. It is important for
>> containerization and AWS Lambda to have strategies for fast startup, maybe 
>> that
>> needs more assistance from either the JVM (Class Data Sharing, Application 
>> CDS,
>> etc.) or execution substrates like AWS Lambda. (The #1 way to help with 
>> startup
>> is to depend on fewer dependencies and load fewer classes, and that's is a
>> cultural thing that you can't solve in a compiler.) Some users are looking
>> toward Graal native-image for fast startup, but there are so many 
>> restrictions
>> with that tool that I don't even know what to say. A Lisp with eval is an
>> open-world assumption, native-image is closed world, and it's Not Java.

> it's not Java but it's moving in the direction to be more Java.

>> That being said, if you look at Clojure bytecode, in general a lot work 
>> happens
>> in <clinit> that can be deferred until an indy instruction bootstraps. (The
>> current strategy predates indy and certainly condy). For example, in
>> https://gist.github.com/ghadishayban/72a87c8e12cd66b0f4e285c1754157f5 there 
>> are
>> two constants (a Pattern and a Var) which get initialized in <clinit> and
>> stuffed into final fields. During the load of Clojure namespaces, we load a 
>> lot
>> of similar, larger datastructures that serve as Var's metadata.

> here the Var seems to be an indy more than a condy because you want Var +
> getRawRoot + checkcast, and the Pattern is more an condy

>> Another constant example is
>> https://gist.github.com/ghadishayban/f7b4c2206836f29d7e9f8cd614cdd2d1
>> With condy, ldc's can take bootstrap methods, so we can get rid of the array
>> construction and defer the <clinit> work, making the meat of the code
>> degenerate to:

>> ldc `:id` (Keyword.class)
>> aload 1
>> ldc `:byte`
>> aload 2
>> ldc `:is`
>> aload 3
>> invokestatic IPersistentMap.of(Object,Object,Object,Object,Object,Object) 
>> (this
>> API doesn't exist, but should!)
>> areturn

> yes, the keyword are condy (or an indy + constant method handle), for the Map,
> you can have an indy that works like the String concatenation introduces in
> Java 9, an indy + a String explaining how to create the map, something like
> ":id,:byte,:is" is all keys are constant or ":is,?,:is" if the second key is
> not constant.

>> We can significantly reduce the bytecode size of regular Clojure functions,
>> which is a proxy for better inlining+peak performance, and defer all the 
>> clinit
>> setup, improving startup.

> yes.

>> (Aside: notice that three of the six components in the map are static, only 
>> the
>> right hand side is dynamic. We could pre-fab an array factory indy that has 
>> the
>> static parts filled in already. I've tried this, and it didn't pay off except
>> with larger maps.)

> the idea with indy is to transform the String of the recipe to a serie of 
> method
> calls that creates the PersistentMap and let the JIT propagate the constants.

>> There are a couple of other invocation types (protocol invokes and keyword
>> invokes) that open-code a PIC in the bytecode, with static fields for the
>> caches. There are various strategies like MutableCallSite with GWTs to handle
>> this, but hey you wrote the cookbook on this subject.

> take a look to the patch, you have the code for the inline cache.

>> In Clojure 1.8, the compiler learned "direct linking", which made calls to
>> Clojure functions call a static method. Previously :

>> getstatic clojure.lang.Var
>> invokevirtual clojure.lang.Var.getRawRoot()
>> checkcast clojure.lang.IFn
>> push arguments...
>> invokeinterface clojure.lang.IFn.invoke(.....)

>> With direct linking:
>> push arguments
>> invokestatic someFunction.invokeStatic(....)

>> Direct linking is not the default except for clojure.core itself comes direct
>> linked, but direct linking traded away dynamicity for performance. (You can't
>> reload things that are direct linked) Since a Var is essentially a box 
>> around a
>> volatile field, there are other ways of getting the performance of the
>> invokestatic without losing the dynamicity.

> you can even get ride of the volatile read :)
> take a look to
> https://github.com/forax/exotic/blob/master/src/main/java/com.github.forax.exotic/com/github/forax/exotic/MostlyConstant.java

>> Reflection is another use-case, but as Alex mentioned, the general 
>> suggestion to
>> users is: don't write reflective code. One of the few compiler flags that 
>> exist
>> is: (set! *warn-on-reflection* true)

>> Anyways, thanks for starting this discussion

> I think a good start is to take a look to Var + Keyword, i will see what i can
> do :)

> Rémi

>> On Wednesday, May 22, 2019 at 9:47:53 PM UTC-4, Alex Miller wrote:
>>> Hi Rémi! Thanks for all your work on ASM and other JVM stuff over the years 
>>> by
>>> the way.
>>> We have of course been poking at indy off and on over the years and there 
>>> have
>>> been a number of experiments with it. Ghadi Shayban has probably done the 
>>> most
>>> work on that and he probably has more useful feedback than I would on the
>>> technical aspects of the code.

>>> Based on where we are in the release cycle right now, I expect that we 
>>> probably
>>> aren't ready to engage with this today and it might be a couple months 
>>> before
>>> we are in the meat of the next release and open to it. Some quick thoughts 
>>> and
>>> questions though...

>>> 1. As with anything we work on, we don't stuff just because it's possible 
>>> to do
>>> but because it satisfies some objective that seems worth doing. I assume the
>>> target benefit here is performance, but is that really it? Are there other
>>> benefits? Are there downsides to using indy vs reflection?

>>> One big thing here is that generally we expect people to remove reflective 
>>> calls
>>> in performance-sensitive code via type hints (and some people more 
>>> thoroughly
>>> try to remove all use of reflection). Thus my expectation would be that the
>>> majority of users would experience no improvement or improvements only in 
>>> parts
>>> of the code that are considered not important from a performance 
>>> perspective.
>>> If we're adding code that increases ambiguity (via having multiple 
>>> invocation
>>> paths which might have different failure modes) without a lot of benefit to
>>> users, then that prioritizes this pretty far down the list for me.

>>> 2. You mentioned the caller sensitive stuff - can you point at some 
>>> resources
>>> about what those are? I guess those are calls checking security policy, etc?

>>> 3. We did some work in the last release to avoid reflective calls to
>>> module-private methods, by modifying our reflective search to prefer 
>>> interfaces
>>> or classes that are module accessible. Does module accessibility affect indy
>>> calls as well?

>>> 4. Clojure has very few compiler / RT flags (and it's most common to use 
>>> none of
>>> them), and that's pretty intentional. Ideally we would not want a
>>> clojure.compiler.emit-indy flag but maybe you added this just to make the 
>>> new
>>> work switchable.

>>> 5. We are somewhat sensitive to trying to make AOT-compiled code work with 
>>> later
>>> Clojure runtimes as much as possible (we don't guarantee binary 
>>> compatibility
>>> but we have a very good track record here and try to never break that
>>> expectation). As such, we generally never change signatures in RT or 
>>> Reflector
>>> or other important interfaces and make only additive changes. I think this
>>> patch is additive in that way, so that's good, but would want to carefully
>>> consider the new publicly accessible methods in Reflector (as we'd be
>>> supporting them forever), like the change in toAccessibleSuperMethod (which 
>>> I'm
>>> not sure is needed?).

>>> There are other imo far more interesting possible uses for indy in places 
>>> where
>>> we care a great deal about performance and those are places where I would 
>>> place
>>> a lot higher priority. Ghadi, in particular, has investigated options for
>>> lazy-initing vars which could have a noticeable impact on startup 
>>> performance
>>> while minimizing the effect on subsequent calls like other approaches that 
>>> have
>>> been tried. Anyways, he can probably chime in on that more.

>>> Alex

>>> On Wednesday, May 22, 2019 at 6:16:58 PM UTC-5, Rémi Forax wrote:
>>>> Hi all,
>>>> now that Clojure is compatible with java 8, it can use invokedynamic where 
>>>> it
>>>> makes sense, i.e. when the compiler has no information to generate 
>>>> directly the
>>>> call in bytecode, instead of using the reflection API.

>>>> In fact, it's a little more complex than that,
>>>> - you can not fully replace all calls to the reflective API to use 
>>>> invokedynamic
>>>> instead, because you have restriction on the methods you can call (you can 
>>>> not
>>>> call a method annotated with @CallerSensitive for security reason) and
>>>> - using the method handle API doesn't necessary means that the calls will 
>>>> be
>>>> faster than using the reflection API if the JIT is not able to inline the
>>>> calls.

>>>> So the idea of the patch is to always generate invokedynamic at compile 
>>>> time but
>>>> at runtime to use the methodhandle API if there is a good chance that the 
>>>> call
>>>> will be inlined and fallback to call the Reflector API otherwise.

>>>> Obviously, i've not read how to contribute before writing the patch, so the
>>>> patch is currently available on github
>>>> [ https://github.com/clojure/clojure/pull/85 |
>>>> https://github.com/clojure/clojure/pull/85 ]

>>>> So now that i've read how to contribute, i think the first question to ask 
>>>> is:
>>>> does it make sense to allow the Clojure to use invokedynamic ?

>>>> regards,
>>>> Rémi

>> --
>> You received this message because you are subscribed to the Google
>> Groups "Clojure" group.
>> To post to this group, send email to clojure@googlegroups.com
>> Note that posts from new members are moderated - please be patient with your
>> first post.
>> To unsubscribe from this group, send email to
>> clojure+unsubscr...@googlegroups.com
>> For more options, visit this group at
>> [ http://groups.google.com/group/clojure?hl=en |
>> http://groups.google.com/group/clojure?hl=en ]
>> ---
>> You received this message because you are subscribed to the Google Groups
>> "Clojure" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email
>> to [ mailto:clojure+unsubscr...@googlegroups.com |
>> clojure+unsubscr...@googlegroups.com ] .
>> To view this discussion on the web visit [
>> https://groups.google.com/d/msgid/clojure/bb830fce-9a70-485f-aa23-5ae3f1e53413%40googlegroups.com?utm_medium=email&utm_source=footer
>> |
>> https://groups.google.com/d/msgid/clojure/bb830fce-9a70-485f-aa23-5ae3f1e53413%40googlegroups.com
>> ] .
>> For more options, visit [ https://groups.google.com/d/optout |
>> https://groups.google.com/d/optout ] .

> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> [ http://groups.google.com/group/clojure?hl=en |
> http://groups.google.com/group/clojure?hl=en ]
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email
> to [ mailto:clojure+unsubscr...@googlegroups.com |
> clojure+unsubscr...@googlegroups.com ] .
> To view this discussion on the web visit [
> https://groups.google.com/d/msgid/clojure/1458192544.1813170.1558623383373.JavaMail.zimbra%40u-pem.fr?utm_medium=email&utm_source=footer
> |
> https://groups.google.com/d/msgid/clojure/1458192544.1813170.1558623383373.JavaMail.zimbra%40u-pem.fr
> ] .
> For more options, visit [ https://groups.google.com/d/optout |
> https://groups.google.com/d/optout ] .

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/clojure/553514973.1829535.1558624807188.JavaMail.zimbra%40u-pem.fr.
For more options, visit https://groups.google.com/d/optout.

Reply via email to