Request for addition of properties for HashMap's Load Factor

Claes Redestad claes.redestad at oracle.com
Sun Oct 28 14:06:34 UTC 2018


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/ConcurrentHashMap.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/java/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_str);
> +        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