diff options
| author | 2022-03-07 12:40:27 +0000 | |
|---|---|---|
| committer | 2022-03-07 12:40:27 +0000 | |
| commit | de49d24993aaa5628ec04df989149be43ee85c1a (patch) | |
| tree | a2988ad54fcf25db90e3191e76c58d9e20514ad4 /test/2239-varhandle-perf/util-src/generate_java.py | |
| parent | 3d004a52434a7dbbc73bb477b7475cad27af6e94 (diff) | |
| parent | 964f5c8b27941570df265da753b69c0a4947a493 (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.py | 504 |
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) |