RFE: Refactor java.util.Optional and add NonNull checks
Oleksii Kucheruk
iselo+openjdk at raccoons.co
Mon Aug 28 10:27:21 UTC 2023
Thank you Quân.
All you saying guys make sense.
Yes there is a difference calling methods by pointer and involving vtable.
But one thing didn't came out of my head:
If virtual dispatch dispatch is 10-time more expensive and polymorphism is
a performance killer so how combinations of "return optional - isPresent-
get" or "return empty - isEmpty" with virtual dispatch and memory
allocation for every empty or sole but polymorph empty could perform
equally or even beat the near zero-cost abstraction of Value classes
according to JMH?
Maybe private final static nested EmptyOptional invokevirtual bytecode refers
a final methods and it need not have a vtable slot allocated. This means
that, after linking, an invokevirtual bytecode might in fact collapse into
the equivalent of an invokestatic bytecode.
The question of full dynamic with anonymous overloading is still unclear to
me.
Benchmark Mode Cnt
Score
Error Units
*OptionalComboIsPresentGet.opDynamicAnonymous thrpt 30
1059868995.937 ± 51273067.116 ops/s*
OptionalComboIsPresentGet.opJdk thrpt 30
1017505800.567
± 16007847.872 ops/s
OptionalComboIsPresentGet.opNestedStatic thrpt 30
951493332.957
± 12167812.398 ops/s
OptionalComboOfIsPresentGetTest.opDynamicAnonymous thrpt 30
1426348334.485
± 20889455.433 ops/s
*OptionalComboOfIsPresentGetTest.opJdk thrpt 30
1435570789.822 ± 16587538.659 ops/s*
OptionalComboOfIsPresentGetTest.opNestedStatic thrpt 30
1418853644.215
± 27755398.645 ops/s
*OptionalComboOfNullableIsEmptyTest.opDynamicAnonymous thrpt 30
1434399595.592 ± 14749443.903 ops/s*
OptionalComboOfNullableIsEmptyTest.opJdk thrpt 30
1255375023.531
± 107403119.573 ops/s
OptionalComboOfNullableIsEmptyTest.opNestedStatic thrpt 30
1415705702.964
± 26328318.672 ops/s
OptionalEmptyTest.opDynamicAnonymous thrpt 30
1417895521.763
± 26311640.557 ops/s
*OptionalEmptyTest.opJdk thrpt 30
1423940087.962 ± 18515297.860 ops/s*
OptionalEmptyTest.opNestedStatic thrpt 30
1424779162.084
± 20288793.337 ops/s
OptionalGetTest.emDynamicAnonymous thrpt 30
569132.235
± 11089.903 ops/s
*OptionalGetTest.emJdk thrpt 30
549764.038 ± 14768.566 ops/s*
OptionalGetTest.emNestedStatic thrpt 30
568308.722
± 6075.671 ops/s
OptionalGetTest.opDynamicAnonymous thrpt 30
953788065.414
± 7043419.784 ops/s
*OptionalGetTest.opJdk thrpt 30
1015679232.049 ± 11726562.032 ops/s*
OptionalGetTest.opNestedStatic thrpt 30
944209513.151
± 17718342.519 ops/s
OptionalIsPresentTest.emDynamicAnonymous thrpt 30
869086292.481
± 11668636.886 ops/s
*OptionalIsPresentTest.emJdk thrpt 30
977042244.447 ± 44558020.220 ops/s*
OptionalIsPresentTest.emNestedStatic thrpt 30
953424664.631
± 8448564.963 ops/s
OptionalIsPresentTest.opDynamicAnonymous thrpt 30
953285872.799
± 6562894.941 ops/s
*OptionalIsPresentTest.opJdk thrpt 30
1006926903.885 ± 18055487.689 ops/s*
OptionalIsPresentTest.opNestedStatic thrpt 30
954121471.431
± 6829528.708 ops/s
OptionalOfNullableTest.emDynamicAnonymous thrpt 30
1422939780.266
± 21693247.354 ops/s
*OptionalOfNullableTest.emJdk thrpt 30
1423340184.237 ± 21296444.017 ops/s*
OptionalOfNullableTest.emNestedStatic thrpt 30
1414965236.385
± 26021734.344 ops/s
OptionalOfNullableTest.opDynamicAnonymous thrpt 30
1412594454.538
± 28123612.298 ops/s
*OptionalOfNullableTest.opJdk thrpt 30
1427924599.589 ± 23469517.835 ops/s*
OptionalOfNullableTest.opNestedStatic thrpt 30
1420053175.637
± 21170929.571 ops/s
OptionalOfTest.emDynamicAnonymous thrpt 30
549907.559
± 14560.954 ops/s
OptionalOfTest.emJdk thrpt 30
557858.903
± 2851.076 ops/s
*OptionalOfTest.emNestedStatic thrpt 30
548655.854 ± 10875.674 ops/s*
OptionalOfTest.opDynamicAnonymous thrpt 30
1411307270.435
± 19832207.408 ops/s
OptionalOfTest.opJdk thrpt 30
1400651442.726
± 23641065.234 ops/s
*OptionalOfTest.opNestedStatic thrpt 30
1433953744.298 ± 21414147.936 ops/s*
On Mon, Aug 28, 2023 at 11:13 AM Quân Anh Mai <anhmdq at gmail.com> wrote:
> Hi,
>
> Polymorphism is a performance killer. Normally this does not matter much,
> but for thin wrappers such as Optional, this is one of the most important
> factor.
>
> Behaviour polymorphism requires virtual dispatch and prevents inlining.
> This is really detrimental, as simple operations such as a getter which is
> previously only consisted of a memory load, in the presence of polymorphism
> would have to go through a 10-time more expensive virtual dispatch.
> Function calls, especially virtual ones, are also opaque and prohibit
> compiler optimisations.
>
> Layout polymorphism prevents direct accesses and requires indirection.
> This means that for every instance of Optional created a memory allocation
> is required. Optional is expected to be a near zero-cost abstraction in the
> presence of Value classes, so making it polymorphic is unacceptable.
>
> Thanks.
>
> On Wed, 23 Aug 2023 at 22:43, Oleksii Kucheruk <iselo+openjdk at raccoons.co>
> wrote:
>
>> Hi there.
>> I have found that `java.util.Optional` is written procedural style and
>> has `ifnonnull` checks in each method. I propose to refactor `Optional` in
>> accordance to OOP-style. This will eliminates all unnecessary
>> `if`-statements, removes duplications and reduces bytecode size more then
>> twice.
>>
>> I have two solutions:
>> 1. Completely dynamic that avoids single static `EMPTY` instance and
>> unchecked casting of each `Optional.empty()`
>> 2. Preserving original single static `EMPTY` per VM.
>>
>> Also there are couple methods that throws NPE due to calling methods on
>> null-objects and requires to add `Objects.requireNonNull(...)`.
>>
>> OptionalInt, OptionalDouble, OptionalLong could be refactored same way
>> even with remove of additional boolean variable `isPresent`.
>>
>> Since I'm new here any guidance will be helpful.
>> Thank you in advance.
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/core-libs-dev/attachments/20230828/a96c7add/attachment-0001.htm>
More information about the core-libs-dev
mailing list