summaryrefslogtreecommitdiff
path: root/test/2239-varhandle-perf/util-src/generate_java.py
diff options
context:
space:
mode:
author Orion Hodson <oth@google.com> 2022-03-07 12:40:27 +0000
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2022-03-07 12:40:27 +0000
commitde49d24993aaa5628ec04df989149be43ee85c1a (patch)
treea2988ad54fcf25db90e3191e76c58d9e20514ad4 /test/2239-varhandle-perf/util-src/generate_java.py
parent3d004a52434a7dbbc73bb477b7475cad27af6e94 (diff)
parent964f5c8b27941570df265da753b69c0a4947a493 (diff)
Add VarHandle benchmarks in the form of a test. am: 964f5c8b27
Original change: https://android-review.googlesource.com/c/platform/art/+/1420959 Change-Id: Ie04b239e60ee4ca980b2058f4dbf40a89dcaf541
Diffstat (limited to 'test/2239-varhandle-perf/util-src/generate_java.py')
-rw-r--r--test/2239-varhandle-perf/util-src/generate_java.py504
1 files changed, 504 insertions, 0 deletions
diff --git a/test/2239-varhandle-perf/util-src/generate_java.py b/test/2239-varhandle-perf/util-src/generate_java.py
new file mode 100644
index 0000000000..4c58c32501
--- /dev/null
+++ b/test/2239-varhandle-perf/util-src/generate_java.py
@@ -0,0 +1,504 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Generate java benchmarks for 2238-varhandle-perf
+"""
+# TODO: fix constants when converting the test to a Golem benchmark
+
+
+from enum import Enum
+from pathlib import Path
+
+import io
+import sys
+
+
+class MemLoc(Enum):
+ FIELD = 0
+ ARRAY = 1
+ BYTE_ARRAY_VIEW = 2
+
+
+def to_camel_case(word):
+ return ''.join(c for c in word.title() if not c == '_')
+
+
+class Benchmark:
+ def __init__(self, code, static, vartype, flavour, klass, method, memloc,
+ byteorder="LITTLE_ENDIAN"):
+ self.code = code
+ self.static = static
+ self.vartype = vartype
+ self.flavour = flavour
+ self.klass = klass
+ self.method = method
+ self.byteorder = byteorder
+ self.memloc = memloc
+
+ def fullname(self):
+ return "{klass}{method}{flavour}{static_name}{memloc}{byteorder}{vartype}Benchmark".format(
+ klass = self.klass,
+ method = to_camel_case(self.method),
+ flavour = self.flavour,
+ static_name = "Static" if self.static else "",
+ memloc = to_camel_case(self.memloc.name),
+ byteorder = to_camel_case(self.byteorder),
+ vartype = to_camel_case(self.vartype))
+
+ def gencode(self):
+ if self.klass == "Reflect":
+ method_suffix = "" if self.vartype == "String" else self.vartype.title()
+ static_first_arg = "null"
+ elif self.klass == "Unsafe":
+ method_suffix = "Object" if self.vartype == "String" else self.vartype.title()
+ static_first_arg = "this.getClass()"
+ else:
+ method_suffix = ""
+ static_first_arg = ""
+
+ first_arg = static_first_arg if self.static else "this"
+
+ return self.code.format(
+ name = self.fullname(),
+ method = self.method + method_suffix,
+ flavour = self.flavour,
+ static_name = "Static" if self.static else "",
+ static_kwd = "static " if self.static else "",
+ this = first_arg,
+ this_comma = "" if not first_arg else first_arg + ", ",
+ vartype = self.vartype,
+ byteorder = self.byteorder,
+ value1 = VALUES[self.vartype][0],
+ value2 = VALUES[self.vartype][1],
+ value1_byte_array = VALUES["byte[]"][self.byteorder][0],
+ value2_byte_array = VALUES["byte[]"][self.byteorder][1],
+ loop = "for (int pass = 0; pass < 100; ++pass)",
+ iters = ITERATIONS)
+
+
+def BenchVHField(code, static, vartype, flavour, method):
+ return Benchmark(code, static, vartype, flavour, "VarHandle", method, MemLoc.FIELD)
+
+
+def BenchVHArray(code, vartype, flavour, method):
+ return Benchmark(code, False, vartype, flavour, "VarHandle", method, MemLoc.ARRAY)
+
+
+def BenchVHByteArrayView(code, byteorder, vartype, flavour, method):
+ return Benchmark(code, False, vartype, flavour, "VarHandle", method, MemLoc.BYTE_ARRAY_VIEW, byteorder)
+
+
+def BenchReflect(code, static, vartype, method):
+ return Benchmark(code, static, vartype, "", "Reflect", method, MemLoc.FIELD)
+
+
+def BenchUnsafe(code, static, vartype, method):
+ return Benchmark(code, static, vartype, "", "Unsafe", method, MemLoc.FIELD)
+
+
+VALUES = {
+ "int": ["42", "~42"],
+ "float": ["3.14f", "2.17f"],
+ "String": ["\"qwerty\"", "null"],
+ "byte[]": {
+ "LITTLE_ENDIAN": [
+ "{ (byte) VALUE, (byte) (VALUE >> 8), (byte) (VALUE >> 16), (byte) (VALUE >> 24) }",
+ "{ (byte) VALUE, (byte) (-1 >> 8), (byte) (-1 >> 16), (byte) (-1 >> 24) }",
+ ],
+ "BIG_ENDIAN": [
+ "{ (byte) (VALUE >> 24), (byte) (VALUE >> 16), (byte) (VALUE >> 8), (byte) VALUE }",
+ "{ (byte) (-1 >> 24), (byte) (-1 >> 16), (byte) (-1 >> 8), (byte) VALUE }",
+ ],
+ },
+}
+
+
+# TODO: fix these numbers when converting the test to a Golem benchmark
+ITERATIONS = 1 # 3000 for a real benchmark
+REPEAT = 2 # 30 for a real benchmark
+REPEAT_HALF = (int) (REPEAT / 2)
+
+
+BANNER = '// This file is generated by util-src/generate_java.py do not directly modify!'
+
+
+VH_IMPORTS = """
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+"""
+
+
+VH_START = BANNER + VH_IMPORTS + """
+class {name} extends MicroBenchmark {{
+ static final {vartype} FIELD_VALUE = {value1};
+ {static_kwd}{vartype} field = FIELD_VALUE;
+ VarHandle vh;
+
+ {name}() throws Throwable {{
+ vh = MethodHandles.lookup().find{static_name}VarHandle(this.getClass(), "field", {vartype}.class);
+ }}
+"""
+
+
+END = """
+ }}
+ }}
+
+ @Override
+ public int innerIterations() {{
+ return {iters};
+ }}
+}}"""
+
+
+VH_GET = VH_START + """
+ @Override
+ public void setup() {{
+ {vartype} v = ({vartype}) vh.{method}{flavour}({this});
+ if (v != FIELD_VALUE) {{
+ throw new RuntimeException("field has unexpected value " + v);
+ }}
+ }}
+
+ @Override
+ public void run() {{
+ {vartype} x;
+ {loop} {{""" + """
+ x = ({vartype}) vh.{method}{flavour}({this});""" * REPEAT + END
+
+
+VH_SET = VH_START + """
+ @Override
+ public void teardown() {{
+ if (field != FIELD_VALUE) {{
+ throw new RuntimeException("field has unexpected value " + field);
+ }}
+ }}
+
+ @Override
+ public void run() {{
+ {vartype} x;
+ {loop} {{""" + """
+ vh.{method}{flavour}({this_comma}FIELD_VALUE);""" * REPEAT + END
+
+
+VH_CAS = VH_START + """
+ @Override
+ public void run() {{
+ boolean success;
+ {loop} {{""" + """
+ success = vh.{method}{flavour}({this_comma}field, {value2});
+ success = vh.{method}{flavour}({this_comma}field, {value1});""" * REPEAT_HALF + END
+
+
+VH_CAE = VH_START + """
+ @Override
+ public void run() {{
+ {vartype} x;
+ {loop} {{""" + """
+ x = ({vartype}) vh.{method}{flavour}({this_comma}field, {value2});
+ x = ({vartype}) vh.{method}{flavour}({this_comma}field, {value1});""" * REPEAT_HALF + END
+
+
+VH_GAS = VH_START + """
+ @Override
+ public void run() {{
+ {vartype} x;
+ {loop} {{""" + """
+ x = ({vartype}) vh.{method}{flavour}({this_comma}{value2});""" * REPEAT + END
+
+
+VH_GAA = VH_START + """
+ @Override
+ public void run() {{
+ {vartype} x;
+ {loop} {{""" + """
+ x = ({vartype}) vh.{method}{flavour}({this_comma}{value2});""" * REPEAT + END
+
+
+VH_GAB = VH_START + """
+ @Override
+ public void run() {{
+ int x;
+ {loop} {{""" + """
+ x = ({vartype}) vh.{method}{flavour}({this_comma}{value2});""" * REPEAT + END
+
+
+VH_START_ARRAY = BANNER + VH_IMPORTS + """
+class {name} extends MicroBenchmark {{
+ static final {vartype} ELEMENT_VALUE = {value1};
+ {vartype}[] array = {{ ELEMENT_VALUE }};
+ VarHandle vh;
+
+ {name}() throws Throwable {{
+ vh = MethodHandles.arrayElementVarHandle({vartype}[].class);
+ }}
+"""
+
+
+VH_GET_A = VH_START_ARRAY + """
+ @Override
+ public void setup() {{
+ {vartype} v = ({vartype}) vh.{method}{flavour}(array, 0);
+ if (v != ELEMENT_VALUE) {{
+ throw new RuntimeException("array element has unexpected value: " + v);
+ }}
+ }}
+
+ @Override
+ public void run() {{
+ {vartype}[] a = array;
+ {vartype} x;
+ {loop} {{""" + """
+ x = ({vartype}) vh.{method}{flavour}(a, 0);""" * REPEAT + END
+
+
+VH_SET_A = VH_START_ARRAY + """
+ @Override
+ public void teardown() {{
+ if (array[0] != {value2}) {{
+ throw new RuntimeException("array element has unexpected value: " + array[0]);
+ }}
+ }}
+
+ @Override
+ public void run() {{
+ {vartype}[] a = array;
+ {vartype} x;
+ {loop} {{""" + """
+ vh.{method}{flavour}(a, 0, {value2});""" * REPEAT + END
+
+
+VH_START_BYTE_ARRAY_VIEW = BANNER + VH_IMPORTS + """
+import java.util.Arrays;
+import java.nio.ByteOrder;
+
+class {name} extends MicroBenchmark {{
+ static final {vartype} VALUE = {value1};
+ byte[] array1 = {value1_byte_array};
+ byte[] array2 = {value2_byte_array};
+ VarHandle vh;
+
+ {name}() throws Throwable {{
+ vh = MethodHandles.byteArrayViewVarHandle({vartype}[].class, ByteOrder.{byteorder});
+ }}
+"""
+
+
+VH_GET_BAV = VH_START_BYTE_ARRAY_VIEW + """
+ @Override
+ public void setup() {{
+ {vartype} v = ({vartype}) vh.{method}{flavour}(array1, 0);
+ if (v != VALUE) {{
+ throw new RuntimeException("array has unexpected value: " + v);
+ }}
+ }}
+
+ @Override
+ public void run() {{
+ byte[] a = array1;
+ {vartype} x;
+ {loop} {{""" + """
+ x = ({vartype}) vh.{method}{flavour}(a, 0);""" * REPEAT + END
+
+
+VH_SET_BAV = VH_START_BYTE_ARRAY_VIEW + """
+ @Override
+ public void teardown() {{
+ if (!Arrays.equals(array2, array1)) {{
+ throw new RuntimeException("array has unexpected values: " +
+ array2[0] + " " + array2[1] + " " + array2[2] + " " + array2[3]);
+ }}
+ }}
+
+ @Override
+ public void run() {{
+ byte[] a = array2;
+ {loop} {{""" + """
+ vh.{method}{flavour}(a, 0, VALUE);""" * REPEAT + END
+
+
+REFLECT_START = BANNER + """
+import java.lang.reflect.Field;
+
+class {name} extends MicroBenchmark {{
+ Field field;
+ {static_kwd}{vartype} value;
+
+ {name}() throws Throwable {{
+ field = this.getClass().getDeclaredField("value");
+ }}
+"""
+
+
+REFLECT_GET = REFLECT_START + """
+ @Override
+ public void run() throws Throwable {{
+ {vartype} x;
+ {loop} {{""" + """
+ x = ({vartype}) field.{method}({this});""" * REPEAT + END
+
+
+REFLECT_SET = REFLECT_START + """
+ @Override
+ public void run() throws Throwable {{
+ {loop} {{""" + """
+ field.{method}({this_comma}{value1});""" * REPEAT + END
+
+
+UNSAFE_START = BANNER + """
+import java.lang.reflect.Field;
+import jdk.internal.misc.Unsafe;
+
+class {name} extends UnsafeMicroBenchmark {{
+ long offset;
+ {static_kwd}{vartype} value = {value1};
+
+ {name}() throws Throwable {{
+ Field field = this.getClass().getDeclaredField("value");
+ offset = get{static_name}FieldOffset(field);
+ }}
+"""
+
+
+UNSAFE_GET = UNSAFE_START + """
+ @Override
+ public void run() throws Throwable {{
+ {vartype} x;
+ {loop} {{""" + """
+ x = ({vartype}) theUnsafe.{method}({this_comma}offset);""" * REPEAT + END
+
+
+UNSAFE_PUT = UNSAFE_START + """
+ @Override
+ public void run() throws Throwable {{
+ {loop} {{""" + """
+ theUnsafe.{method}({this_comma}offset, {value1});""" * REPEAT + END
+
+
+UNSAFE_CAS = UNSAFE_START + """
+ @Override
+ public void run() throws Throwable {{
+ {loop} {{""" + """
+ theUnsafe.{method}({this_comma}offset, {value1}, {value2});
+ theUnsafe.{method}({this_comma}offset, {value2}, {value1});""" * REPEAT_HALF + END
+
+
+ALL_BENCHMARKS = (
+ [BenchVHField(VH_GET, static, vartype, flavour, "get")
+ for flavour in ["", "Acquire", "Opaque", "Volatile"]
+ for static in [True, False]
+ for vartype in ["int", "String"]] +
+ [BenchVHField(VH_SET, static, vartype, flavour, "set")
+ for flavour in ["", "Volatile", "Opaque", "Release"]
+ for static in [True, False]
+ for vartype in ["int", "String"]] +
+ [BenchVHField(VH_CAS, static, vartype, flavour, "compareAndSet")
+ for flavour in [""]
+ for static in [True, False]
+ for vartype in ["int", "String"]] +
+ [BenchVHField(VH_CAS, static, vartype, flavour, "weakCompareAndSet")
+ for flavour in ["", "Plain", "Acquire", "Release"]
+ for static in [True, False]
+ for vartype in ["int", "String"]] +
+ [BenchVHField(VH_CAE, static, vartype, flavour, "compareAndExchange")
+ for flavour in ["", "Acquire", "Release"]
+ for static in [True, False]
+ for vartype in ["int", "String"]] +
+ [BenchVHField(VH_GAS, static, vartype, flavour, "getAndSet")
+ for flavour in ["", "Acquire", "Release"]
+ for static in [True, False]
+ for vartype in ["int", "String"]] +
+ [BenchVHField(VH_GAA, static, vartype, flavour, "getAndAdd")
+ for flavour in ["", "Acquire", "Release"]
+ for static in [True, False]
+ for vartype in ["int", "float"]] +
+ [BenchVHField(VH_GAB, static, vartype, flavour, "getAndBitwise")
+ for flavour in [oper + mode
+ for oper in ["Or", "Xor", "And"]
+ for mode in ["", "Acquire", "Release"]]
+ for static in [True, False]
+ for vartype in ["int"]] +
+ [BenchVHArray(VH_GET_A, vartype, flavour, "get")
+ for flavour in [""]
+ for vartype in ["int", "String"]] +
+ [BenchVHArray(VH_SET_A, vartype, flavour, "set")
+ for flavour in [""]
+ for vartype in ["int", "String"]] +
+ [BenchVHByteArrayView(VH_GET_BAV, byteorder, vartype, flavour, "get")
+ for flavour in [""]
+ for byteorder in ["BIG_ENDIAN", "LITTLE_ENDIAN"]
+ for vartype in ["int"]] +
+ [BenchVHByteArrayView(VH_SET_BAV, byteorder, vartype, flavour, "set")
+ for flavour in [""]
+ for byteorder in ["BIG_ENDIAN", "LITTLE_ENDIAN"]
+ for vartype in ["int"]] +
+ [BenchReflect(REFLECT_GET, static, vartype, "get")
+ for static in [True, False]
+ for vartype in ["int", "String"]] +
+ [BenchReflect(REFLECT_SET, static, vartype, "set")
+ for static in [True, False]
+ for vartype in ["int", "String"]] +
+ [BenchUnsafe(UNSAFE_GET, static, vartype, "get")
+ for static in [True, False]
+ for vartype in ["int", "String"]] +
+ [BenchUnsafe(UNSAFE_PUT, static, vartype, "put")
+ for static in [True, False]
+ for vartype in ["int", "String"]] +
+ [BenchUnsafe(UNSAFE_CAS, static, vartype, method)
+ for method in ["compareAndSwap", "compareAndSet"]
+ for static in [True, False]
+ for vartype in ["int", "String"]])
+
+
+MAIN = BANNER + """
+public class Main {
+ static MicroBenchmark[] benchmarks;
+
+ private static void initialize() throws Throwable {
+ benchmarks = new MicroBenchmark[] {""" + "".join(["""
+ new {}(),""".format(b.fullname()) for b in ALL_BENCHMARKS]) + """
+ };
+ }
+
+ public static void main(String[] args) throws Throwable {
+ initialize();
+ for (MicroBenchmark benchmark : benchmarks) {
+ benchmark.report();
+ }
+ }
+}"""
+
+
+def main(argv):
+ final_java_dir = Path(argv[1])
+ if not final_java_dir.exists() or not final_java_dir.is_dir():
+ print("{} is not a valid java dir".format(final_java_dir), file=sys.stderr)
+ sys.exit(1)
+
+ for bench in ALL_BENCHMARKS:
+ file_path = final_java_dir / "{}.java".format(bench.fullname())
+ with file_path.open("w") as f:
+ print(bench.gencode(), file=f)
+
+ file_path = final_java_dir / "Main.java"
+ with file_path.open("w") as f:
+ print(MAIN, file=f)
+
+
+if __name__ == '__main__':
+ main(sys.argv)