RFR: 8061950: Class.getMethods() exhibits quadratic time complexity

Stanimir Simeonoff stanimir at riflexo.com
Wed Nov 5 18:27:38 UTC 2014


A very small issue:

MethodTable.java:

 697                     while (i < hwm && next == null) next = methods[i++];


In my opinion the condition should be while (next == null && i < hwm) as next
will be non-null on each invocation of next() and generally it's more
likely to exit the loop.
I suppose HashMapImpl is unused .

HashArray.java:
  82             Integer.highestOneBit(expectedMaxSize + (expectedMaxSize << 1))


This effectively multiples expectedMaxSize by 3, I guess you meant >>
instead.
Perhaps extra a method about effective capacity?

I will take a deeper look a bit later.

Stanimir


On Wed, Nov 5, 2014 at 6:58 PM, Peter Levart <peter.levart at gmail.com> wrote:

> On 11/01/2014 06:49 PM, Peter Levart wrote:
>
>> On 10/30/2014 07:13 PM, Martin Buchholz wrote: Which is why I was
>>> expecting to have to write an adaptive
>>> data structure that switches from linear search to hash-based when
>>> some threshold is exceeded.
>>>
>>
>> ...or two data structures. Since I have already arranged so that
>> construction of MethodTable gets an upper limit on the total number of
>> possible Method(s) that can be added to it, we can have two MethodTable
>> implementations and switch based on this number:
>>
>> http://cr.openjdk.java.net/~plevart/jdk9-dev/Class.getMethods/webrev.05/
>>
>
> Hi,
>
> I made some progress on this front. I created new MethodTable
> implementation (the third one) with similar performance as HashMap based,
> but producing half the garbage. I also added some optimizations to
> getMethod(), getMethods() and MethodTable in general that deal with special
> cases / parameter types comparison and have measurable impact on
> performance (for example loading all rt.jar classes and calling
> getMethods() for them):
>
> Original:
>
> 19658 classes loaded in 2.003623648 seconds.
> 494392 methods obtained in 0.932151066 seconds.
> 494392 methods obtained in 0.968247076 seconds.
> 494392 methods obtained in 0.989218185 seconds.
> 494392 methods obtained in 1.018700179 seconds.
> 494392 methods obtained in 0.945078767 seconds.
> 494392 methods obtained in 0.930856897 seconds.
> 494392 methods obtained in 0.905420555 seconds.
> 494392 methods obtained in 0.89253006 seconds.
> 494392 methods obtained in 0.914590141 seconds.
> 494392 methods obtained in 0.891616716 seconds.
> 494392 methods obtained in 0.891839815 seconds.
> 494392 methods obtained in 0.892863985 seconds.
> 494392 methods obtained in 0.960643274 seconds.
> 494392 methods obtained in 0.959266392 seconds.
> 494392 methods obtained in 0.894688322 seconds.
> 494392 methods obtained in 0.892751891 seconds.
> 494392 methods obtained in 0.912542838 seconds.
> 494392 methods obtained in 0.912219636 seconds.
> 494392 methods obtained in 0.895791468 seconds.
> 494392 methods obtained in 0.891673959 seconds.
>
> Patched:
>
> 19658 classes loaded in 2.145270948 seconds.
> 494398 methods obtained in 0.722630874 seconds.
> 494398 methods obtained in 0.697521755 seconds.
> 494398 methods obtained in 0.689642554 seconds.
> 494398 methods obtained in 0.742724992 seconds.
> 494398 methods obtained in 0.695693313 seconds.
> 494398 methods obtained in 0.685169108 seconds.
> 494398 methods obtained in 0.663012634 seconds.
> 494398 methods obtained in 0.666446935 seconds.
> 494398 methods obtained in 0.675662884 seconds.
> 494398 methods obtained in 0.65369404 seconds.
> 494398 methods obtained in 0.656608178 seconds.
> 494398 methods obtained in 0.668384051 seconds.
> 494398 methods obtained in 0.657548957 seconds.
> 494398 methods obtained in 0.672332234 seconds.
> 494398 methods obtained in 0.655699295 seconds.
> 494398 methods obtained in 0.671819628 seconds.
> 494398 methods obtained in 0.657232183 seconds.
> 494398 methods obtained in 0.654462137 seconds.
> 494398 methods obtained in 0.659630473 seconds.
> 494398 methods obtained in 0.674219391 seconds.
>
> (the difference in method count is expected - patched code contains new
> methods in existing rt.jar classes ;-)
>
> 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.
>
> 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.
>
>
> Regards, Peter
>
>



More information about the core-libs-dev mailing list