Request for addition of properties for HashMap's Load Factor
roshan mangal
roshanmangal at gmail.com
Sat Oct 27 10:08:41 UTC 2018
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