Proposal: optimization of Map.copyOf and Collectors.toUnmodifiableMap

Peter Levart peter.levart at gmail.com
Mon Jun 11 10:57:57 UTC 2018


Hi,

Those two methods were added in JDK 10 and they are not very optimal. 
Map.copyOf(Map map) 1st dumps the source map into an array of 
Map.Entry(s) (map.entrySet().toArray(new Entry[0])), which typically 
creates new Map.Entry objects, then passes the array to 
Map.ofEntries(Map.Entry[] entries) factory method that iterates the 
array and constructs a key/value interleaved array from it which is 
passed to ImmutableCollections.MapN constructor, which then constructs a 
linear-probing hash table from it. So each key and value is copied 3 
times, while several temporary objects are created in the process.

One copying step could be eliminated and construction of temporary 
Map.Entry objects too:

http://cr.openjdk.java.net/~plevart/jdk-dev/UnmodifiableMap_copyOf/webrev.01/

Collecting stream(s) using Collectors.toUnmodifiableMap() 1st collects 
key/value pairs into a HashMap, then it performs equivalent operations 
as Map.copyOf(hashMap) at the end. Using Map.copyOf() directly benefits 
this collection operation too.

The following benchmark:

http://cr.openjdk.java.net/~plevart/jdk-dev/UnmodifiableMap_copyOf/UnmodifiableMapBench.java


Shows up to 30% improvement of .copyOf operation with this patch applied:

Original:

Benchmark                               (size)  Mode Cnt      Score     
Error  Units
UnmodifiableMapBench.copyOf                 10  avgt 10    403.633 ±   
2.640  ns/op
UnmodifiableMapBench.copyOf                100  avgt   10 3489.623 ±  
44.590  ns/op
UnmodifiableMapBench.copyOf               1000  avgt   10 40030.572 ± 
277.075  ns/op
UnmodifiableMapBench.toUnmodifiableMap      10  avgt 10    831.221 ±   
3.816  ns/op
UnmodifiableMapBench.toUnmodifiableMap     100  avgt   10 9783.519 ±  
43.097  ns/op
UnmodifiableMapBench.toUnmodifiableMap    1000  avgt   10 96524.536 ± 
670.818  ns/op

Patched:

Benchmark                               (size)  Mode Cnt      Score      
Error  Units
UnmodifiableMapBench.copyOf                 10  avgt 10    264.172 ±    
1.882  ns/op
UnmodifiableMapBench.copyOf                100  avgt   10 2318.974 ±   
15.877  ns/op
UnmodifiableMapBench.copyOf               1000  avgt   10 29291.782 ± 
3139.737  ns/op
UnmodifiableMapBench.toUnmodifiableMap      10  avgt 10    771.221 ±   
65.432  ns/op
UnmodifiableMapBench.toUnmodifiableMap     100  avgt   10 9275.016 ±  
725.722  ns/op
UnmodifiableMapBench.toUnmodifiableMap    1000  avgt   10 82204.342 ±  
851.741  ns/op


Production of garbage is also reduced, since no Map.Entry temporary 
objects are constructed:

Original:

Benchmark (size)  Mode  Cnt      Score       Error   Units
UnmodifiableMapBench.copyOf:·gc.alloc.rate.norm 10  avgt   10    416.001 
±     0.002    B/op
UnmodifiableMapBench.copyOf:·gc.alloc.rate.norm 100  avgt   10   
2936.005 ±     0.019    B/op
UnmodifiableMapBench.copyOf:·gc.alloc.rate.norm 1000  avgt   10  
28136.059 ±     0.199    B/op
UnmodifiableMapBench.toUnmodifiableMap:·gc.alloc.rate.norm 10  avgt   
10   1368.001 ±     0.004    B/op
UnmodifiableMapBench.toUnmodifiableMap:·gc.alloc.rate.norm 100  avgt   
10  10208.139 ±     0.045    B/op
UnmodifiableMapBench.toUnmodifiableMap:·gc.alloc.rate.norm 1000  avgt   
10  93025.923 ±     0.573    B/op

Patched:

Benchmark (size)  Mode  Cnt      Score       Error   Units
UnmodifiableMapBench.copyOf:·gc.alloc.rate.norm 10  avgt   10    304.000 
±     0.001    B/op
UnmodifiableMapBench.copyOf:·gc.alloc.rate.norm 100  avgt   10   
2464.004 ±     0.012    B/op
UnmodifiableMapBench.copyOf:·gc.alloc.rate.norm 1000  avgt   10  
24064.040 ±     0.137    B/op
UnmodifiableMapBench.toUnmodifiableMap:·gc.alloc.rate.norm 10  avgt   
10   1256.001 ±     0.003    B/op
UnmodifiableMapBench.toUnmodifiableMap:·gc.alloc.rate.norm 100  avgt   
10   9720.153 ±     0.055    B/op
UnmodifiableMapBench.toUnmodifiableMap:·gc.alloc.rate.norm 1000  avgt   
10  88905.688 ±     0.574    B/op


So what do you think? Is this an improvement?

Regards, Peter



More information about the core-libs-dev mailing list