changeset 33:2302c92fc1d9

fraggers.* even better LDS targeting.
author shade
date Fri, 02 Dec 2016 14:03:29 +0100
parents 7b87f91c5db9
children 73d0b304ec43
files src/main/java/org/openjdk/gcbench/GCBench.java src/main/java/org/openjdk/gcbench/fragger/ArrayFragger.java src/main/java/org/openjdk/gcbench/fragger/LinkedListFragger.java src/main/java/org/openjdk/gcbench/fragger/TreeFragger.java src/main/java/org/openjdk/gcbench/util/AllocProfileSupport.java
diffstat 5 files changed, 157 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/openjdk/gcbench/GCBench.java	Fri Dec 02 12:43:28 2016 +0100
+++ b/src/main/java/org/openjdk/gcbench/GCBench.java	Fri Dec 02 14:03:29 2016 +0100
@@ -242,13 +242,12 @@
                     Dimensions.lds(4)
             ));
 
-// FIXME: Enable the test back, it runs too slow
-//            tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.fragger.LinkedListFragger.class,
-//                    "fragger.linkedlist", groupDescr + "Retains a LinkedList.",
-//                    true,
-//                    Dimensions.heapSize(4),
-//                    Dimensions.lds(4)
-//            ));
+            tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.fragger.LinkedListFragger.class,
+                    "fragger.linkedlist", groupDescr + "Retains a LinkedList.",
+                    true,
+                    Dimensions.heapSize(4),
+                    Dimensions.lds(4)
+            ));
         }
 
         {
--- a/src/main/java/org/openjdk/gcbench/fragger/ArrayFragger.java	Fri Dec 02 12:43:28 2016 +0100
+++ b/src/main/java/org/openjdk/gcbench/fragger/ArrayFragger.java	Fri Dec 02 14:03:29 2016 +0100
@@ -1,5 +1,6 @@
 package org.openjdk.gcbench.fragger;
 
+import org.openjdk.gcbench.util.AllocProfileSupport;
 import org.openjdk.gcbench.util.ratelimit.MultiTokenBucket;
 import org.openjdk.jmh.annotations.*;
 import org.openjdk.jol.info.GraphLayout;
@@ -16,6 +17,8 @@
 @State(Scope.Benchmark)
 public class ArrayFragger {
 
+    public static final int PAYLOAD_SIZE = 16384;
+
     @Param({"1000"})
     long ldsMB;
 
@@ -28,9 +31,29 @@
 
     MultiTokenBucket bucket;
 
+    Object[] o0;
+    Object[] o16;
+
     private int getSizePerCount() {
-        return 4 + // array element
-                16; // object
+        if (AllocProfileSupport.isAvailable()) {
+            long a1 = AllocProfileSupport.getAllocatedBytes();
+            o0 = new Object[0];
+            long a2 = AllocProfileSupport.getAllocatedBytes();
+            o16 = new Object[16];
+            for (int c = 0; c < 16; c++) {
+                o16[c] = new byte[PAYLOAD_SIZE];
+            }
+            long a3 = AllocProfileSupport.getAllocatedBytes();
+
+            long sizeO0 = a2 - a1;
+            long sizeO16 = a3 - a2;
+
+            return (int) ((sizeO16 - sizeO0) / 16);
+        } else {
+            // best guess
+            return 4 + // array element
+                    16 + PAYLOAD_SIZE; // object
+        }
     }
 
     @Setup
@@ -39,14 +62,14 @@
         count = (int) Math.max(1, ldsMB * 1024 * 1024 / getSizePerCount());
         objects = new Object[count];
         for (int c = 0; c < count; c++) {
-            objects[c] = new Object();
+            objects[c] = new byte[PAYLOAD_SIZE];
         }
     }
 
     @Benchmark
     public void test() {
         bucket.limit();
-        objects[ThreadLocalRandom.current().nextInt(count)] = new Object();
+        objects[ThreadLocalRandom.current().nextInt(count)] = new byte[PAYLOAD_SIZE];
     }
 
 }
