Hi again,
By simple rearrangement of code in Proxy.getProxyClass (moved the
parameters validation to slow-path) I managed to speed-up the fast-path
getProxyClass so that now it's raw speed is 40x the speed of current
getProxyClass method:
* http://dl.dropbox.com/u/101777488/jdk8-tl/proxy/webrev.02/index.html
Here are quick measurements (only for i7 this time):
*
https://raw.github.com/plevart/jdk8-tl/proxy/test/proxy_benchmark_results_v2.txt
Regards, Peter
On 01/27/2013 02:32 PM, Peter Levart wrote:
Hi Alan, David,
I think I have a solution for scalability problems of
java.lang.reflect.Proxy:
* http://dl.dropbox.com/u/101777488/jdk8-tl/proxy/webrev.01/index.html
I designed it along the lines mentioned in previous mail (see below).
First I thought that both caches (a cache mapping request parameters
of Proxy.getProxyClass to a proxy Class and a cache for resolving the
Proxy.isProxyClass method) could be placed inside the j.l.ClassLoader.
And they actually could. I tried this and it worked correctly, but the
raw (single-threaded) performance of Proxy.isProxyClass was half the
performance of current JDK8 isProxyClass method that way. The main
performance hog I identified was the Class.getClassLoader() method.
This method delegates to a native JVM method after checking the
callers permissions. So the plan to keep the isProxyClass cache inside
the ClassLoader failed. But then the ClassValue solution that I
proposed in previous revision works beautifully and fast, so I kept
this solution for isProxyClass cache. The ThreadLocal trick to
initialize the ClassValue for a particular proxy class is necessary
because ClassValue does not have a put() method. It does have
actually, but it's package private and the comment suggests that it
might not be there forever:
// Possible functionality for JSR 292 MR 1
/*public*/ void put(Class<?> type, T value) {
ClassValueMap map = getMap(type);
map.changeEntry(this, value);
}
I could use the JavaLangAccess to access it from Proxy code though, so
this is another refinement that is possible and would eliminate the
need for ThreadLocal trick.
The cache for getProxyClass method on the other hand is most naturally
hosted by j.l.ClassLoader and that's where I put it in the proposed
patch. Currently the cross-package access to it is implemented using
Unsafe, but the performance aspect is not so critical that It couldn't
be changed to using JavaLangAccess instead if desired.
I did some performance testing too. Here are the results taken on 3
different systems (i7 Linux PC, Raspberry Pi and Sun-Blade-T6320
Solaris machine):
*
https://raw.github.com/plevart/jdk8-tl/proxy/test/proxy_benchmark_results.txt
These are the test sources i used:
*
https://github.com/plevart/jdk8-tl/blob/proxy/test/src/test/ProxyBenchmarkTest.java
*
https://github.com/plevart/micro-bench/blob/master/src/si/pele/microbench/TestRunner.java
Results show scalability and raw performance improvements for both
critical Proxy methods.
So what do you think?
Regards, Peter
On 01/25/2013 06:55 PM, Peter Levart wrote:
On 01/24/2013 03:34 PM, Peter Levart wrote:
On 01/24/2013 03:10 PM, Alan Bateman wrote:
On 24/01/2013 13:49, Peter Levart wrote:
Should I file a RFE first?
Sorry I don't have time at the moment to study the proposed patch
but just to mention that it has come up a few times, its just that
it never bubbled up to the top of anyone's list. Here's the bug
tracking it:
http://bugs.sun.com/view_bug.do?bug_id=7123493
-Alan.
I belive that is another bottleneck. It is mentioning the
Proxy.getProxyClass method which also uses synchronization for
maintaining a cache of proxy classes by request parameters. I could
as well try to fix this too in the same patch if there is interest.
Regards, Peter
Hi Alan, David,
I thought about the ways to fix Proxy.isProxyClass() scalability and
the Proxy.getProxyClass() scalability. While they are different
methods, each with it's own data structure, I think that both
problems can be solved with a single solution and that solution does
not involve neither adding fields to j.l.Class nor ClassValue.
The solution is actually very simple. I just want to validate my
reasoning before jumping to implement it:
- for solving scalability of getProxyClass cache, a field with a
reference to ConcurrentHashMap<List<String>, Class<? extends Proxy>>
is added to j.l.ClassLoader
- for solving scalability of isProxyClass, a field with a reference
to ConcurrentHashMap<Class<? extends Proxy>, Boolean> is added to
j.l.ClassLoader
Both maps hold strong references to Class objects, but only for the
classes that are loaded by the ClassLoader that references them. Each
ClassLoader already holds a strong reference to all the Class objects
for the classes that were loaded by it in a Vector. Holding another
reference does not present any problem, right?
I think this would be the best solution and it would solve both
scalability problems of j.l.Proxy in one go.
Am I missing something?
Regards, Peter