On Mon, 4 Jan 2021 16:40:50 GMT, Claes Redestad <[email protected]> wrote:

> By caching default constructors used in 
> `java.security.Provider::newInstanceUtil` in a `ClassValue`, we can reduce 
> the overhead of allocating instances in a variety of places, e.g., 
> `MessageDigest::getInstance`, without compromising thread-safety or security.
> 
> On the provided microbenchmark `MessageDigest.getInstance(digesterName)` 
> improves substantially for any `digesterName` - around -90ns/op and -120B/op:
> Benchmark                                                                  
> (digesterName)  Mode  Cnt     Score     Error   Units
> GetMessageDigest.getInstance                                                  
>         md5  avgt   30   293.929 ±  11.294   ns/op
> GetMessageDigest.getInstance:·gc.alloc.rate.norm                              
>         md5  avgt   30   424.028 ±   0.003    B/op
> GetMessageDigest.getInstance                                                  
>       SHA-1  avgt   30   322.928 ±  16.503   ns/op
> GetMessageDigest.getInstance:·gc.alloc.rate.norm                              
>       SHA-1  avgt   30   688.039 ±   0.003    B/op
> GetMessageDigest.getInstance                                                  
>     SHA-256  avgt   30   338.140 ±  13.902   ns/op
> GetMessageDigest.getInstance:·gc.alloc.rate.norm                              
>     SHA-256  avgt   30   640.037 ±   0.002    B/op
> GetMessageDigest.getInstanceWithProvider                                      
>         md5  avgt   30   312.066 ±  12.805   ns/op
> GetMessageDigest.getInstanceWithProvider:·gc.alloc.rate.norm                  
>         md5  avgt   30   424.029 ±   0.003    B/op
> GetMessageDigest.getInstanceWithProvider                                      
>       SHA-1  avgt   30   345.777 ±  16.669   ns/op
> GetMessageDigest.getInstanceWithProvider:·gc.alloc.rate.norm                  
>       SHA-1  avgt   30   688.040 ±   0.003    B/op
> GetMessageDigest.getInstanceWithProvider                                      
>     SHA-256  avgt   30   371.134 ±  18.485   ns/op
> GetMessageDigest.getInstanceWithProvider:·gc.alloc.rate.norm                  
>     SHA-256  avgt   30   640.039 ±   0.004    B/op
> Patch:
> Benchmark                                                                  
> (digesterName)  Mode  Cnt     Score     Error   Units
> GetMessageDigest.getInstance                                                  
>         md5  avgt   30   210.629 ±   6.598   ns/op
> GetMessageDigest.getInstance:·gc.alloc.rate.norm                              
>         md5  avgt   30   304.021 ±   0.002    B/op
> GetMessageDigest.getInstance                                                  
>       SHA-1  avgt   30   229.161 ±   8.158   ns/op
> GetMessageDigest.getInstance:·gc.alloc.rate.norm                              
>       SHA-1  avgt   30   568.030 ±   0.002    B/op
> GetMessageDigest.getInstance                                                  
>     SHA-256  avgt   30   260.013 ±  15.032   ns/op
> GetMessageDigest.getInstance:·gc.alloc.rate.norm                              
>     SHA-256  avgt   30   520.030 ±   0.002    B/op
> GetMessageDigest.getInstanceWithProvider                                      
>         md5  avgt   30   231.928 ±  10.455   ns/op
> GetMessageDigest.getInstanceWithProvider:·gc.alloc.rate.norm                  
>         md5  avgt   30   304.020 ±   0.002    B/op
> GetMessageDigest.getInstanceWithProvider                                      
>       SHA-1  avgt   30   247.178 ±  11.209   ns/op
> GetMessageDigest.getInstanceWithProvider:·gc.alloc.rate.norm                  
>       SHA-1  avgt   30   568.029 ±   0.002    B/op
> GetMessageDigest.getInstanceWithProvider                                      
>     SHA-256  avgt   30   265.625 ±  10.465   ns/op
> GetMessageDigest.getInstanceWithProvider:·gc.alloc.rate.norm                  
>     SHA-256  avgt   30   520.030 ±   0.003    B/op
> 
> See: 
> https://cl4es.github.io/2021/01/04/Investigating-MD5-Overheads.html#reflection-overheads
>  for context.

I refactored and optimized the lookup code further, getting rid of a number of 
bottlenecks:

- Cache Constructors in Provider.Service instead of via a ClassValue.
- Also cache the impl Class, wrap Class and Constructor in WeakReference if not 
loaded by the null classloader (many builtins will be)
- Cache EngineDescription in Service, avoiding a lookup on the hot path
- We were hitting a synchronized method in ProviderConfig.getProvider(). The 
provider field is volatile already, so I used the double-check idiom here to 
avoid synchronization on the hot path
- ServiceKey.hashCode using Objects.hash was cause for allocation, simplified 
and optimized it.

Benchmark                                                      (digesterName)  
Mode  Cnt     Score    Error   Units
GetMessageDigest.getInstance                                              MD5  
avgt   30   143.803 ±  5.431   ns/op
GetMessageDigest.getInstance:·gc.alloc.rate.norm                          MD5  
avgt   30   280.015 ±  0.001    B/op

-------------

PR: https://git.openjdk.java.net/jdk/pull/1933

Reply via email to