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