On Mon, 17 Apr 2023 12:14:55 GMT, Pavel Rappo <pra...@openjdk.org> wrote:
>> 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. > > src/java.base/share/classes/java/lang/Enum.java line 175: > >> 173: * >> 174: * @implNote Once initialized, the field value does not change. >> 175: * Hotspot's identity hash code generation also never returns zero > >> Hotspot's identity hash code generation also never returns zero > > Isn't that behavior VM-specific? Also, where is it documented? Yes, it is implementation-specific, that is why it says "Hotspot's identity hash code". The relevant code blocks are https://github.com/openjdk/jdk/blob/cc60f2ff3f16bdb04917e09cb87f09bd544f1f8b/src/hotspot/share/oops/markWord.hpp#L231-L233 (property) and https://github.com/openjdk/jdk/blob/cc60f2ff3f16bdb04917e09cb87f09bd544f1f8b/src/hotspot/share/runtime/synchronizer.cpp#L826-L827 (invariant). > src/java.base/share/classes/java/lang/Enum.java line 191: > >> 189: int hc = hash; >> 190: if (hc == 0) { >> 191: hc = hash = System.identityHashCode(this); > > Why not `hc = hash = super.hashCode()`? Saves the virtual call, makes for a simpler intrinsic path (no need to handle NPE and fold away, for example), since we know the super-class is already `java.lang.Object`. Unless I miss something else here... ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/13491#discussion_r1168654218 PR Review Comment: https://git.openjdk.org/jdk/pull/13491#discussion_r1168656186