| #!/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) |