> 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 --- 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/1458192544.1813170.1558623383373.JavaMail.zimbra%40u-pem.fr. For more options, visit https://groups.google.com/d/optout.