Updated version at http://cr.openjdk.java.net/~skuksenko/jsr335/8024635/webrev.02/ according comments: - remove "null check" comment near Object.requireNonNull calls - distort/(change parameters order) in key-purposed MethodType constructor - move count-slot logic from checkPType to checkPTypes.
On 09/26/2013 11:09 PM, John Rose wrote: > (Quick note on format: I have changed the subject line to include the > conventional bug number and size estimate. The bug number makes it easier to > track in mailers.) > > On Sep 26, 2013, at 9:22 AM, Sergey Kuksenko <sergey.kukse...@oracle.com> > wrote: > >> Hi All, >> >> I updated fix. >> You may find it here >> http://cr.openjdk.java.net/~skuksenko/jsr335/8024635/webrev.01/ >> >> See my comments and new webrev descition below >> >> On 09/18/2013 12:59 AM, John Rose wrote: >>>>> We can tweak that later if necessary. There are probably only a small >>>>> number of such strings, so maybe a small leaky map would do the trick as >>>>> well. >>>> In case of small amount of such strings we will get a huge overhead from >>>> any kind of map. >>> I would expect O(10^1.5) memory references and branches. Depends on what >>> you mean by "huge". >> Sure. I think the question to use external map or field may be decided >> later when/if it will be needed. >> Just some statictics, I've collected on nashorn/octane benchmarks >> (statistics collected only for the single(first) benchmark iteration: >> mandreel: 514 unique strings, toMethodDescriptorString called 69047 times >> box2d: 560 unique strings, 34776 toMethodDescriptorString invocations. > > Good data; thanks. > >> >>> Would there be any benefit from recording the original string from the >>> constant pool? The JVM path for this is >>> SystemDictionary::find_method_handle_type, which makes an upcall to >>> MethodHandleNatives.findMethodHandleType. Currently only the ptypes and >>> rtype are passed, but the JVM could also convert the internal Symbol to a >>> java.lang.String and pass that also. In that case, MT's created by JVM >>> upcalls would be pre-equipped with descriptor strings. >>> >>> This is probably worth an experiment, although it requires some JVM work. >> >> I am definitely sure that we shouldn't do that. >> Right now caching desriptor strings is internal decision of MethodType. >> Otherwise it will make interfaces more complex. > > Yes, I agree. Also, it would only benefit the 514 calls which introduce > unique strings, whereas your change helps the 69047 calls for descriptor > strings. > >>> I hope you get my overall point: Hand optimizations have a continuing >>> cost, related to obscuring the logical structure of the code. The >>> transforming engineer might make a mistake, and later maintaining engineers >>> might also make mistakes. >>> >>> https://blogs.oracle.com/jrose/entry/three_software_proverbs >> >> And it doesn't mean that we should afraid any kind of optimization. >> Lucky positions - someone(or something) will optimize it for us. But >> sometimes JIT (even smart JIT) is not enough. > > Sometimes. When that happens we reluctantly hand-optimize our code. > >> Let's back to the patch.construstors >> I decided not change original checkPType/checkRType code, except >> explicit Objects.requireNonNull. The problem here that we are creating >> new MethodType objects which are already exists, we have to create MT as >> a key for searching in the internedTable. Ratio between already exiting >> and new MethodTypes a quite huge. >> Here is some statistics I've collected on nashorn/octane benchmarks >> (statistics collected only for the single(first) benchmark iteration: >> >> mandreel: >> - 1739 unique MethodTypes, >> - 878414 MethodType.makeImpl requests; >> box2d: >> - 1861 unique MethodTypes, >> - 527486 MethodType.makeImpl requests; >> gameboy: >> - 2062 unique MethodTypes, >> - 311378 MethodType.makeImpl requests; >> >> So >> 1. MethodType constructor do checkPTypes/checkRType which are frequently >> (99%) useless - we already have the same (checked) MethodType in the >> internTable. >> 2. Less than 1% of created MethodTypes will be stored into internTable, >> but reusing that MethodType objects prevent ti from scalar replacement. >> (I've heard that Graal may do flow-sensitive scalar replacement, but C2 >> can't do). >> >> What I did. I separate constructors: >> - for long lived MethodTypes with all checks, >> - for short lived MethodTypes used only as key for searching in the >> InterTable, no checks are performed. >> if we didn't find MethodType in the table we always create a new one >> (with checks) that is required in less than 1% cases. And we remove at >> least one obstacle for scalar replacement. Unfortunately, scalaer >> replacement for expression "internTable.get(new MethodType(rtype, >> ptypes))" was not performed, the reason may be evaluated, and hope it >> would be easier to achieve scalarization in this case. > > Brilliant. I love this. > > The constructor signatures are too similar given the subtle difference > between their semantics. Please distort the signature of the extra-sensitive > constructor. (Imagine the wrong one accidentally invoked by helpful IDE code > completers.) Put in a leading int dummy argument, or reverse the parameters, > or some similar hack. (If we had named constructors, we'd hack the name > instead.) I admit this is of marginal benefit, since there is only one place > where the constructors are called, but it's good style. If there were more > places calling the constructor, we'd absolutely want a clear distinction > between checked and unchecked. > > If it still makes sense, I wouldn't mind moving the slot-count logic > (long/double adjustment) out of checkPType into checkPTypes. But I think > that's moot with this much better change. > - slots += checkPtype(ptype); > + checkPtype(ptype); > + if (ptype == double.class || ptype == long.class) { > + slots++; > + } > > Good work. Reviewed. (You need another reviewer, I think.) > > — John > -- Best regards, Sergey Kuksenko