On Mon, 17 Apr 2023 13:27:43 GMT, olivergillespie <d...@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.
>
> olivergillespie has refreshed the contents of this pull request, and previous 
> commits have been removed. Incremental views are not available. The pull 
> request now contains one commit:
> 
>   8306075: Micro-optimize Enum.hashCode

src/java.base/share/classes/java/lang/Enum.java line 181:

> 179:      */
> 180:     @Stable
> 181:     private int hash;

Should not we hide the field from reflection?

I understand this is an implementation detail, however, it makes a big 
difference that you add a private field to the *public* class that is *meant* 
to be inherited by user classes. Although I'm not aware of a particular 
example, this can be a breaking change for user code that does introspection on 
enums.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/13491#discussion_r1168724994

Reply via email to