Looking at Peter's work here is still on my long TODO list, but I was
hoping first to get in my concurrency correctness fixes for core
reflection, which conflicts slightly...

On Sun, Nov 30, 2014 at 1:48 PM, Peter Levart <peter.lev...@gmail.com> wrote:
> Hi Joel,
>
> I managed to find some time to create some tests for this patch:
>
> http://cr.openjdk.java.net/~plevart/jdk9-dev/Class.getMethods/webrev.07/
>
> Both MethodTable and HashArray unit tests are provided. I had to create a
> special TestProxy to access package-private classes from the tests.
>
> There are no changes to j.l.Class or j.l.r.Method from webrev.06 (I just
> re-based them to current tip).
>
> I also included the patch to StarInheritance test that I forgot to include
> in webrev.06.
>
> Comments inline...
>
> On 11/13/2014 10:39 AM, Joel Borggrén-Franck wrote:
>>
>> Hi Peter,
>>
>> As always, thanks for taking a look at this,
>>
>> This is quite big so in order to make this more approachable perhaps you
>> can split the patch up into a series? If you start with creating the
>> MethodTable interface, adding tests for how the interface should behave and
>> refactored the current MethodArray into implementing that interface while
>> also changing the lookup logic that would be easier to review.
>
>
> Well, there's not much to refactor in MethodArray when implementing
> MethodTable. They are two entirely different APIs with entirely different
> implementations.
>
>> Then you could add different implementations of MethodTable (with
>> additional unit tests) as follow up patches.
>
>
> You can view the MethodTable.SimpleArrayImpl as the basic implementation of
> the MethodTable API  and a replacement for MethodArray.
> MethodTable.HashArrayImpl is the alternative implementation for bigger
> sizes. The same unit tests are executed against both implementations.
>
>> I am a bit concerned about the size and scope of the implementations. In
>> general I would prefer if you targeted these to the precise need of core
>> reflection today. If you want to expand these to general purpose data
>> structures (even internal ones) I think that is a larger effort.
>
>
> I stripped HashArray and only left those methods that are needed to
> implement MethodTable API and execute the tests.
>
>> In general I think the changes to Class are sound, but there is a slight
>> change in the default method pruning. The call to removeLessSpecifics was
>> deliberately placed at the end, so that all default methods would be present
>> (the algorithm is sensitive to the order of pair vise comparisons). Since we
>> add methods in a deterministic order, I think consolidate() as you go should
>> result in the same set of methods, but I haven’t 100% convinced myself of
>> this just yet.
>
>
> I think it results in the same methods. I haven't yet found an example where
> it would result in different set of methods. All JDK classes return same
> methods with current implementation as with patched one.
>
>> Have you double checked that all methods returning root Method/Ctors are
>> private?
>
>
> I checked all usages of private methods that I have changed and are now
> returning root objects and made sure those are copied before being exposed
> to the outside or being modified.
>
> Regards, Peter
>
>
>> On 5 nov 2014, at 17:58, Peter Levart <peter.lev...@gmail.com> wrote:
>>
>>> Here's new webrev:
>>>
>>> http://cr.openjdk.java.net/~plevart/jdk9-dev/Class.getMethods/webrev.06/
>>>
>>>
>>> The optimizations made from webrev.05 are:
>>>
>>> - getMethod() skips construction of MethodTable if there are no
>>> (super)interfaces.
>>> - getMethods() returns just declared public methods if there are no
>>> superclass and no (super)interfaces.
>>> - comparing method parameter types is optimized by adding two methods to
>>> Method/LangReflectAccess/ReflectionFactory.
>>>
>>> New MethodTable implementation based on a linear-probe hash table is a
>>> space/garbage improvement. I took IdentityHashMap, removed unneeded stuff
>>> and modified it's API. The result is a HashArray. It's API is similar in
>>> function and form to java.util.Map, but doesn't use separate keys and
>>> values. An element of HashArray is a key and a value at the same time.
>>> Elements are always non-null, so the method return values are unambiguous.
>>> As HashArray is a linear-probe hash table and there are no Map.Entry objects
>>> involved, the underlying data structure is very simple and memory efficient.
>>> It is just a sparse array of elements with length that is always a power of
>>> two and larger than 3 * size / 2. It also features overriddable element
>>> equals/hashCode methods. I made it a separate generic class because I think
>>> it can find it's usage elsewhere (for example as a cannonicalizing cache).
>>>
>>> Since HashArray based MethodTable is more space-efficient I moved the
>>> line between simple array based and HashArray based MethodTable down to 20
>>> elements to minimize the worst-case scenario effect. Calling getMethods() on
>>> all rt.jar classes now constructs about 3/4 simple array based and 1/4
>>> HashArray based MethodTables.
>>>
>> HashArray.java:
>>
>> I was hoping for a decent set of unit tests for the new HashArray<T> data
>> structure. I think it is reasonable to test the corner cases/non-trivial
>> areas of the table (closeDeletion(), rezise() etc). Perhaps also run these
>> over the simple implementation. Also, please document thread safety (there
>> is none IFAICT it should just be noted).
>>
>> Instead of using inheritance to change the behavior of equals() and hash()
>> you could give it two lambdas at table creation time, a ToIntFunction<T> for
>> hash() and a BiPredicate<T,T> for equals(). Might not give you the
>> performance we need though.
>>
>> Note that the file doesn’t actually compile in jdk9/dev, you have two
>> unchecked casts and we build with -Werror.
>>
>> MethodTable.java
>>
>> HashMapImpl is missing serialVersionUID, but it looks like this class
>> won’t be needed at all.
>>
>>
>>> Here's also Martin's ManyMethodsBenchmark:
>>>
>>> Original:
>>>
>>> Base class load time: 129.95 ms
>>> getDeclaredMethods: 65521 methods, 36.58 ms total time, 0.0006 ms per
>>> method
>>> getMethods        : 65530 methods, 47.43 ms total time, 0.0007 ms per
>>> method
>>> Derived class load time: 32216.09 ms
>>> getDeclaredMethods: 65521 methods, 35.05 ms total time, 0.0005 ms per
>>> method
>>> getMethods        : 65530 methods, 8068.66 ms total time, 0.1231 ms per
>>> method
>>>
>>>
>>> Patched (using HashArray based MethodTable):
>>>
>>> Base class load time: 126.00 ms
>>> getDeclaredMethods: 65521 methods, 36.83 ms total time, 0.0006 ms per
>>> method
>>> getMethods        : 65530 methods, 45.08 ms total time, 0.0007 ms per
>>> method
>>> Derived class load time: 31865.27 ms
>>> getDeclaredMethods: 65521 methods, 35.01 ms total time, 0.0005 ms per
>>> method
>>> getMethods        : 65530 methods, 78.05 ms total time, 0.0012 ms per
>>> method
>>>
>>>
>>> All 86 jtreg test in java.lang/Class/ and java/lang/reflect/ still pass.
>>>
>> I have seen discussion about allocation, should we measure and compare?
>> You can probably use the Hotspot impl of ThreadMXBean to get the allocation
>> in the tread.
>>
>> Also, it might be time to fix the boolean parameters:
>>
>> 2741         Method[] declaredMethods = privateGetDeclaredMethods(true);
>> 2742         Class<?> superclass = getSuperclass();
>> 2743         Class<?>[] interfaces = getInterfaces(false);
>>
>> Perhaps just add boolean constants somewhere so that it is easier to
>> decode.
>>
>> 2741         Method[] declaredMethods =
>> privateGetDeclaredMethods(PUBLIC_METHOD_ONLY);
>> 2742         Class<?> superclass = getSuperclass();
>> 2743         Class<?>[] interfaces = getInterfaces(NO_COPY_RESULT);
>>
>> or so.
>>
>> HashArray.java:
>>
>> 155         if (lookupObj == null) throw new NullPointerException();
>>
>> use Objects.requreNonNull() ?
>>
>> cheers
>> /Joel
>>
>

Reply via email to