Request for addition of properties for HashMap's Load Factor

Martin Buchholz martinrb at google.com
Sun Oct 28 20:34:56 UTC 2018


Adding the proposed system properties is probably not going to happen -
getting good results on specjbb is not the only goal.

I agree with Claes that adding machinery to make it easier to create a
modified JDK is a good direction.  If nothing else, it's open source and
you can build your own JDK with local mods.

On Sun, Oct 28, 2018 at 7:06 AM, Claes Redestad <claes.redestad at oracle.com>
wrote:

> Hi,
>
> I'd prefer something like this to change the value of the constants rather
> than
> add a property lookup in a lot of constructors, which might cause
> regressions in some
> applications.
>
> There might be upcoming ways to initialize such read-only properties
> before we load in
> System.getProperties (and thus CHM) so we can retain the finality of the
> load factor
> constants.
>
> One could also easily imagine a jlink plugin that allows you to customize
> the constants:
> this would allow you to tune for your deployments centrally without having
> to sprinkle
> -Dproperties everywhere.
>
> /Claes
>
>
> On 2018-10-27 12:08, roshan mangal wrote:
>
>> Hi All,
>>
>> HashMap's performance significantly depends on Load Factor. A lower value
>> of Load Factor will increase the table size but will reduce collision and
>> boost performance.
>> It would be helpful if we can have "LoadFactor" as a tunable based on
>> application usage.
>>
>> Currently, only two options are available for setting of Load Factor's
>> value.
>>
>> 1. Use the default value of Load Factor i.e. 0.75 or,
>> 2. Use application provided value, which is used during HashMap's object
>> creation in the constructor.
>>
>> Application user has no option to change it to get a better-optimized
>> value
>> as per usage.
>>
>> I am proposing an additional option, which is java property(-D) driven
>> LoadFactor for  three classes listed below
>>
>> 1. src/java.base/share/classes/java/util/HashMap.java
>> 2. src/java.base/share/classes/java/util/concurrent/ConcurrentH
>> ashMap.java
>> 3. src/java.base/share/classes/java/util/WeakHashMap.java
>> note: If other classes need Load Factor java property, we can add there
>> also.
>>
>> I have tuned Load Factor for Specjbb2015 and got 1% to 2% improvement on
>> the multi-JVM max-JOPS score at loadfactor=0.6.
>>
>> Please find a patch for load factor properties addition in jdk12 below.
>> Usage:
>> -Djava.util.HashMap.loadfactor=< value between 0 to 1 >
>> -Djava.util.concurrent.ConcurrentHashMap.loadfactor=< value between 0 to
>> 1
>>
>>> -Djava.util.WeakHashMap.loadfactor=< value between 0 to 1 >
>>>
>> ############################# PATCH ##################################
>> diff -r 2e495bbdc2b7 src/java.base/share/classes/java/util/HashMap.java
>> --- a/src/java.base/share/classes/java/util/HashMap.java Mon Oct 22
>> 14:03:06 2018 +0800
>> +++ b/src/java.base/share/classes/java/util/HashMap.java Sat Oct 27
>> 20:31:19 2018 +0530
>> @@ -35,7 +35,7 @@
>>   import java.util.function.Consumer;
>>   import java.util.function.Function;
>>   import jdk.internal.misc.SharedSecrets;
>> +import sun.security.action.GetPropertyAction;
>>   /**
>>    * Hash table based implementation of the {@code Map} interface.  This
>>    * implementation provides all of the optional map operations, and
>> permits
>> @@ -243,9 +243,9 @@
>>       static final int MAXIMUM_CAPACITY = 1 << 30;
>>
>>       /**
>> -     * The load factor used when none specified in constructor.
>> +     * The load factor used when none specified in constructor or
>> property.
>>        */
>> -    static final float DEFAULT_LOAD_FACTOR = 0.75f;
>> +    static float DEFAULT_LOAD_FACTOR = 0.75f;
>>
>>       /**
>>        * The bin count threshold for using a tree rather than list for a
>> @@ -426,7 +426,7 @@
>>        *
>>        * @serial
>>        */
>> -    final float loadFactor;
>> +     float loadFactor;
>>
>>       /* ---------------- Public operations -------------- */
>>
>> @@ -461,6 +461,16 @@
>>        */
>>       public HashMap(int initialCapacity) {
>>           this(initialCapacity, DEFAULT_LOAD_FACTOR);
>> +
>> +        Properties props = GetPropertyAction.privilegedGetProperties();
>> +        if( props != null ) {
>> +          String loadfactor =
>> +            props.getProperty("java.util.HashMap.loadfactor");
>> +          float LOAD_FACTOR = ( loadfactor == null ||
>> loadfactor.isEmpty()
>> ) ? DEFAULT_LOAD_FACTOR : Float.parseFloat(loadfactor);
>> +          DEFAULT_LOAD_FACTOR = ( LOAD_FACTOR < 0 || LOAD_FACTOR > 1.0 )
>> ?
>> DEFAULT_LOAD_FACTOR : LOAD_FACTOR ;
>> +        }
>> +        this.loadFactor = DEFAULT_LOAD_FACTOR;
>> +
>>       }
>>
>>       /**
>> @@ -468,7 +478,17 @@
>>        * (16) and the default load factor (0.75).
>>        */
>>       public HashMap() {
>> +
>>           this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields
>> defaulted
>> +
>> +        Properties props = GetPropertyAction.privilegedGetProperties();
>> +        if( props != null ) {
>> +          String loadfactor =
>> +            props.getProperty("java.util.HashMap.loadfactor");
>> +          float LOAD_FACTOR = ( loadfactor == null ||
>> loadfactor.isEmpty()
>> ) ? DEFAULT_LOAD_FACTOR : Float.parseFloat(loadfactor);
>> +          DEFAULT_LOAD_FACTOR = ( LOAD_FACTOR < 0 || LOAD_FACTOR > 1.0 )
>> ?
>> DEFAULT_LOAD_FACTOR : LOAD_FACTOR ;
>> +        }
>> +        this.loadFactor = DEFAULT_LOAD_FACTOR;
>>       }
>>
>>       /**
>> @@ -481,6 +501,14 @@
>>        * @throws  NullPointerException if the specified map is null
>>        */
>>       public HashMap(Map<? extends K, ? extends V> m) {
>> +       Properties props = GetPropertyAction.privilegedGetProperties();
>> +       if( props != null ) {
>> +         String loadfactor =
>> +           props.getProperty("java.util.HashMap.loadfactor");
>> +         float LOAD_FACTOR = ( loadfactor == null || loadfactor.isEmpty()
>> ) ? DEFAULT_LOAD_FACTOR : Float.parseFloat(loadfactor);
>> +         DEFAULT_LOAD_FACTOR = ( LOAD_FACTOR < 0 || LOAD_FACTOR > 1.0 ) ?
>> DEFAULT_LOAD_FACTOR : LOAD_FACTOR ;
>> +       }
>> +
>>           this.loadFactor = DEFAULT_LOAD_FACTOR;
>>           putMapEntries(m, false);
>>       }
>> diff -r 2e495bbdc2b7 src/java.base/share/classes/ja
>> va/util/WeakHashMap.java
>> --- a/src/java.base/share/classes/java/util/WeakHashMap.java Mon Oct 22
>> 14:03:06 2018 +0800
>> +++ b/src/java.base/share/classes/java/util/WeakHashMap.java Sat Oct 27
>> 20:31:19 2018 +0530
>> @@ -31,7 +31,7 @@
>>   import java.util.function.BiConsumer;
>>   import java.util.function.BiFunction;
>>   import java.util.function.Consumer;
>> +import sun.security.action.GetPropertyAction;
>>
>>   /**
>>    * Hash table based implementation of the {@code Map} interface, with
>> @@ -152,7 +152,19 @@
>>       /**
>>        * The load factor used when none specified in constructor.
>>        */
>> -    private static final float DEFAULT_LOAD_FACTOR = 0.75f;
>> +    private static float DEFAULT_LOAD_FACTOR = 0.75f;
>> +
>> +    static {
>> +      Properties props = GetPropertyAction.privilegedGetProperties();
>> +      if( props != null ){
>> +        final String loadfactor =
>> +                 props.getProperty("java.util.WeakHashMap.loadfactor");
>> +
>> +        float LOAD_FACTOR = ( loadfactor == null || loadfactor.isEmpty()
>> )
>> ? DEFAULT_LOAD_FACTOR : Float.parseFloat(loadfactor);
>> +        DEFAULT_LOAD_FACTOR = ( LOAD_FACTOR < 0 || LOAD_FACTOR > 1.0 ) ?
>> DEFAULT_LOAD_FACTOR : LOAD_FACTOR ;
>> +      }
>> +
>> +    }
>>
>>       /**
>>        * The table, resized as necessary. Length MUST Always be a power of
>> two.
>> diff -r 2e495bbdc2b7
>> src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java
>> ---
>> a/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java
>> Mon Oct 22 14:03:06 2018 +0800
>> +++
>> b/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java
>> Sat Oct 27 20:31:19 2018 +0530
>> @@ -69,7 +69,8 @@
>>   import java.util.function.ToLongFunction;
>>   import java.util.stream.Stream;
>>   import jdk.internal.misc.Unsafe;
>> +import sun.security.action.GetPropertyAction;
>> +import java.util.Properties;
>>   /**
>>    * A hash table supporting full concurrency of retrievals and
>>    * high expected concurrency for updates. This class obeys the
>> @@ -532,7 +533,7 @@
>>        * simpler to use expressions such as {@code n - (n >>> 2)} for
>>        * the associated resizing threshold.
>>        */
>> -    private static final float LOAD_FACTOR = 0.75f;
>> +    private static float LOAD_FACTOR = 0.75f;
>>
>>       /**
>>        * The bin count threshold for using a tree rather than list for a
>> @@ -891,6 +892,15 @@
>>        */
>>       public ConcurrentHashMap(int initialCapacity,
>>                                float loadFactor, int concurrencyLevel) {
>> +      Properties props = GetPropertyAction.privilegedGetProperties();
>> +      if( props != null ) {
>> +        final String loadfactor_str =
>> +
>>   props.getProperty("java.util.concurrent.ConcurrentHashMap.loadfactor");
>> +        float PROP_LOAD_FACTOR = ( loadfactor_str == null ||
>> loadfactor_str.isEmpty() ) ? LOAD_FACTOR : Float.parseFloat(loadfactor_st
>> r);
>> +        LOAD_FACTOR = ( PROP_LOAD_FACTOR < 0 || PROP_LOAD_FACTOR > 1.0 )
>> ?
>> LOAD_FACTOR : PROP_LOAD_FACTOR ;
>> +        // Modify if property is set
>> +        loadFactor = ( loadfactor_str == null || loadfactor_str.isEmpty()
>> ) ? loadFactor : LOAD_FACTOR;
>> +      }
>>           if (!(loadFactor > 0.0f) || initialCapacity < 0 ||
>> concurrencyLevel <= 0)
>>               throw new IllegalArgumentException();
>>           if (initialCapacity < concurrencyLevel)   // Use at least as
>> many
>> bins
>>
>> ######################################################################
>>
>> Thanks,
>> Roshan Mangal
>>
>
>


More information about the jdk-dev mailing list