RFR: 8006593: Performance and compatibility improvements to hash based Map implementations
Mike Duigou
mike.duigou at oracle.com
Tue Mar 5 22:46:48 UTC 2013
I have updated the webrev to remove the useAltHashing boolean.
http://cr.openjdk.java.net/~mduigou/JDK-8006593/5/webrev/
Mike
On Mar 5 2013, at 00:43 , Peter Levart wrote:
> Hi Mike,
>
> You could also get rid of boolean useAltHashing field in both HashMap and Hashtable. It saves 8 bytes in both HM and HT objects in 32bit addressing modes (64bit addressing modes are not affected due to different alignment). Like the following (a patch against your webrev):
>
> Index: jdk/src/share/classes/java/util/Hashtable.java
> ===================================================================
> --- jdk/src/share/classes/java/util/Hashtable.java (date 1362469665000)
> +++ jdk/src/share/classes/java/util/Hashtable.java (revision )
> @@ -213,12 +213,6 @@
> }
>
> /**
> - * If {@code true} then perform alternative hashing of String keys to reduce
> - * the incidence of collisions due to weak hash code calculation.
> - */
> - transient boolean useAltHashing = false;
> -
> - /**
> * A randomizing value associated with this instance that is applied to
> * hash code of keys to make hash collisions harder to find.
> */
> @@ -228,8 +222,8 @@
> * Initialize the hashing mask value.
> */
> final boolean initHashSeedAsNeeded(int capacity) {
> - boolean currentAltHashing = useAltHashing;
> - useAltHashing = sun.misc.VM.isBooted() &&
> + boolean currentAltHashing = hashSeed != 0;
> + boolean useAltHashing = sun.misc.VM.isBooted() &&
> (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
> boolean switching = currentAltHashing ^ useAltHashing;
> if (switching) {
> Index: jdk/src/share/classes/java/util/HashMap.java
> ===================================================================
> --- jdk/src/share/classes/java/util/HashMap.java (date 1362472425000)
> +++ jdk/src/share/classes/java/util/HashMap.java (revision )
> @@ -224,17 +224,11 @@
> }
>
> /**
> - * If {@code true} then perform alternative hashing of String keys to reduce
> - * the incidence of collisions due to weak hash code calculation.
> - */
> - transient boolean useAltHashing = false;
> -
> - /**
> * A randomizing value associated with this instance that is applied to
> - * hash code of keys to make hash collisions harder to find. Initialized via
> - * sun.misc.Unsafe when needed.
> + * hash code of keys to make hash collisions harder to find.
> + * Also used (when != 0) to indicate use of alternative String hashing.
> */
> - transient int hashSeed = 0;
> + transient int hashSeed;
>
> /**
> * Constructs an empty <tt>HashMap</tt> with the specified initial
> @@ -319,8 +313,8 @@
> * really need it.
> */
> final boolean initHashSeedAsNeeded(int capacity) {
> - boolean currentAltHashing = useAltHashing;
> - useAltHashing = sun.misc.VM.isBooted() &&
> + boolean currentAltHashing = hashSeed != 0;
> + boolean useAltHashing = sun.misc.VM.isBooted() &&
> (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
> boolean switching = currentAltHashing ^ useAltHashing;
> if (switching) {
> @@ -339,12 +333,9 @@
> * in lower bits. Note: Null keys always map to hash 0, thus index 0.
> */
> final int hash(Object k) {
> - int h = 0;
> - if (useAltHashing) {
> - if (k instanceof String) {
> + int h = hashSeed;
> + if (h != 0 && k instanceof String) {
> - return sun.misc.Hashing.stringHash32((String) k);
> + return sun.misc.Hashing.stringHash32((String) k);
> - }
> - h = hashSeed;
> }
>
> h ^= k.hashCode();
>
>
> Regards, Peter
>
> On 03/05/2013 07:08 AM, Mike Duigou wrote:
>> After looking more closely at the single read reference to hashSeed I decided to simplify things and make hashSeed non-final in both Hashtable and HashMap.
>>
>> I've posted a refreshed webrev.
>>
>> http://cr.openjdk.java.net/~mduigou/JDK-8006593/4/webrev/
>>
>> Mike
>>
>> On Mar 4 2013, at 14:25 , Peter Levart wrote:
>>
>>>
>>> On 03/04/2013 11:14 PM, Peter Levart wrote:
>>>> Hi mike,
>>>>
>>>> I doubt (haven't tried it really with your code) that hashSeed will be seen by code to be anything else but 0, since it is initialized to a constant value. For example, this code:
>>>>
>>>> public class ModifyingFinalTest {
>>>> static final Unsafe unsafe;
>>>> static final long valueOffset;
>>>>
>>>> static {
>>>> try {
>>>> Field f = Unsafe.class.getDeclaredField("theUnsafe");
>>>> f.setAccessible(true);
>>>> unsafe = (Unsafe)f.get(null);
>>>> valueOffset = unsafe.objectFieldOffset(ModifyingFinalTest.class.getDeclaredField("value"));
>>>> }
>>>> catch (IllegalAccessException | NoSuchFieldException e) {
>>>> throw new Error(e);
>>>> }
>>>> }
>>>>
>>>> final int value = 0;
>>>>
>>>> void test() {
>>>> unsafe.putIntVolatile(this, valueOffset, 1);
>>>> printValue();
>>>> unsafe.putIntVolatile(this, valueOffset, 2);
>>>> printValue();
>>>> unsafe.putIntVolatile(this, valueOffset, 3);
>>>> printValue();
>>>> }
>>>>
>>>> void printValue() {
>>>> System.out.println(value);
>>>> }
>>>>
>>>> public static void main(String[] args) {
>>>> new ModifyingFinalTest().test();
>>>> }
>>>> }
>>>>
>>>>
>>>> Prints:
>>>>
>>>> 0
>>>> 0
>>>> 0
>>>>
>>>>
>>>> It's a different thing, if the initialization is changed to:
>>>>
>>>> final int value = "".length();
>>>>
>>>> But I don't know if each access in source is actually guaranteed to translate to a real read of field in this case either. Is Unsafe.putIntVolatile() making this happen somehow magically?
>>>
>>> Well, that's not what I wanted to ask. I wanted to ask if the first access in source that happens after Unsafe.putIntVolatile() in same thread is guaranteed to read the field and not re-use a cached value ready in some register for example. Is Unsafe.putIntVolatile() making this happen somehow magically?
>>>
>>>>
>>>> Regards, Peter
>>>>
>>>>
>>>> On 03/04/2013 09:21 PM, Mike Duigou wrote:
>>>>> Hello all;
>>>>>
>>>>> The alternative hashing implementation introduced in 7u6 added an unfortunate bottleneck to the initialization of HashMap and Hashtable instances. This patch avoids the performance bottleneck of using a shared Random instance by using a ThreadLocalRandom instead.
>>>>>
>>>>> Along with this change are some additional performance improvements to further reduce the overhead of the alternative hashing feature and generally improve the performance of Hashtable or HashMap.
>>>>>
>>>>> c
>>>>>
>>>>> Once review is completed here this patch will be proposed to JDK7u-dev for integration into the next 7u performance/feature release.
>>>>>
>>>>> Mike
>>>>>
>>>>
>>>
>>
>
More information about the core-libs-dev
mailing list