changeset 373:325e0f2bcdef

Remove multiple implementations of Counter, avoid interface calls.
author shade
date Wed, 01 Mar 2017 12:39:57 +0100
parents 802120309836
children 7658cbdf76a4
files jcstress-core/src/main/java/org/openjdk/jcstress/infra/processors/JCStressTestProcessor.java jcstress-core/src/main/java/org/openjdk/jcstress/util/Counter.java jcstress-core/src/main/java/org/openjdk/jcstress/util/HashCounter.java jcstress-core/src/main/java/org/openjdk/jcstress/util/OpenAddressHashCounter.java jcstress-core/src/test/java/org/openjdk/jcstress/util/CounterTest.java jcstress-core/src/test/java/org/openjdk/jcstress/util/OpenAddressHashCounterTest.java
diffstat 6 files changed, 209 insertions(+), 351 deletions(-) [+]
line wrap: on
line diff
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/infra/processors/JCStressTestProcessor.java	Tue Feb 28 21:45:06 2017 +0100
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/infra/processors/JCStressTestProcessor.java	Wed Mar 01 12:39:57 2017 +0100
@@ -325,7 +325,7 @@
         pw.println();
 
         for (ExecutableElement a : info.getActors()) {
-            pw.println("    OpenAddressHashCounter<" + r + "> counter_" + a.getSimpleName() + ";");
+            pw.println("    Counter<" + r + "> counter_" + a.getSimpleName() + ";");
         }
 
         if (!isStateItself) {
@@ -402,7 +402,7 @@
         pw.println("        version = new StateHolder<>(new Pair[0], " + actorsCount + ", config.spinLoopStyle);");
 
         for (ExecutableElement a : info.getActors()) {
-            pw.println("        counter_" + a.getSimpleName() + " = new OpenAddressHashCounter<>();");
+            pw.println("        counter_" + a.getSimpleName() + " = new Counter<>();");
         }
 
 
@@ -424,7 +424,7 @@
         pw.println();
         pw.println("        waitFor(tasks);");
         pw.println();
-        pw.println("        Counter<" + r + "> counter = new OpenAddressHashCounter<>();");
+        pw.println("        Counter<" + r + "> counter = new Counter<>();");
         for (ExecutableElement a : info.getActors()) {
             pw.println("        counter.merge(counter_" + a.getSimpleName() + ");");
         }
@@ -432,7 +432,7 @@
         pw.println("    }");
         pw.println();
 
-        pw.println("    public final void jcstress_consume(StateHolder<Pair> holder, OpenAddressHashCounter<" + r + "> cnt, int a, int actors) {");
+        pw.println("    public final void jcstress_consume(StateHolder<Pair> holder, Counter<" + r + "> cnt, int a, int actors) {");
         pw.println("        Pair[] pairs = holder.pairs;");
         pw.println("        int len = pairs.length;");
         pw.println("        int left = a * len / actors;");
@@ -507,7 +507,7 @@
                 pw.println("        " + t + " lt = test;");
             }
 
-            pw.println("        OpenAddressHashCounter<" + r + "> counter = counter_" + a.getSimpleName() + ";");
+            pw.println("        Counter<" + r + "> counter = counter_" + a.getSimpleName() + ";");
 
             pw.println("        while (true) {");
             pw.println("            StateHolder<Pair> holder = version;");
@@ -716,7 +716,7 @@
 
         pw.println("    @Override");
         pw.println("    public void run() {");
-        pw.println("        Counter<Outcome> results = new OpenAddressHashCounter<>();");
+        pw.println("        Counter<Outcome> results = new Counter<>();");
         pw.println();
         pw.println("        for (int c = 0; c < config.iters; c++) {");
         pw.println("            try {");
@@ -897,7 +897,7 @@
                 ExecutorService.class, Future.class, TimeUnit.class,
                 TestConfig.class, TestResultCollector.class,
                 Runner.class, StateHolder.class, Counter.class,
