505 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			505 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
| #!/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)
 |