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.