--- a/src/main/java/org/openjdk/gcbench/fragger/LinkedListFragger.java	Fri Dec 02 12:43:28 2016 +0100
+++ b/src/main/java/org/openjdk/gcbench/fragger/LinkedListFragger.java	Fri Dec 02 14:03:29 2016 +0100
@@ -1,5 +1,6 @@
 package org.openjdk.gcbench.fragger;
 
+import org.openjdk.gcbench.util.AllocProfileSupport;
 import org.openjdk.gcbench.util.ratelimit.MultiTokenBucket;
 import org.openjdk.jmh.annotations.*;
 
@@ -17,6 +18,8 @@
 @State(Scope.Benchmark)
 public class LinkedListFragger {
 
+    public static final int PAYLOAD_SIZE = 16384;
+
     @Param({"1000"})
     long ldsMB;
 
@@ -29,9 +32,29 @@
 
     int count;
 
+    LinkedList<Object> o0;
+    LinkedList<Object> o16;
+
     private int getSizePerCount() {
-        return 24 + // Node
-               64; // payload
+        if (AllocProfileSupport.isAvailable()) {
+            long a1 = AllocProfileSupport.getAllocatedBytes();
+            o0 = new LinkedList<>();
+            long a2 = AllocProfileSupport.getAllocatedBytes();
+            o16 = new LinkedList<>();
+            for (int c = 0; c < 16; c++) {
+                o16.add(new byte[PAYLOAD_SIZE]);
+            }
+            long a3 = AllocProfileSupport.getAllocatedBytes();
+
+            long sizeO0 = a2 - a1;
+            long sizeO16 = a3 - a2;
+
+            return (int) ((sizeO16 - sizeO0) / 16);
+        } else {
+            // best guess
+            return 24 + // Node
+                    16 + PAYLOAD_SIZE; // payload
+        }
     }
 
     @Setup
@@ -40,14 +63,14 @@
         count = (int)Math.max(1, ldsMB * 1024 * 1024 / getSizePerCount());
         objects = new LinkedList<>();
         for (int c = 0; c < count; c++) {
-            objects.add(new byte[48]);
+            objects.add(new byte[PAYLOAD_SIZE]);
         }
     }
 
     @Benchmark
     public void test() {
         bucket.limit();
-        objects.set(ThreadLocalRandom.current().nextInt(count), new byte[48]);
+        objects.set(ThreadLocalRandom.current().nextInt(count), new byte[PAYLOAD_SIZE]);
     }
 
 }
--- a/src/main/java/org/openjdk/gcbench/fragger/TreeFragger.java	Fri Dec 02 12:43:28 2016 +0100
+++ b/src/main/java/org/openjdk/gcbench/fragger/TreeFragger.java	Fri Dec 02 14:03:29 2016 +0100
@@ -1,5 +1,6 @@
 package org.openjdk.gcbench.fragger;
 
+import org.openjdk.gcbench.util.AllocProfileSupport;
 import org.openjdk.gcbench.util.ratelimit.MultiTokenBucket;
 import org.openjdk.jmh.annotations.*;
 
