RFR: 8247532: Records deserialization is slow

Johannes Kuhn info at j-kuhn.de
Sun Jun 14 17:03:00 UTC 2020


On 14-Jun-20 18:28, Peter Levart wrote:
> Hi,
>
>
> I noticed that deserializing records (new preview Java feature in 
> JDK14 and JDK15) is slow compared to equivalent classical classes. I 
> created a JMH benchmark [1] to se how it compares (ran it on JDK14):
>
>
> Benchmark Mode  Cnt       Score        Error   Units
> RecordSerializationBench.deserializeClasses avgt   10      31.911 
> ±      0.218   us/op
> RecordSerializationBench.deserializeClasses:·gc.alloc.rate avgt   
> 10     815.106 ±      5.545  MB/sec
> RecordSerializationBench.deserializeClasses:·gc.alloc.rate.norm avgt   
> 10   40921.903 ±      0.615    B/op
> RecordSerializationBench.deserializeClasses:·gc.churn.G1_Eden_Space 
> avgt   10     839.522 ±    191.195  MB/sec
> RecordSerializationBench.deserializeClasses:·gc.churn.G1_Eden_Space.norm 
> avgt   10   42153.661 ±   9682.799    B/op
> RecordSerializationBench.deserializeClasses:·gc.churn.G1_Survivor_Space 
> avgt   10       0.117 ±      0.492  MB/sec
> RecordSerializationBench.deserializeClasses:·gc.churn.G1_Survivor_Space.norm 
> avgt   10       5.835 ±     24.447    B/op
> RecordSerializationBench.deserializeClasses:·gc.count avgt   10      
> 21.000               counts
> RecordSerializationBench.deserializeClasses:·gc.time avgt   10      
> 17.000                   ms
> RecordSerializationBench.deserializeRecords avgt   10     531.333 
> ±      3.094   us/op
> RecordSerializationBench.deserializeRecords:·gc.alloc.rate avgt   
> 10     346.511 ±      1.997  MB/sec
> RecordSerializationBench.deserializeRecords:·gc.alloc.rate.norm avgt   
> 10  289637.193 ±      6.894    B/op
> RecordSerializationBench.deserializeRecords:·gc.churn.G1_Eden_Space 
> avgt   10     359.773 ±    191.116  MB/sec
> RecordSerializationBench.deserializeRecords:·gc.churn.G1_Eden_Space.norm 
> avgt   10  300657.838 ± 159724.346    B/op
> RecordSerializationBench.deserializeRecords:·gc.churn.G1_Survivor_Space 
> avgt   10       0.007 ±      0.012  MB/sec
> RecordSerializationBench.deserializeRecords:·gc.churn.G1_Survivor_Space.norm 
> avgt   10       6.020 ±      9.910    B/op
> RecordSerializationBench.deserializeRecords:·gc.count avgt   10       
> 9.000               counts
> RecordSerializationBench.deserializeRecords:·gc.time avgt   10      
> 14.000                   ms
>
>
> ...not only it is it about 16x slower, it also produces 7x garbage. I 
> checked the code and it is not very optimal. It matches the record 
> component names with object stream fields in O(n^2) way. It uses 
> method handles but binds arguments of canonical constructor each time 
> an instance of record is constructed. So I tried to optimize it [2] 
> and with that patch on top of JDK16 the benchmark produces the 
> following results:
>
>
> Benchmark Mode  Cnt      Score       Error   Units
> RecordSerializationBench.deserializeClasses avgt   10     31.049 ±     
> 0.235   us/op
> RecordSerializationBench.deserializeClasses:·gc.alloc.rate avgt   
> 10    837.614 ±     6.326  MB/sec
> RecordSerializationBench.deserializeClasses:·gc.alloc.rate.norm avgt   
> 10  40921.931 ±     0.666    B/op
> RecordSerializationBench.deserializeClasses:·gc.churn.G1_Eden_Space 
> avgt   10    867.743 ±   251.373  MB/sec
> RecordSerializationBench.deserializeClasses:·gc.churn.G1_Eden_Space.norm 
> avgt   10  42405.532 ± 12403.301    B/op
> RecordSerializationBench.deserializeClasses:·gc.churn.G1_Survivor_Space 
> avgt   10      0.126 ±     0.478  MB/sec
> RecordSerializationBench.deserializeClasses:·gc.churn.G1_Survivor_Space.norm 
> avgt   10      6.113 ±    23.268    B/op
> RecordSerializationBench.deserializeClasses:·gc.count avgt   10     
> 22.000              counts
> RecordSerializationBench.deserializeClasses:·gc.time avgt   10     
> 19.000                  ms
> RecordSerializationBench.deserializeRecords avgt   10     33.588 ±     
> 0.394   us/op
> RecordSerializationBench.deserializeRecords:·gc.alloc.rate avgt   
> 10    500.033 ±     5.871  MB/sec
> RecordSerializationBench.deserializeRecords:·gc.alloc.rate.norm avgt   
> 10  26425.293 ±     0.759    B/op
> RecordSerializationBench.deserializeRecords:·gc.churn.G1_Eden_Space 
> avgt   10    512.772 ±   288.112  MB/sec
> RecordSerializationBench.deserializeRecords:·gc.churn.G1_Eden_Space.norm 
> avgt   10  27090.499 ± 15175.280    B/op
> RecordSerializationBench.deserializeRecords:·gc.churn.G1_Survivor_Space 
> avgt   10      0.134 ±     0.496  MB/sec
> RecordSerializationBench.deserializeRecords:·gc.churn.G1_Survivor_Space.norm 
> avgt   10      7.128 ±    26.526    B/op
> RecordSerializationBench.deserializeRecords:·gc.count avgt   10     
> 13.000              counts
> RecordSerializationBench.deserializeRecords:·gc.time avgt   10     
> 17.000                  ms
>
>
> ...so here the speed is comparable and it even produces less garbage.
>
>
> I created an issue [3].
>
> So WDYT? Since this is still a preview feature in JDK15, is it 
> possible to squeeze it into JDK15?
>
> Regards, Peter
>
>
> [1] 
> http://cr.openjdk.java.net/~plevart/jdk-dev/RecordsDeserialization/RecordSerializationBench.java
>
> [2] 
> http://cr.openjdk.java.net/~plevart/jdk-dev/RecordsDeserialization/webrev.01/
>
> [3] https://bugs.openjdk.java.net/browse/JDK-8247532
>
>
>
Small suggestion: RecordSupport.defaultValueExtractorFor could be 
written as:

     return MethodHandles.empty(MethodType.methodType(pClass, 
byte[].class, Object[].class));

It could then be inlined.

- Johannes



More information about the core-libs-dev mailing list