8223078: Add microbenchmark for array copying/clearing/resizing
authormartin
Wed, 01 May 2019 09:06:12 -0700
changeset 54666 1b5f0a3f9c41
parent 54665 f14a826e3c2e
child 54667 f8d2b5ce4491
8223078: Add microbenchmark for array copying/clearing/resizing Reviewed-by: dl, redestad
test/micro/org/openjdk/bench/java/lang/ArrayFiddle.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/micro/org/openjdk/bench/java/lang/ArrayFiddle.java	Wed May 01 09:06:12 2019 -0700
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2019 Google Inc.  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.
+ *
+ * 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.bench.java.lang;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+
+import java.util.Arrays;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Explores the cost of copying the contents of one array to another, as is
+ * commonly seen in collection classes that need to resize their backing
+ * array, like ArrayList.
+ *
+ * We have multiple variations on copying, and we explore the cost of
+ * clearing the old array, which might help for generational GCs.
+ *
+ * Benchmarks the operations in the ancient
+ * JDK-6428387: array clone() much slower than Arrays.copyOf
+ *
+ * 2019 results on x86:
+ *
+ * The "simple" benchmarks below have the same performance, except that
+ * simple_copyLoop is surprisingly 5x slower.  The array copying intrinsics
+ * are very effective and a naive loop does not get optimized the same way.
+ * OTOH there is no intrinsic for Arrays.fill but the naive array zeroing loop
+ * *does* get optimized to something a little faster than than arraycopy.
+ *
+ * System.arraycopy and Arrays.fill have such outstanding performance that
+ * one should use them to replace handwritten loops whenever possible.
+ *
+ * This benchmark is great for measuring cache effects, e.g. size=10^6 has 5x
+ * the per-element cost of size=10^3 (See "The Myth of RAM".)
+ *
+ * (cd $(hg root) && for size in 3 16 999 999999; do make test TEST="micro:java.lang.ArrayFiddle" MICRO="FORK=2;WARMUP_ITER=4;ITER=4;OPTIONS=-opi $size -p size=$size" |& perl -ne 'print if /^Benchmark/ .. /^Finished running test/'; done)
+ */
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Benchmark)
+public class ArrayFiddle {
+    @Param("999")
+    public int size;
+
+    public int largerSize;
+    public Object[] data;
+    public Object[] copy;
+
+    @Setup
+    public void setup() {
+        largerSize = size + (size >> 1);
+
+        data = new Object[size];
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        for (int i = data.length; i--> 0; )
+            data[i] = rnd.nextInt(256);
+
+        copy = data.clone();
+    }
+
+    // --- "simple" benchmarks just make an array clone
+
+    @Benchmark
+    public Object[] simple_clone() {
+        return data.clone();
+    }
+
+    @Benchmark
+    public Object[] simple_copyOf() {
+        return Arrays.copyOf(data, data.length);
+    }
+
+    @Benchmark
+    public Object[] simple_arraycopy() {
+        Object[] out = new Object[data.length];
+        System.arraycopy(data, 0, out, 0, data.length);
+        return out;
+    }
+
+    @Benchmark
+    public Object[] simple_copyLoop() {
+        final Object[] data = this.data;
+        int len = data.length;
+        Object[] out = new Object[len];
+        for (int i = 0; i < len; i++)
+            out[i] = data[i];
+        return out;
+    }
+
+    // --- "grow" benchmarks have an output array that is larger
+
+    private Object[] input_array() {
+        System.arraycopy(data, 0, copy, 0, size);
+        return copy;
+    }
+
+    @Benchmark
+    public Object[] grow_copyLoop() {
+        Object[] in = input_array();
+        Object[] out = new Object[largerSize];
+        for (int i = 0, len = in.length; i < len; i++)
+            out[i] = in[i];
+        return out;
+    }
+
+    @Benchmark
+    public Object[] grow_copyZeroLoop() {
+        Object[] in = input_array();
+        Object[] out = new Object[largerSize];
+        for (int i = 0, len = in.length; i < len; i++) {
+            out[i] = in[i];
+            in[i] = null;
+        }
+        return out;
+    }
+
+    @Benchmark
+    public Object[] grow_arraycopy() {
+        Object[] in = input_array();
+        Object[] out = new Object[largerSize];
+        System.arraycopy(in, 0, out, 0, size);
+        return out;
+    }
+
+    @Benchmark
+    public Object[] grow_arraycopy_fill() {
+        Object[] in = input_array();
+        Object[] out = new Object[largerSize];
+        System.arraycopy(in, 0, out, 0, size);
+        Arrays.fill(in, null);
+        return out;
+    }
+
+    @Benchmark
+    public Object[] grow_arraycopy_zeroLoop() {
+        Object[] in = input_array();
+        Object[] out = new Object[largerSize];
+        System.arraycopy(in, 0, out, 0, size);
+        for (int i = 0, len = in.length; i < len; i++)
+            in[i] = null;
+        return out;
+    }
+
+    @Benchmark
+    public Object[] grow_copyOf() {
+        Object[] in = input_array();
+        Object[] out = Arrays.copyOf(in, largerSize);
+        return out;
+    }
+
+    @Benchmark
+    public Object[] grow_copyOf_fill() {
+        Object[] in = input_array();
+        Object[] out = Arrays.copyOf(in, largerSize);
+        Arrays.fill(in, null);
+        return out;
+    }
+
+    @Benchmark
+    public Object[] grow_copyOf_zeroLoop() {
+        Object[] in = input_array();
+        Object[] out = Arrays.copyOf(in, largerSize);
+        for (int i = 0, len = in.length; i < len; i++)
+            in[i] = null;
+        return out;
+    }
+
+}