Proxy.isProxyClass scalability (original) (raw)
Peter Levart peter.levart at gmail.com
Sat Apr 20 16:37:18 UTC 2013
- Previous message: Proxy.isProxyClass scalability
- Next message: Proxy.isProxyClass scalability
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
On 04/20/2013 09:31 AM, Peter Levart wrote:
Hi Mandy,
I have another idea. Before jumping to implement it, I will first ask what do you think of it. For example: - have an optimal interface names key calculated from interfaces. - visibility of interfaces and other validations are pushed to slow-path (inside ProxyClassFactory.apply) - the proxy Class object returned from WeakCache.get() is post-validated with a check like: for (Class<?> intf : interfaces) { if (!intf.isAssignableFrom(proxyClass)) { throw new IllegalArgumentException(); } } // return post-validated proxyClass from getProxyClass0()...
I just did it:
http://dl.dropboxusercontent.com/u/101777488/jdk8-tl/proxy-wc/webrev.03/index.html
I also incorporated the expunging optimization that I talked about in one of previous mails.
And the keys, composed of interface names, are now more space-friendly too:
- a key for 0 interface proxy is a singleton Object
- a key for 1 interface proxy is the name of the interface itself (an already interned String)
- a key for 2 interface proxy is a special class with 2 references to interned Strings
- a key for 3+ interface proxy is a special class wrapping an array of interned Strings
The performance is screaming again (WeakCache+post-validation):
ns/op WeakCache+ WeakCache+
Test Threads Original pre-valid. post-valid. ======================= ======= =========== =========== =========== Proxy_getProxyClass 1 2,420.99 2,258.00 211.04 4 3,075.39 2,644.26 282.93 8 5,374.45 5,159.71 432.14
Proxy_isProxyClassTrue 1 97.75 45.37 42.67 4 2,505.92 42.59 42.77 8 5,042.61 75.44 73.20
Proxy_isProxyClassFalse 1 89.20 1.40 1.40 4 2,548.61 1.40 1.40 8 4,901.56 2.82 2.80
Annotation_equals 1 224.39 201.82 202.97 4 2,046.21 200.61 204.89 8 3,564.78 347.27 344.57
And the space savings are now even greater. Patched code is always better space-wise. The savings are:
32 bit addressing:
- 56 bytes per proxy class implementing 1 interface
- 32 bytes per proxy class implementing 2 interfaces
- 16 bytes per proxy class implementing 3 or more interfaces
64 bit addressing:
- 80 bytes per proxy class implementing 1 interface
- 56 bytes per proxy class implementing 2 interfaces
- 24 bytes per proxy class implementing 3 or more interfaces
Regards, Peter
P.S. Details:
32 bit addressing:
** Original j.l.r.Proxy
** 0 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 400 400
1 1 760 360
2 9 1072 312
** Original j.l.r.Proxy
** 1 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 400 400
1 1 768 368
1 2 920 152
1 3 1072 152
1 4 1224 152
1 5 1376 152
1 6 1528 152
1 7 1680 152
1 8 1832 152
2 9 2152 320
2 10 2304 152
2 11 2456 152
2 12 2672 216
2 13 2824 152
2 14 2976 152
2 15 3128 152
2 16 3280 152
** Original j.l.r.Proxy
** 2 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 400 400
1 1 768 368
1 2 920 152
1 3 1072 152
1 4 1224 152
1 5 1376 152
1 6 1528 152
1 7 1680 152
1 8 1832 152
2 9 2152 320
2 10 2304 152
2 11 2456 152
2 12 2672 216
2 13 2824 152
2 14 2976 152
2 15 3128 152
2 16 3280 152
** Original j.l.r.Proxy
** 3 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 400 400
1 1 776 376
1 2 936 160
1 3 1096 160
1 4 1256 160
1 5 1416 160
1 6 1576 160
1 7 1736 160
1 8 1896 160
2 9 2224 328
2 10 2384 160
2 11 2544 160
2 12 2768 224
2 13 2928 160
2 14 3088 160
2 15 3248 160
2 16 3408 160
** Original j.l.r.Proxy
** 4 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 400 400
1 1 776 376
1 2 936 160
1 3 1096 160
1 4 1256 160
1 5 1416 160
1 6 1576 160
1 7 1736 160
1 8 1896 160
2 9 2224 328
2 10 2384 160
2 11 2544 160
2 12 2768 224
2 13 2928 160
2 14 3088 160
2 15 3248 160
2 16 3408 160
** Patched j.l.r.Proxy
** 0 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 240 240
1 1 768 528
2 9 1072 304
** Patched j.l.r.Proxy
** 1 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 240 240
1 1 752 512
1 2 848 96
1 3 944 96
1 4 1040 96
1 5 1136 96
1 6 1232 96
1 7 1328 96
1 8 1424 96
2 9 1728 304
2 10 1824 96
2 11 1920 96
2 12 2080 160
2 13 2176 96
2 14 2272 96
2 15 2368 96
2 16 2464 96
** Patched j.l.r.Proxy
** 2 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 240 240
1 1 776 536
1 2 896 120
1 3 1016 120
1 4 1136 120
1 5 1256 120
1 6 1376 120
1 7 1496 120
1 8 1616 120
2 9 1944 328
2 10 2064 120
2 11 2184 120
2 12 2368 184
2 13 2488 120
2 14 2608 120
2 15 2728 120
2 16 2848 120
** Patched j.l.r.Proxy
** 3 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 240 240
1 1 800 560
1 2 944 144
1 3 1088 144
1 4 1232 144
1 5 1376 144
1 6 1520 144
1 7 1664 144
1 8 1808 144
2 9 2160 352
2 10 2304 144
2 11 2448 144
2 12 2656 208
2 13 2800 144
2 14 2944 144
2 15 3088 144
2 16 3232 144
** Patched j.l.r.Proxy
** 4 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 240 240
1 1 800 560
1 2 944 144
1 3 1088 144
1 4 1232 144
1 5 1376 144
1 6 1520 144
1 7 1664 144
1 8 1808 144
2 9 2160 352
2 10 2304 144
2 11 2448 144
2 12 2656 208
2 13 2800 144
2 14 2944 144
2 15 3088 144
2 16 3232 144
64 bit addressing:
** Original j.l.r.Proxy
** 0 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 632 632
1 1 1208 576
2 9 1728 520
** Original j.l.r.Proxy
** 1 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 632 632
1 1 1216 584
1 2 1448 232
1 3 1680 232
1 4 1912 232
1 5 2144 232
1 6 2376 232
1 7 2608 232
1 8 2840 232
2 9 3368 528
2 10 3600 232
2 11 3832 232
2 12 4192 360
2 13 4424 232
2 14 4656 232
2 15 4888 232
2 16 5120 232
** Original j.l.r.Proxy
** 2 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 632 632
1 1 1224 592
1 2 1464 240
1 3 1704 240
1 4 1944 240
1 5 2184 240
1 6 2424 240
1 7 2664 240
1 8 2904 240
2 9 3440 536
2 10 3680 240
2 11 3920 240
2 12 4288 368
2 13 4528 240
2 14 4768 240
2 15 5008 240
2 16 5248 240
** Original j.l.r.Proxy
** 3 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 632 632
1 1 1232 600
1 2 1480 248
1 3 1728 248
1 4 1976 248
1 5 2224 248
1 6 2472 248
1 7 2720 248
1 8 2968 248
2 9 3512 544
2 10 3760 248
2 11 4008 248
2 12 4384 376
2 13 4632 248
2 14 4880 248
2 15 5128 248
2 16 5376 248
** Original j.l.r.Proxy
** 4 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 632 632
1 1 1240 608
1 2 1496 256
1 3 1752 256
1 4 2008 256
1 5 2264 256
1 6 2520 256
1 7 2776 256
1 8 3032 256
2 9 3584 552
2 10 3840 256
2 11 4096 256
2 12 4480 384
2 13 4736 256
2 14 4992 256
2 15 5248 256
2 16 5504 256
** Patched j.l.r.Proxy
** 0 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 336 336
1 1 1216 880
2 9 1720 504
** Patched j.l.r.Proxy
** 1 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 336 336
1 1 1200 864
1 2 1352 152
1 3 1504 152
1 4 1656 152
1 5 1808 152
1 6 1960 152
1 7 2112 152
1 8 2264 152
2 9 2768 504
2 10 2920 152
2 11 3072 152
2 12 3352 280
2 13 3504 152
2 14 3656 152
2 15 3808 152
2 16 3960 152
** Patched j.l.r.Proxy
** 2 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 336 336
1 1 1232 896
1 2 1416 184
1 3 1600 184
1 4 1784 184
1 5 1968 184
1 6 2152 184
1 7 2336 184
1 8 2520 184
2 9 3056 536
2 10 3240 184
2 11 3424 184
2 12 3736 312
2 13 3920 184
2 14 4104 184
2 15 4288 184
2 16 4472 184
** Patched j.l.r.Proxy
** 3 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 336 336
1 1 1272 936
1 2 1496 224
1 3 1720 224
1 4 1944 224
1 5 2168 224
1 6 2392 224
1 7 2616 224
1 8 2840 224
2 9 3416 576
2 10 3640 224
2 11 3864 224
2 12 4216 352
2 13 4440 224
2 14 4664 224
2 15 4888 224
2 16 5112 224
** Patched j.l.r.Proxy
** 4 interfaces / proxy class
class proxy size of delta to
loaders classes caches prev.ln.
0 0 336 336
1 1 1280 944
1 2 1512 232
1 3 1744 232
1 4 1976 232
1 5 2208 232
1 6 2440 232
1 7 2672 232
1 8 2904 232
2 9 3488 584
2 10 3720 232
2 11 3952 232
2 12 4312 360
2 13 4544 232
2 14 4776 232
2 15 5008 232
2 16 5240 232
On 04/20/2013 09:31 AM, Peter Levart wrote:
Hi Mandy,
I have another idea. Before jumping to implement it, I will first ask what do you think of it. For example: - have an optimal interface names key calculated from interfaces. - visibility of interfaces and other validations are pushed to slow-path (inside ProxyClassFactory.apply) - the proxy Class object returned from WeakCache.get() is post-validated with a check like: for (Class<?> intf : interfaces) { if (!intf.isAssignableFrom(proxyClass)) { throw new IllegalArgumentException(); } } // return post-validated proxyClass from getProxyClass0()... I feel that Class.isAssignableFrom(Class) check could be much faster and that the only reason the check can fail is if some interface is not visible from the class loader. Am I correct? Regards, Peter
On 04/19/2013 04:36 PM, Peter Levart wrote: Hi Mandy, On 04/19/2013 07:33 AM, Mandy Chung wrote:
https://dl.dropboxusercontent.com/u/101777488/jdk8-tl/proxy-wc/webrev.02/index.html What about package-private in java.lang.reflect? It makes Proxy itself much easier to read. When we decide which way to go, I can remove the interface and only leave a single package-private class...
thanks. Moving it to a single package-private classin java.lang.reflectand remove the interface sounds good. Right. I have merged your patch with the recent TL repo and did some clean up while reviewing it. Some comments: 1. getProxyClass0 should validate the input interfaces and throw IAE if any illegal argument (e.g. interfaces are not visible to the given loader) before calling proxyClassCache.get(loader, interfaces). I moved back the validation code from ProxyClassFactory.apply to getProxyClass0. Ops, you're right. There could be a request with interface(s) with same name(s) but loaded by different loader(s) and such code could return wrong pre-cached proxy class instead of throwing exception. Unfortunately, moving validation to slow-path was the cause of major performance and scalability improvement observed. With validation moved back to getProxyClass0 (in spite of using two-level WeakCache), we have the following performance (WeakCache#1): Summary (4 Cores x 2 Threads i7 CPU): Test Threads ns/op Original WeakCache#1 ======================= ======= ============== =========== ProxygetProxyClass 1 2,403.27 2,174.51 4 3,039.01 2,555.00 8 5,193.58 4,273.42 ProxyisProxyClassTrue 1 95.02 43.14 4 2,266.29 43.23 8 4,782.29 72.06 ProxyisProxyClassFalse 1 95.02 1.36 4 2,186.59 1.36 8 4,891.15 2.72 Annotationequals 1 240.10 195.68 4 1,864.06 201.41 8 8,639.20 337.46 It's a little better than original getProxyClass(), but not much. The isProxyClass() and consequently Annotation.equals() are far better though. But as you might have guessed, I kind of solved that too... My first attempt was to optimize the interface validation. Only the "visibility" part is necessary to be performed on fast-path. Uniqueness and other things can be performed on slow-path. With split-validation and hacks like: private static final MethodHandle findLoadedClass0MH, findBootstrapClassMH; private static final ClassLoader dummyCL = new ClassLoader() {}; static { try { Method method = ClassLoader.class.getDeclaredMethod("findLoadedClass0", String.class); method.setAccessible(true); findLoadedClass0MH = MethodHandles.lookup().unreflect(method); method = ClassLoader.class.getDeclaredMethod("findBootstrapClass", String.class); method.setAccessible(true); findBootstrapClassMH = MethodHandles.lookup().unreflect(method); } catch (NoSuchMethodException e) { throw (Error) new NoSuchMethodError(e.getMessage()).initCause(e); } catch (IllegalAccessException e) { throw (Error) new IllegalAccessError(e.getMessage()).initCause(e); } } static Class<?> findLoadedClass(ClassLoader loader, String name) { try { if (VM.isSystemDomainLoader(loader)) { return (Class<?>) findBootstrapClassMH.invokeExact(dummyCL, name); } else { return (Class<?>) findLoadedClass0MH.invokeExact(loader, name); } } catch (RuntimeException | Error e) { throw e; } catch (Throwable t) { throw new UndeclaredThrowableException(t); } } ... using findLoadedClass(loader, intf.getName()) and only doing Class.forName(intf.getName(), false, loader) if the former returned null ... I managed to reclaim some performance (WeakCache#1+): Test Threads ns/op Original WeakCache#1 WeakCache#1+ ======================= ======= ============== =========== ============ ProxygetProxyClass 1 2,403.27 2,174.51 1,589.36 4 3,039.01 2,555.00 1,929.11 8 5,193.58 4,273.42 3,409.77 ...but that was still not very satisfactory. I modified the KeyFactory to create keys that weakly-reference interface Class objects and implement hashCode/equals in terms of comparing those Class objects. With such keys, no false aliasing can occur and the whole validation can be pushed back to slow-path. I tried hard to create keys with as little space overhead as possible: http://dl.dropboxusercontent.com/u/101777488/jdk8-tl/proxy-wc-wi/webrev.01/index.html ...but there can be no miracles. The good news is that the performance is back (WeakCache#2): Summary (4 Cores x 2 Threads i7 CPU): Test Threads ns/op Original WeakCache#1 WeakCache#2 ======================= ======= ============== =========== =========== ProxygetProxyClass 1 2,403.27 2,174.51 163.57 4 3,039.01 2,555.00 211.70 8 5,193.58 4,273.42 322.14 ProxyisProxyClassTrue 1 95.02 43.14 41.23 4 2,266.29 43.23 42.20 8 4,782.29 72.06 72.21 ProxyisProxyClassFalse 1 95.02 1.36 1.36 4 2,186.59 1.36 1.36 8 4,891.15 2.72 2.72 Annotationequals 1 240.10 195.68 194.61 4 1,864.06 201.41 198.81 8 8,639.20 337.46 342.90 ... and the most common usage (proxy class implementing exactly one interface) uses even less space than with interface-names-key - 16 bytes saved per proxy class vs. 8 bytes saved (32 bit addressing mode): -------------------------------------- Original j.l.r.Proxy 1 interfaces / proxy class class proxy size of delta to loaders classes caches prev.ln. -------- -------- -------- -------- 0 0 400 400 1 1 768 368 1 2 920 152 1 3 1072 152 1 4 1224 152 1 5 1376 152 1 6 1528 152 1 7 1680 152 1 8 1832 152 2 9 2152 320 2 10 2304 152 2 11 2456 152 2 12 2672 216 2 13 2824 152 2 14 2976 152 2 15 3128 152 2 16 3280 152 -------------------------------------- Patched j.l.r.Proxy 1 interfaces / proxy class class proxy size of delta to loaders classes caches prev.ln. -------- -------- -------- -------- 0 0 240 240 1 1 792 552 1 2 928 136 1 3 1064 136 1 4 1200 136 1 5 1336 136 1 6 1472 136 1 7 1608 136 1 8 1744 136 2 9 2088 344 2 10 2224 136 2 11 2360 136 2 12 2560 200 2 13 2696 136 2 14 2832 136 2 15 2968 136 2 16 3104 136 Did you know, that Proxy.getProxyClass() can generate proxy classes implementing 0 interfaces? It can. There's exactly one such class generated per class loader: -------------------------------------- Original j.l.r.Proxy 0 interfaces / proxy class class proxy size of delta to loaders classes caches prev.ln. -------- -------- -------- -------- 0 0 400 400 1 1 760 360 2 2 1072 312 -------------------------------------- Patched j.l.r.Proxy 0 interfaces / proxy class class proxy size of delta to loaders classes caches prev.ln. -------- -------- -------- -------- 0 0 240 240 1 1 728 488 2 2 1040 312 With 2 or more interfaces implemented by proxy class the space overhead increases faster than with original Proxy: -------------------------------------- Original j.l.r.Proxy 2 interfaces / proxy class class proxy size of delta to loaders classes caches prev.ln. -------- -------- -------- -------- 0 0 400 400 1 1 768 368 1 2 920 152 1 3 1072 152 1 4 1224 152 1 5 1376 152 1 6 1528 152 1 7 1680 152 1 8 1832 152 2 9 2152 320 2 10 2304 152 2 11 2456 152 2 12 2672 216 2 13 2824 152 2 14 2976 152 2 15 3128 152 2 16 3280 152 -------------------------------------- Patched j.l.r.Proxy 2 interfaces / proxy class class proxy size of delta to loaders classes caches prev.ln. -------- -------- -------- -------- 0 0 240 240 1 1 832 592 1 2 1008 176 1 3 1184 176 1 4 1360 176 1 5 1536 176 1 6 1712 176 1 7 1888 176 1 8 2064 176 2 9 2448 384 2 10 2624 176 2 11 2800 176 2 12 3040 240 2 13 3216 176 2 14 3392 176 2 15 3568 176 2 16 3744 176 -------------------------------------- Original j.l.r.Proxy 3 interfaces / proxy class class proxy size of delta to loaders classes caches prev.ln. -------- -------- -------- -------- 0 0 400 400 1 1 776 376 1 2 936 160 1 3 1096 160 1 4 1256 160 1 5 1416 160 1 6 1576 160 1 7 1736 160 1 8 1896 160 2 9 2224 328 2 10 2384 160 2 11 2544 160 2 12 2768 224 2 13 2928 160 2 14 3088 160 2 15 3248 160 2 16 3408 160 -------------------------------------- Patched j.l.r.Proxy 3 interfaces / proxy class class proxy size of delta to loaders classes caches prev.ln. -------- -------- -------- -------- 0 0 240 240 1 1 864 624 1 2 1072 208 1 3 1280 208 1 4 1488 208 1 5 1696 208 1 6 1904 208 1 7 2112 208 1 8 2320 208 2 9 2736 416 2 10 2944 208 2 11 3152 208 2 12 3424 272 2 13 3632 208 2 14 3840 208 2 15 4048 208 2 16 4256 208 -------------------------------------- Original j.l.r.Proxy 4 interfaces / proxy class class proxy size of delta to loaders classes caches prev.ln. -------- -------- -------- -------- 0 0 400 400 1 1 776 376 1 2 936 160 1 3 1096 160 1 4 1256 160 1 5 1416 160 1 6 1576 160 1 7 1736 160 1 8 1896 160 2 9 2224 328 2 10 2384 160 2 11 2544 160 2 12 2768 224 2 13 2928 160 2 14 3088 160 2 15 3248 160 2 16 3408 160 -------------------------------------- Patched j.l.r.Proxy 4 interfaces / proxy class class proxy size of delta to loaders classes caches prev.ln. -------- -------- -------- -------- 0 0 240 240 1 1 896 656 1 2 1136 240 1 3 1376 240 1 4 1616 240 1 5 1856 240 1 6 2096 240 1 7 2336 240 1 8 2576 240 2 9 3024 448 2 10 3264 240 2 11 3504 240 2 12 3808 304 2 13 4048 240 2 14 4288 240 2 15 4528 240 2 16 4768 240 There's an increase of 8 bytes per proxy class key for each 2 interfaces added to proxy class in original Proxy code, but there's an increase of 32 bytes per proxy class key for each single interface added in patched Proxy code. I think the most common usage is to implement a single interface and there is 16 bytes gained for each such usage compared to original Proxy code. 2. I did some cleanup to restore some original comments to make the diffs clearer where the change is. 3. I removed the newInstance method which is dead code after my previous code. Since we are in this code, I took the chance to clean that up and also change a couple for-loop to use for-each. I have put the merged and slightly modified Proxy.java and webrev at: http://cr.openjdk.java.net/~mchung/jdk8/webrevs/7123493/webrev.00/ We will use this bug for your contribution: 7123493 : (proxy) Proxy.getProxyClass doesn't scale under high load I took j.l.r.Proxy file from your webrev and just changed the KeyFactory implementation. WeakCache is generic and can be used unchanged with either implementation of KeyFactory. For the weak cache class, since it's for proxy implementation use, I suggest to take out the supportContainsValueOperation flagas it always keeps the reverse map for isProxyClass lookup. You can simply call Objects.requireNonNull(param) instead of requireNonNull(param, "param-name") since the proxy implementation should have made sure the argument is non-null. Formatting nits: 68 Object cacheKey = CacheKey.valueOf( 69 key, 70 refQueue 71 ); should be: all in one line or line break on a long-line. Same for method signature. 237 void expungeFrom( 238 ConcurrentMap> map, 239 ConcurrentMap<?, Boolean> reverseMap 240 ); should be: void expungeFrom(ConcurrentMap> map, ConcurrentMap<?, Boolean> reverseMap); so that it'll be more consistent with the existing code. I'll do a detailed review on the weak cache class as you will finalize the code per the decision to go with the two-level weak cache. I hope I have addressed all that in above webrev. Regards, Peter Thanks again for the good work. Mandy [1] http://openjdk.java.net/jeps/161
- Previous message: Proxy.isProxyClass scalability
- Next message: Proxy.isProxyClass scalability
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]