> Improve the speed of Enum.hashCode by caching the identity hashcode on first > use. I've seen an application where Enum.hashCode is a hot path, and this is > fairly simple speedup. The memory overhead is low; in enums with no extra > fields there is already a 4-byte space due to alignment so this new field can > slot in 'for free'. In other cases, the singleton nature of enum values means > that the number of total instances is typically very low, so a small > per-instance overhead is not a concern. > > Please see more discussion/explanation in the [original enhancement > request](https://bugs.openjdk.org/browse/JDK-8306075). > > ### Benchmark > > > > Before: > > Benchmark Mode Cnt Score Error Units > # Intel Cascade lake > EnumHashCode.constant avgt 15 1.602 ± 0.011 ns/op > EnumHashCode.field avgt 15 1.681 ± 0.014 ns/op > # Arm Neoverse N1 > EnumHashCode.constant avgt 15 1.642 ± 0.033 ns/op > EnumHashCode.field avgt 15 1.717 ± 0.059 ns/op > > > > After: > > Benchmark Mode Cnt Score Error Units > # Intel Cascade lake > EnumHashCode.constant avgt 15 0.479 ± 0.001 ns/op > EnumHashCode.field avgt 15 0.799 ± 0.002 ns/op > # Arm Neoverse N1 > EnumHashCode.constant avgt 15 0.802 ± 0.002 ns/op > EnumHashCode.field avgt 15 1.059 ± 0.056 ns/op > > > Using `-prof perfasm` on the benchmark, we can compare the generated code for > x86_64: > > Before: > > │ 0x00007fae4868dd17: lea (%r12,%r10,8),%rsi ;*getfield e > {reexecute=0 rethrow=0 return_oop=0} > │ ; - > org.sample.EnumHashCode::field@1 (line 24) > │ ; - > org.sample.jmh_generated.EnumHashCode_field_jmhTest::field_avgt_jmhStub@17 > (line 186) > │ 0x00007fae4868dd1b: mov (%rsi),%r10 > │ 0x00007fae4868dd1e: mov %r10,%r11 > │ 0x00007fae4868dd21: and $0x3,%r11 > │ 0x00007fae4868dd25: cmp $0x1,%r11 > │ 0x00007fae4868dd29: jne 0x00007fae4868dcc6 > │ 0x00007fae4868dd2b: shr $0x8,%r10 > │ 0x00007fae4868dd2f: mov %r10d,%eax > │ 0x00007fae4868dd32: and $0x7fffffff,%eax > │ 0x00007fae4868dd37: test %eax,%eax > │ 0x00007fae4868dd39: je 0x00007fae4868dcc6 ;*invokespecial > hashCode {reexecute=0 rethrow=0 return_oop=0} > │ ; - > java.lang.Enum::hashCode@1 (line 175) > > > This is the normal Object.hashCode intrinsic, which involves reading the > object header, extracting the hash code and handling two slow-path cases > (displaced object header, hash not initialized). > > After: > > > │ 0x00007f550068e3b4: mov 0x10(%r12,%r10,8),%r8d <-- read the hash > field > │ 0x00007f550068e3b9: test %r8d,%r8d <-- if (hash == 0) > │ 0x00007f550068e3bc: je 0x00007f550068e413 <-- slow init path, > only taken on first use > > > Thanks @shipilev for help with the implementation and interpreting the > generated code.
olivergillespie has updated the pull request incrementally with one additional commit since the last revision: Make the new field transient Avoid changing the serialized form. ------------- Changes: - all: https://git.openjdk.org/jdk/pull/13491/files - new: https://git.openjdk.org/jdk/pull/13491/files/5039ffff..f5608481 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=13491&range=01 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=13491&range=00-01 Stats: 1 line in 1 file changed: 0 ins; 0 del; 1 mod Patch: https://git.openjdk.org/jdk/pull/13491.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/13491/head:pull/13491 PR: https://git.openjdk.org/jdk/pull/13491