@@ -15,6 +16,8 @@
 @State(Scope.Benchmark)
 public class TreeFragger {
 
+    public static final int PAYLOAD_SIZE = 16384;
+
     @Param({"1000"})
     long ldsMB;
 
@@ -27,48 +30,61 @@
 
     MultiTokenBucket bucket;
 
+    Object o0, o16;
+
     private int getSizePerCount() {
-        return 24 + // Node
-               16; // payload
+        if (AllocProfileSupport.isAvailable()) {
+            long a1 = AllocProfileSupport.getAllocatedBytes();
+            o0 = create(0);
+            long a2 = AllocProfileSupport.getAllocatedBytes();
+            o16 = create(16);
+            long a3 = AllocProfileSupport.getAllocatedBytes();
+
+            long sizeO0 = a2 - a1;
+            long sizeO16 = a3 - a2;
+
+            return (int) ((sizeO16 - sizeO0) / 16);
+        } else {
+            // best guess
+            return 24 + // Node
+                    16 + PAYLOAD_SIZE; // payload
+        }
     }
 
     @Setup
     public void setup() {
         bucket = new MultiTokenBucket(rate);
         count = (int)Math.max(1, ldsMB * 1024 * 1024 / getSizePerCount());
+        root = create(count);
+    }
 
-        root = new Node(new Object());
+    private Node create(int count) {
+        Node root = new Node(new Object());
 
         for (int addr = 0; addr < count; addr++) {
             Node cur = root;
             for (int m = 31 - Integer.numberOfLeadingZeros(addr); m >= 0; m--) {
                 if ((addr & (1 << m)) != 0) {
                     if (cur.left == null) {
-                        cur.left = new Node(new Object());
+                        cur.left = new Node(new byte[PAYLOAD_SIZE]);
                     }
                     cur = cur.left;
                 } else {
                     if (cur.right == null) {
-                        cur.right = new Node(new Object());
+                        cur.right = new Node(new byte[PAYLOAD_SIZE]);
                     }
                     cur = cur.right;
                 }
             }
         }
-    }
 
-    public static int align(int size, int align) {
-        if ((size % align) == 0) {
-            return size;
-        } else {
-            return ((size / align) + 1) * align;
-        }
+        return root;
     }
 
     @Benchmark
     public void test() {
         bucket.limit();
-        doStore(ThreadLocalRandom.current().nextInt(count), new Object());
+        doStore(ThreadLocalRandom.current().nextInt(count), new byte[PAYLOAD_SIZE]);
     }
 
     private void doStore(int addr, Object obj) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/org/openjdk/gcbench/util/AllocProfileSupport.java	Fri Dec 02 14:03:29 2016 +0100
@@ -0,0 +1,68 @@
+package org.openjdk.gcbench.util;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadMXBean;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * This class encapsulates any platform-specific functionality. It is supposed
+ * to gracefully fail if some functionality is not available. This class
+ * resolves most special classes via Reflection to enable building against a
+ * standard JDK.
+ */
+public class AllocProfileSupport {
+    private static final boolean ALLOC_AVAILABLE;
+    private static ThreadMXBean ALLOC_MX_BEAN;
+    private static Method ALLOC_MX_BEAN_GETTER;
+
+    static {
+        ALLOC_AVAILABLE = tryInitAlloc();
+    }
+
+    private static boolean tryInitAlloc() {
+        try {
+            Class<?> internalIntf = Class.forName("com.sun.management.ThreadMXBean");
+            ThreadMXBean bean = ManagementFactory.getThreadMXBean();
+            if (!internalIntf.isAssignableFrom(bean.getClass())) {
+                Class<?> pmo = Class.forName("java.lang.management.PlatformManagedObject");
+                Method m = ManagementFactory.class.getMethod("getPlatformMXBean", Class.class, pmo);
+                bean = (ThreadMXBean) m.invoke(null, internalIntf);
+                if (bean == null) {
+                    throw new UnsupportedOperationException("No way to access private ThreadMXBean");
+                }
+            }
+
+            ALLOC_MX_BEAN = bean;
+            ALLOC_MX_BEAN_GETTER = internalIntf.getMethod("getThreadAllocatedBytes", long[].class);
+            getAllocatedBytes(bean.getAllThreadIds());
+
+            return true;
+        } catch (Throwable e) {
+            System.out.println("WARNING: Allocation profiling is not available: " + e.getMessage());
+        }
+        return false;
+    }
+
+    public static long getAllocatedBytes() {
+        long[] threadIds = {Thread.currentThread().getId()};
+        return getAllocatedBytes(threadIds)[0];
+    }
+
+    private static long[] getAllocatedBytes(long[] threadIds) {
+        if (ALLOC_AVAILABLE) {
+            try {
+                return (long[]) ALLOC_MX_BEAN_GETTER.invoke(ALLOC_MX_BEAN, (Object) threadIds);
+            } catch (InvocationTargetException | IllegalAccessException e) {
+                throw new IllegalStateException(e);
+            }
+        } else {
+            return new long[threadIds.length];
+        }
+    }
+
+    public static boolean isAvailable() {
+        return ALLOC_AVAILABLE;
+    }
+
+}