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