RFR(S): 8087143: Reduce Symbol::_identity_hash to 2 bytes
Ioi Lam
ioi.lam at oracle.com
Mon Jun 22 17:23:02 UTC 2015
On 6/22/15 1:49 AM, Aleksey Shipilev wrote:
> On 06/18/2015 11:46 PM, Yumin Qi wrote:
>> Please review the small change for
>>
>> bug: https://bugs.openjdk.java.net/browse/JDK-8087143
>> webrev: http://cr.openjdk.java.net/~minqi/8087143/webrev01/
>>
>> Summary: _identity_hash is an integer in Symbol (SymbolBase), it is used
>> to compute hash bucket index by modulus division of table size.
>> Currently in hotspot, no table size is more than 65535 so we can use
>> short instead. For case with table size over 65535 we can use the first
>> two bytes of symbol data to be as the upper 16 bits for the calculation
>> but rare cases.
> Wait a minute.
>
> 1) We know there are users who deal with overloaded system hash tables
> by increasing their sizes (bucket count) beyond defaults, and into >64K
> area. The symbol table performance in that area is important. Therefore,
> the considerations for entropy beyond 64K are important, and should not
> be discounted as rare case.
Hi Aleksey,
In most cases, if there's ever a need to increase the size of any
hashtables that uses Symbol::identity_hash(), it would be the system
dictionary. (The SymbolTable computes the hash using the string
contents, not Symbol::identity_hash(). See SymbolTable::hash_symbo).
Today, the system dictionary size is calculated in
SystemDictionary::calculate_systemdictionary_size. The biggest possible
size is 99991.
const int SystemDictionary::_primelist[_prime_array_size] =
{1009,2017,4049,5051,10103,
20201,40423,99991};
I think 99991 should work with cases where there are 1,000,000 loaded
classes (average 10 entries per bucket). If we load more than that many
classes, the system dictionary lookup speed probably won't be your
biggest problem.
The hash value for the system dictionary is calculated here in hashtable.hpp
unsigned int compute_hash(Symbol* name, ClassLoaderData* loader_data) {
unsigned int name_hash = name->identity_hash();
// loader is null with CDS
assert(loader_data != NULL || UseSharedSpaces || DumpSharedSpaces,
"only allowed with shared spaces");
unsigned int loader_hash = loader_data == NULL ? 0 :
loader_data->identity_hash();
return name_hash ^ loader_hash;
}
Assuming the worst case, where all classes are loaded by the same class
loader, we want to increase the entropy of name->identity_hash().
Since it's absolutely unlikely that anyone would have a system
dictionary (or any hash table) larger than 16M entries (even if we
change SystemDictionary::calculate_systemdictionary_size), let's forget
the top 8 bits, and just look at the bottom 24 bits of
Symbol::identity_hash().
Most classes names start with "ja" or "co" (java.*, or com.*), using
the first two bytes of the _body[] doesn't really help that much. But it
probably won't hurt, either. We can't use _body[2] or higher because it
may cross a page boundary and we may read from an unallocated page.
For our usage (the worst case table size is a small multiple of 65536),
if we do
(unsigned)((uintptr_t)(this) >> (LogMinObjAlignmentInBytes + 3))
this should work with allocators that use a 64-bit alignment
Also, the _length should be random enough, so let's add that to the hash
code:
unsigned identity_hash() {
return (unsigned)_identity_hash
| ((unsigned)_body[0]) << 16)
| ((unsigned)_body[1]) << 16) // increase randomness of bits [16 .. 23]
| ((unsigned)_length)
| ((unsigned)((uintptr_t)(this) >> (LogMinObjAlignmentInBytes + 3)));
}
I think this would give enough entropy for all probably usage scenarios.
What do you think?
- Ioi
> 2) This change seems to make a hash function much more heavy-weight
> than before. But hash functions are supposed to be fast. At very least,
> ditch the division [1], and do something along the lines of:
>
> unsigned upper = (unsigned)((uintptr_t)(this) >>
> LogMinObjAlignmentInBytes);
>
> 3) Pointer addresses, especially when allocated in arena-ed allocators,
> are known to have low entropy (randomness). Mixing in _body[0] would
> help only so much. It might be better to ditch "this", and the mix more
> _body values again? Note that it is better to concat the values rather
> than xor them to get more randomness.
>
> Hence, I would go with this hash:
>
> unsigned identity_hash() {
> return (unsigned)_identity_hash
> | ((unsigned)_body[0]) << 16);
> | ((unsigned)_body[1]) << 24);
> }
>
> This also yields a nicer generated code [2].
>
> Or, if we feel lucky today, make a single load [3]:
>
> unsigned identity_hash() {
> return (unsigned)_identity_hash
> | ((((jshort*)_body)[0]) << 16);
> }
>
>
> Thanks,
> -Aleksey
>
> [1] webrev.02 version; gcc 4.8.2, x86_64, __attribute__ ((noinline))
> added to Symbol::identity_hash:
>
> 0000000000565ad0 <_ZN6Symbol13identity_hashEv>:
> ; %rcx = ObjectAlignmentInBytes
> 565ad0: 48 8d 0d 69 a0 9a 00 lea 0x9aa069(%rip),%rcx
>
> 565ad7: 55 push %rbp
> 565ad8: 48 89 f8 mov %rdi,%rax
>
> ; %edx = 0
> 565adb: 31 d2 xor %edx,%edx
>
> ; %rbp = "this"
> 565add: 48 89 e5 mov %rsp,%rbp
>
> ; "this"/ObjectAlignmentInBytes, result in %eax
> 565ae0: 48 f7 31 divq (%rcx)
>
> ; sign-extended load of a ***byte*** _body[0] to %edx
> 565ae3: 0f be 57 06 movsbl 0x6(%rdi),%edx
>
> 565ae7: 5d pop %rbp
>
> ; %edx = (upper ^ _body[0])
> 565ae8: 31 c2 xor %eax,%edx
>
> ; %eax = _identity_hash
> 565aea: 0f bf 47 04 movswl 0x4(%rdi),%eax
>
> ; %edx = (upper ^ _body[0]) << 16
> 565aee: c1 e2 10 shl $0x10,%edx
>
> ; %eax = _identity_hash + (upper ^ _body[0]) << 16
> 565af1: 01 d0 add %edx,%eax
>
> 565af3: c3 retq
>
>
> [2] proposed version; gcc 4.8.2, x86_64, __attribute__ ((noinline))
> added to Symbol::identity_hash:
>
> 0000000000565ad0 <_ZN6Symbol13identity_hashEv>:
> ; %eax = _body[0]
> 565ad0: 0f be 47 06 movsbl 0x6(%rdi),%eax
>
> ; %edx = _body[1]
> 565ad4: 0f be 57 07 movsbl 0x7(%rdi),%edx
>
> 565ad8: 55 push %rbp
> 565ad9: 48 89 e5 mov %rsp,%rbp
>
> ; %edx = _body[1] << 24
> 565adc: c1 e2 18 shl $0x18,%edx
>
> ; %edx = _body[0] << 16
> 565adf: c1 e0 10 shl $0x10,%eax
>
> ; %eax = (_body[0] << 16) | (_body[0] << 24)
> 565ae2: 09 d0 or %edx,%eax
>
> ; %edx = _identity_hash
> 565ae4: 0f bf 57 04 movswl 0x4(%rdi),%edx
> 565ae8: 5d pop %rbp
>
> ; % eax = _identity_hash | (_body[0] << 16) | (_body[1] << 24)
> 565ae9: 09 d0 or %edx,%eax
>
> 565aeb: c3 retq
>
> [3] proposed version #2; gcc 4.8.2, x86_64, __attribute__ ((noinline))
> added to Symbol::identity_hash:
>
> 0000000000565ad0 <_ZN6Symbol13identity_hashEv>:
> ; %eax = (_body[0], _body[1])
> 565ad0: 0f bf 47 06 movswl 0x6(%rdi),%eax
>
> ; %edx = _identity_hash
> 565ad4: 0f bf 57 04 movswl 0x4(%rdi),%edx
>
> 565ad8: 55 push %rbp
> 565ad9: 48 89 e5 mov %rsp,%rbp
>
> ; %eax = (_body[0], _body[1]) << 16
> 565adc: c1 e0 10 shl $0x10,%eax
>
> ; %eax = _identity_hash | (_body[0], _body[1])
> 565adf: 09 d0 or %edx,%eax
>
> 565ae1: 5d pop %rbp
> 565ae2: c3 retq
>
>
More information about the hotspot-runtime-dev
mailing list