tmf: Implement a cache of TmfStateValue

This patch provides a caching implementation in the TmfStateValue factory.
TmfStateValue are immutable and can be shared.

By changing the size of the cache, we observed this cache ratio.

size         hit      total     ratio
  2 count:  7369956/15551000 [0.4739216770625683]
  4 count: 11742495/15530000 [0.7561168705730843]
  8 count: 14622383/15545000 [0.9406486330009649]
 16 count: 15007948/15552000 [0.965017232510288]
 32 count: 15372868/15543000 [0.9890541079585665]
 64 count: 15432506/15523000 [0.9941703279005347]
128 count: 15488720/15534000 [0.9970851036436205]

Which seems to be an excellent ratio for a really small cache.
The same idea is used with Integer.valueOf(...).

Change-Id: I3701b900035d83b19531101ab60e7292899d8cef
Signed-off-by: Etienne Bergeron <etienne.bergeron@gmail.com>
Reviewed-on: https://git.eclipse.org/r/19030
Reviewed-by: Alexandre Montplaisir <alexmonthy@voxpopuli.im>
IP-Clean: Alexandre Montplaisir <alexmonthy@voxpopuli.im>
Tested-by: Alexandre Montplaisir <alexmonthy@voxpopuli.im>
diff --git a/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statevalue/TmfStateValue.java b/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statevalue/TmfStateValue.java
index 6330cae..acb17df 100644
--- a/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statevalue/TmfStateValue.java
+++ b/lttng/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statevalue/TmfStateValue.java
@@ -29,6 +29,19 @@
  * @author Alexandre Montplaisir
  */
 public abstract class TmfStateValue implements ITmfStateValue {
+
+    // ------------------------------------------------------------------------
+    // State value caches (sizes must be powers of 2)
+    // ------------------------------------------------------------------------
+
+    private static final int INT_CACHE_SIZE = 128;
+    private static final int LONG_CACHE_SIZE = 128;
+    private static final int DOUBLE_CACHE_SIZE = 128;
+
+    private static final IntegerStateValue intCache[] = new IntegerStateValue[INT_CACHE_SIZE];
+    private static final LongStateValue longCache[] = new LongStateValue[LONG_CACHE_SIZE];
+    private static final DoubleStateValue doubleCache[] = new DoubleStateValue[DOUBLE_CACHE_SIZE];
+
     // ------------------------------------------------------------------------
     // Factory methods to instantiate new state values
     // ------------------------------------------------------------------------
@@ -56,7 +69,17 @@
      * @return The newly-created TmfStateValue object
      */
     public static TmfStateValue newValueInt(int intValue) {
-        return new IntegerStateValue(intValue);
+        /* Lookup in cache for the existence of the same value. */
+        int offset = intValue & (INT_CACHE_SIZE - 1);
+        IntegerStateValue cached = intCache[offset];
+        if (cached != null && cached.unboxInt() == intValue) {
+            return cached;
+        }
+
+        /* Not in cache, create a new value and cache it. */
+        IntegerStateValue newValue = new IntegerStateValue(intValue);
+        intCache[offset] = newValue;
+        return newValue;
     }
 
     /**
@@ -68,7 +91,17 @@
      * @since 2.0
      */
     public static TmfStateValue newValueLong(long longValue) {
-        return new LongStateValue(longValue);
+        /* Lookup in cache for the existence of the same value. */
+        int offset = (int) longValue & (LONG_CACHE_SIZE - 1);
+        LongStateValue cached = longCache[offset];
+        if (cached != null && cached.unboxLong() == longValue) {
+            return cached;
+        }
+
+        /* Not in cache, create a new value and cache it. */
+        LongStateValue newValue = new LongStateValue(longValue);
+        longCache[offset] = newValue;
+        return newValue;
     }
 
     /**
@@ -79,7 +112,22 @@
      * @return The newly-created TmfStateValue object
      */
     public static TmfStateValue newValueDouble(double value) {
-        return new DoubleStateValue(value);
+        /* Lookup in cache for the existence of the same value. */
+        int offset = (int) Double.doubleToLongBits(value) & (DOUBLE_CACHE_SIZE - 1);
+        DoubleStateValue cached = doubleCache[offset];
+
+        /*
+         * We're using Double.compare() instead of .equals(), because .compare()
+         * works when both values are Double.NaN.
+         */
+        if (cached != null && Double.compare(cached.unboxDouble(), value) == 0) {
+            return cached;
+        }
+
+        /* Not in cache, create a new value and cache it. */
+        DoubleStateValue newValue = new DoubleStateValue(value);
+        doubleCache[offset] = newValue;
+        return newValue;
     }
 
     /**