-                WhiteBoxSupport.class, OpenAddressHashCounter.class, ExecutionException.class
+                WhiteBoxSupport.class, ExecutionException.class
         };
 
         for (Class<?> c : imports) {
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/util/Counter.java	Tue Feb 28 21:45:06 2017 +0100
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/util/Counter.java	Wed Mar 01 12:39:57 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,7 +24,15 @@
  */
 package org.openjdk.jcstress.util;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
 /**
  * Computes the histogram on arbitrary results.
@@ -32,7 +40,40 @@
  *
  * @param <R> result type.
  */
-public interface Counter<R> {
+public final class Counter<R> implements Serializable {
+
+    /*
+     * Implementation notes: the requirements for Counter are very relaxed,
+     * and therefore this implementation cuts around the edges heavily:
+     *   - we do not allow nulls!
+     *   - we do not support removals, therefore cleanup strategy is not
+     *     required to implement;
+     *   - values are always longs, saving us additional dereferences and
+     *     boxing/unboxing;
+     *   - resizes are infrequent, since most of the time the case count is
+     *     minuscule;
+     */
+
+    private static final int RECIPROCAL_LOAD_FACTOR = 10;
+    private static final int INITIAL_CAPACITY = 64;
+
+    private Object[] keys;
+    private long[] counts;
+    private int length;
+    private int keyCount;
+
+    public Counter() {
+        this(INITIAL_CAPACITY);
+    }
+
+    public Counter(int len) {
+        length = len;
+
+        @SuppressWarnings("unchecked")
+        R[] table = (R[]) new Object[len];
+        this.keys = table;
+        this.counts = new long[len];
+    }
 
     /**
      * Records the result.
@@ -40,7 +81,9 @@
      *
      * @param result result to record
      */
-    void record(R result);
+    public final void record(R result) {
+        record(result, 1);
+    }
 
     /**
      * Records the result with given occurrences count.
@@ -49,25 +92,124 @@
      * @param result result to record
      * @param count number of occurences to record
      */
-    void record(R result, long count);
+    public final void record(R result, long count) {
+        int idx = result.hashCode() & (length - 1);
+
+        Object k = keys[idx];
+        while (k != null) {
+            // hit the bucket, update and exit
+            if (k.equals(result)) {
+                counts[idx] += count;
+                return;
+            }
+
+            // trying the next bucket
+            idx = (idx + 1) & (length - 1);
+            k = keys[idx];
+        }
+
+        // whoops, map is overloaded, resize to make up
+        // the space, try again (succeeding), and exit;
+        // we might want to resize early
+        if (keyCount * RECIPROCAL_LOAD_FACTOR > length) {
+            resize();
+            record(result, count);
+            return;
+        }
+
+        // completely new key, insert, and exit
+        keyCount++;
+        keys[idx] = decouple(result);
+        counts[idx] = count;
+    }
 
     /**
      * Merge another counter data into this counter
      * @param other counter
      */
-    void merge(Counter<R> other);
+    public final void merge(Counter<R> other) {
+        for (R key : other.elementSet()) {
+            record(key, other.count(key));
+        }
+    }
+
+    private void resize() {
+        Object[] prevKeys = keys;
+        long[] prevCounts = counts;
+
+        // double so:
+        int newLen = (length << 1);
+
+        // allocate new stuff
+        @SuppressWarnings("unchecked")
+        R[] table = (R[]) new Object[newLen];
+        keys = table;
+        counts = new long[newLen];
+        length = newLen;
+
+        // rehash the entire map
+        for (int kIdx = 0; kIdx < prevKeys.length; kIdx++) {
+            Object k = prevKeys[kIdx];
+            if (k != null) {
+                int idx = k.hashCode() & (length - 1);
+                while (keys[idx] != null) {
+                    idx = (idx + 1) & (length - 1);
+                }
+                keys[idx] = k;
+                counts[idx] = prevCounts[kIdx];
+            }
+        }
+    }
 
     /**
      * Return the result count.
      *
      * @param result result to count
      */
-    long count(R result);
+    public final long count(R result) {
+        int idx = result.hashCode() & ((length - 1));
+        while (keys[idx] != null) {
+            if (keys[idx].equals(result)) {
+                return counts[idx];
+            }
+            idx = (idx + 1) & (length - 1);
+        }
+        return 0L;
+    }
+
+    private static <T> T decouple(T result) {
+        try {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(bos);
+            oos.writeObject(result);
+            oos.close();
+
+            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+            ObjectInputStream ois = new ObjectInputStream(bis);
+
+            @SuppressWarnings("unchecked")
+            T t = (T)ois.readObject();
+
+            return t;
+        } catch (IOException | ClassNotFoundException e) {
+            throw new IllegalStateException(e);
+        }
+    }
 
     /**
      * Return the collection of accumulated unique results.
      * @return set
      */
-    Collection<R> elementSet();
+    public final Collection<R> elementSet() {
+        List<R> res = new ArrayList<>();
+        for (Object k : keys) {
+            if (k != null) {
+                @SuppressWarnings("unchecked")
+                R e = (R) k;
+                res.add(e);
+            }
+        }
+        return res;
+    }
 
 }
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/util/HashCounter.java	Tue Feb 28 21:45:06 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-/*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.openjdk.jcstress.util;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-public class HashCounter<T> implements Counter<T> {
-
-    private Map<T, Holder> ms = new HashMap<>();
-
-    @Override
-    public void record(T result) {
-        record(result, 1);
-    }
-
-    @Override
-    public void record(T result, long count) {
-        Holder holder = ms.get(result);
-        if (holder == null) {
-            holder = new Holder();
-            ms.put(decouple(result), holder);
-        }
-        holder.value += count;
-    }
-
-    @Override
-    public void merge(Counter<T> other) {
-        for (T key : other.elementSet()) {
-            record(key, other.count(key));
-        }
-    }
-
-    private T decouple(T result) {
-        try {
-            ByteArrayOutputStream bos = new ByteArrayOutputStream();
-            ObjectOutputStream oos = new ObjectOutputStream(bos);
-            oos.writeObject(result);
-            oos.close();
-
-            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
-            ObjectInputStream ois = new ObjectInputStream(bis);
-
-            @SuppressWarnings("unchecked")
-            T t = (T)ois.readObject();
-
-            return t;
-        } catch (IOException | ClassNotFoundException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public long count(T result) {
-        Holder holder = ms.get(result);
-        return holder == null ? 0 : holder.value;
-    }
-
-    @Override
-    public Set<T> elementSet() {
-        return ms.keySet();
-    }
-
-    private static class Holder {
-        public long value;
-    }
-
-}
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/util/OpenAddressHashCounter.java	Tue Feb 28 21:45:06 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-/*
- * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.openjdk.jcstress.util;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-public class OpenAddressHashCounter<R> implements Counter<R>, Serializable {
-
-    /*
-     * Implementation notes: the requirements for Counter are very relaxed,
-     * and therefore this implementation cuts around the edges heavily:
-     *   - we do not allow nulls!
-     *   - we do not support removals, therefore cleanup strategy is not
-     *     required to implement;
-     *   - values are always longs, saving us additional dereferences and
-     *     boxing/unboxing;
-     *   - resizes are infrequent, since most of the time the case count is
-     *     minuscule;
-     */
-
-    private static final int RECIPROCAL_LOAD_FACTOR = 10;
-    private static final int INITIAL_CAPACITY = 64;
-
-    private Object[] keys;
-    private long[] counts;
-    private int length;
-    private int keyCount;
-
-    public OpenAddressHashCounter() {
-        this(INITIAL_CAPACITY);
-    }
-
-    public OpenAddressHashCounter(int len) {
-        length = len;
-
-        @SuppressWarnings("unchecked")
-        R[] table = (R[]) new Object[len];
-        this.keys = table;
-        this.counts = new long[len];
-    }
-
-    @Override
-    public void record(R result) {
-        record(result, 1);
-    }
-
-    @Override
-    public void record(R result, long count) {
-        int idx = result.hashCode() & (length - 1);
-
-        Object k = keys[idx];
-        while (k != null) {
-            // hit the bucket, update and exit
-            if (k.equals(result)) {
-                counts[idx] += count;
-                return;
-            }
-
-            // trying the next bucket
-            idx = (idx + 1) & (length - 1);
-            k = keys[idx];
-        }
-
-        // whoops, map is overloaded, resize to make up
-        // the space, try again (succeeding), and exit;
-        // we might want to resize early
-        if (keyCount * RECIPROCAL_LOAD_FACTOR > length) {
-            resize();
-            record(result, count);
-            return;
-        }
-
-        // completely new key, insert, and exit
-        keyCount++;
-        keys[idx] = decouple(result);
-        counts[idx] = count;
-    }
-
-    @Override
-    public void merge(Counter<R> other) {
-        for (R key : other.elementSet()) {
-            record(key, other.count(key));
-        }
-    }
-
-    private void resize() {
-        Object[] prevKeys = keys;
-        long[] prevCounts = counts;
-
-        // double so:
-        int newLen = (length << 1);
-
-        // allocate new stuff
-        @SuppressWarnings("unchecked")
-        R[] table = (R[]) new Object[newLen];
-        keys = table;
-        counts = new long[newLen];
-        length = newLen;
-
-        // rehash the entire map
-        for (int kIdx = 0; kIdx < prevKeys.length; kIdx++) {
-            Object k = prevKeys[kIdx];
-            if (k != null) {
-                int idx = k.hashCode() & (length - 1);
-                while (keys[idx] != null) {
-                    idx = (idx + 1) & (length - 1);
-                }
-                keys[idx] = k;
-                counts[idx] = prevCounts[kIdx];
-            }
-        }
-    }
-
-    @Override
-    public long count(R result) {
-        int idx = result.hashCode() & ((length - 1));
-        while (keys[idx] != null) {
-            if (keys[idx].equals(result)) {
-                return counts[idx];
-            }
-            idx = (idx + 1) & (length - 1);
-        }
-        return 0L;
-    }
-
-    private static <T> T decouple(T result) {
-        try {
-            ByteArrayOutputStream bos = new ByteArrayOutputStream();
-            ObjectOutputStream oos = new ObjectOutputStream(bos);
-            oos.writeObject(result);
-            oos.close();
-
-            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
-            ObjectInputStream ois = new ObjectInputStream(bis);
-
-            @SuppressWarnings("unchecked")
-            T t = (T)ois.readObject();
-
-            return t;
-        } catch (IOException | ClassNotFoundException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public Collection<R> elementSet() {
-        List<R> res = new ArrayList<>();
-        for (Object k : keys) {
-            if (k != null) {
-                @SuppressWarnings("unchecked")
-                R e = (R) k;
-                res.add(e);
-            }
-        }
-        return res;
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jcstress-core/src/test/java/org/openjdk/jcstress/util/CounterTest.java	Wed Mar 01 12:39:57 2017 +0100
@@ -0,0 +1,53 @@
+package org.openjdk.jcstress.util;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+public class CounterTest {
+
+    @Test
+    public void test1() {
+        Counter<String> cnt = new Counter<>();
+        cnt.record("Foo");
+
+        Assert.assertEquals(1, cnt.count("Foo"));
+        Assert.assertEquals(1, cnt.elementSet().size());
+        Assert.assertEquals("Foo", cnt.elementSet().iterator().next());
+    }
+
+    @Test
+    public void test2() {
+        Counter<String> cnt = new Counter<>();
+        cnt.record("Foo", 2);
+
+        Assert.assertEquals(2, cnt.count("Foo"));
+        Assert.assertEquals(1, cnt.elementSet().size());
+        Assert.assertEquals("Foo", cnt.elementSet().iterator().next());
+    }
+
+    @Test
+    public void test3() {
+        Counter<String> cnt = new Counter<>();
+        cnt.record("Foo", 1);
+        cnt.record("Bar", 1);
+
+        Assert.assertEquals(1, cnt.count("Foo"));
+        Assert.assertEquals(1, cnt.count("Bar"));
+        Assert.assertEquals(2, cnt.elementSet().size());
+    }
+
+    @Test
+    public void test4() {
+        Counter<String> cnt = new Counter<>();
+        for (int c = 0; c < 1000; c++) {
+            cnt.record("Foo" + c, c);
+        }
+
+        for (int c = 0; c < 1000; c++) {
+            Assert.assertEquals(c, cnt.count("Foo" + c));
+        }
+
+        Assert.assertEquals(1000, cnt.elementSet().size());
+    }
+
+}
--- a/jcstress-core/src/test/java/org/openjdk/jcstress/util/OpenAddressHashCounterTest.java	Tue Feb 28 21:45:06 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package org.openjdk.jcstress.util;
-
-import junit.framework.Assert;
-import org.junit.Test;
-
-public class OpenAddressHashCounterTest {
-
-    @Test
-    public void test1() {
-        Counter<String> cnt = new OpenAddressHashCounter<>();
-        cnt.record("Foo");
-
-        Assert.assertEquals(1, cnt.count("Foo"));
-        Assert.assertEquals(1, cnt.elementSet().size());
-        Assert.assertEquals("Foo", cnt.elementSet().iterator().next());
-    }
-
-    @Test
-    public void test2() {
-        Counter<String> cnt = new OpenAddressHashCounter<>();
-        cnt.record("Foo", 2);
-
-        Assert.assertEquals(2, cnt.count("Foo"));
-        Assert.assertEquals(1, cnt.elementSet().size());
-        Assert.assertEquals("Foo", cnt.elementSet().iterator().next());
-    }
-
-    @Test
-    public void test3() {
-        Counter<String> cnt = new OpenAddressHashCounter<>();
-        cnt.record("Foo", 1);
-        cnt.record("Bar", 1);
-
-        Assert.assertEquals(1, cnt.count("Foo"));
-        Assert.assertEquals(1, cnt.count("Bar"));
-        Assert.assertEquals(2, cnt.elementSet().size());
-    }
-
-    @Test
-    public void test4() {
-        Counter<String> cnt = new OpenAddressHashCounter<>();
-        for (int c = 0; c < 1000; c++) {
-            cnt.record("Foo" + c, c);
-        }
-
-        for (int c = 0; c < 1000; c++) {
-            Assert.assertEquals(c, cnt.count("Foo" + c));
-        }
-
-        Assert.assertEquals(1000, cnt.elementSet().size());
-    }
-
-}