Yes, it's 2 subclasses hierarchy. https://github.com/iselo/jdk-optional/tree/master/lib/src I'm open to learning about my pitfalls.
# JMH version: 1.36 # VM version: JDK 20.0.2, Java HotSpot(TM) 64-Bit Server VM, 20.0.2+9-78 # VM invoker: /Library/Java/JavaVirtualMachines/jdk-20.jdk/Contents/Home/bin/java # VM options: -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/Users/iselo/IdeaProjects/jmh/lib/build/tmp/jmh -Duser.country=GB -Duser.language=en -Duser.variant # Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable) # Warmup: 2 iterations, 10 s each # Measurement: 10 iterations, 10 s each # Timeout: 10 min per iteration # Threads: 1 thread, will synchronize iterations # Benchmark mode: Throughput, ops/time On Mon, Aug 28, 2023 at 4:04 PM - <liangchenb...@gmail.com> wrote: > Can you share the benchmark code? Maybe C2 profiled class hierarchy, > determined your optional only has 2 subclasses and can thus generate > optimized machine code. Or maybe your benchmark is wrong that the result is > not consumed by blackhole so the whole benchmark is moot. We need the code > to make a verdict. > > Chen > > On Mon, Aug 28, 2023 at 6:27 PM Oleksii Kucheruk < > iselo+open...@raccoons.co> wrote: > >> 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 <anh...@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+open...@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. >>>> >>>