1#!/usr/bin/python3 2# 3# Copyright (C) 2022 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17""" 18Generate java benchmarks for varhandles. 19Adapted to use CrystalBall from art/test/2239-varhandle-perf/util-src/generate_java.py. 20 21To run use: python generate_java.py <destination_directory> 22 23And then to correct lint errors (from frameworks/base): 24../../tools/repohooks/tools/google-java-format.py --fix --google-java-format-diff ../../external/google-java-format/scripts/google-java-format-diff.py 25""" 26 27 28from enum import Enum 29from pathlib import Path 30 31import io 32import sys 33 34 35class MemLoc(Enum): 36 FIELD = 0 37 ARRAY = 1 38 BYTE_ARRAY_VIEW = 2 39 40 41def to_camel_case(word): 42 return ''.join(c for c in word.title() if not c == '_') 43 44 45LOOP ="BenchmarkState state = mPerfStatusReporter.getBenchmarkState();\n while (state.keepRunning())" 46 47class Benchmark: 48 def __init__(self, code, static, vartype, flavour, klass, method, memloc, 49 byteorder="LITTLE_ENDIAN"): 50 self.code = code 51 self.static = static 52 self.vartype = vartype 53 self.flavour = flavour 54 self.klass = klass 55 self.method = method 56 self.byteorder = byteorder 57 self.memloc = memloc 58 59 def fullname(self): 60 return "{klass}{method}{flavour}{static_name}{memloc}{byteorder}{vartype}PerfTest".format( 61 klass = self.klass, 62 method = to_camel_case(self.method), 63 flavour = self.flavour, 64 static_name = "Static" if self.static else "", 65 memloc = to_camel_case(self.memloc.name), 66 byteorder = to_camel_case(self.byteorder), 67 vartype = to_camel_case(self.vartype)) 68 69 def gencode(self): 70 if self.klass == "Reflect": 71 method_suffix = "" if self.vartype == "String" else self.vartype.title() 72 static_first_arg = "null" 73 elif self.klass == "Unsafe": 74 method_suffix = "Object" if self.vartype == "String" else self.vartype.title() 75 static_first_arg = "this.getClass()" 76 else: 77 method_suffix = "" 78 static_first_arg = "" 79 80 first_arg = static_first_arg if self.static else "this" 81 82 return self.code.format( 83 name = self.fullname(), 84 method = self.method + method_suffix, 85 flavour = self.flavour, 86 static_name = "Static" if self.static else "", 87 static_kwd = "static " if self.static else "", 88 static_prefix = "s" if self.static else "m", 89 this = first_arg, 90 this_comma = "" if not first_arg else first_arg + ", ", 91 vartype = self.vartype, 92 byteorder = self.byteorder, 93 value1 = VALUES[self.vartype][0], 94 value2 = VALUES[self.vartype][1], 95 value1_byte_array = VALUES["byte[]"][self.byteorder][0], 96 value2_byte_array = VALUES["byte[]"][self.byteorder][1], 97 loop = LOOP) 98 99 100def BenchVHField(code, static, vartype, flavour, method): 101 return Benchmark(code, static, vartype, flavour, "VarHandle", method, MemLoc.FIELD) 102 103 104def BenchVHArray(code, vartype, flavour, method): 105 return Benchmark(code, False, vartype, flavour, "VarHandle", method, MemLoc.ARRAY) 106 107 108def BenchVHByteArrayView(code, byteorder, vartype, flavour, method): 109 return Benchmark(code, False, vartype, flavour, "VarHandle", method, MemLoc.BYTE_ARRAY_VIEW, byteorder) 110 111 112def BenchReflect(code, static, vartype, method): 113 return Benchmark(code, static, vartype, "", "Reflect", method, MemLoc.FIELD) 114 115 116def BenchUnsafe(code, static, vartype, method): 117 return Benchmark(code, static, vartype, "", "Unsafe", method, MemLoc.FIELD) 118 119 120VALUES = { 121 "int": ["42", "~42"], 122 "float": ["3.14f", "2.17f"], 123 "String": ["\"qwerty\"", "null"], 124 "byte[]": { 125 "LITTLE_ENDIAN": [ 126 "{ (byte) VALUE, (byte) (VALUE >> 8), (byte) (VALUE >> 16), (byte) (VALUE >> 24) }", 127 "{ (byte) VALUE, (byte) (-1 >> 8), (byte) (-1 >> 16), (byte) (-1 >> 24) }", 128 ], 129 "BIG_ENDIAN": [ 130 "{ (byte) (VALUE >> 24), (byte) (VALUE >> 16), (byte) (VALUE >> 8), (byte) VALUE }", 131 "{ (byte) (-1 >> 24), (byte) (-1 >> 16), (byte) (-1 >> 8), (byte) VALUE }", 132 ], 133 }, 134} 135 136REPEAT = 2 137REPEAT_HALF = (int) (REPEAT / 2) 138 139 140BANNER = """/* 141 * Copyright (C) 2022 The Android Open Source Project 142 * 143 * Licensed under the Apache License, Version 2.0 (the "License"); 144 * you may not use this file except in compliance with the License. 145 * You may obtain a copy of the License at 146 * 147 * http://www.apache.org/licenses/LICENSE-2.0 148 * 149 * Unless required by applicable law or agreed to in writing, software 150 * distributed under the License is distributed on an "AS IS" BASIS, 151 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 152 * See the License for the specific language governing permissions and 153 * limitations under the License. 154 */ 155 // This file is generated by generate_java.py do not directly modify!""" 156 157 158VH_IMPORTS = """ 159package android.libcore.varhandles; 160 161import android.perftests.utils.BenchmarkState; 162import android.perftests.utils.PerfStatusReporter; 163 164import androidx.test.filters.LargeTest; 165import androidx.test.runner.AndroidJUnit4; 166 167import org.junit.After; 168import org.junit.Before; 169import org.junit.Rule; 170import org.junit.Test; 171import org.junit.runner.RunWith; 172 173import java.lang.invoke.MethodHandles; 174import java.lang.invoke.VarHandle; 175""" 176 177 178VH_START = BANNER + VH_IMPORTS + """ 179@RunWith(AndroidJUnit4.class) 180@LargeTest 181public class {name} {{ 182 @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); 183 static final {vartype} FIELD_VALUE = {value1}; 184 {static_kwd}{vartype} {static_prefix}Field = FIELD_VALUE; 185 VarHandle mVh; 186 187 public {name}() throws Throwable {{ 188 mVh = MethodHandles.lookup().find{static_name}VarHandle(this.getClass(), "{static_prefix}Field", {vartype}.class); 189 }} 190""" 191 192 193END = """ 194 }} 195 }} 196}}""" 197 198 199VH_GET = VH_START + """ 200 @Before 201 public void setup() {{ 202 {vartype} v = ({vartype}) mVh.{method}{flavour}({this}); 203 if (v != FIELD_VALUE) {{ 204 throw new RuntimeException("field has unexpected value " + v); 205 }} 206 }} 207 208 @Test 209 public void run() {{ 210 {vartype} x; 211 {loop} {{""" + """ 212 x = ({vartype}) mVh.{method}{flavour}({this});""" * REPEAT + END 213 214 215VH_SET = VH_START + """ 216 @After 217 public void teardown() {{ 218 if ({static_prefix}Field != FIELD_VALUE) {{ 219 throw new RuntimeException("{static_prefix}Field has unexpected value " + {static_prefix}Field); 220 }} 221 }} 222 223 @Test 224 public void run() {{ 225 {vartype} x; 226 {loop} {{""" + """ 227 mVh.{method}{flavour}({this_comma}FIELD_VALUE);""" * REPEAT + END 228 229 230VH_CAS = VH_START + """ 231 @Test 232 public void run() {{ 233 boolean success; 234 {loop} {{""" + """ 235 success = mVh.{method}{flavour}({this_comma}{static_prefix}Field, {value2}); 236 success = mVh.{method}{flavour}({this_comma}{static_prefix}Field, {value1});""" * REPEAT_HALF + END 237 238 239VH_CAE = VH_START + """ 240 @Test 241 public void run() {{ 242 {vartype} x; 243 {loop} {{""" + """ 244 x = ({vartype}) mVh.{method}{flavour}({this_comma}{static_prefix}Field, {value2}); 245 x = ({vartype}) mVh.{method}{flavour}({this_comma}{static_prefix}Field, {value1});""" * REPEAT_HALF + END 246 247 248VH_GAS = VH_START + """ 249 @Test 250 public void run() {{ 251 {vartype} x; 252 {loop} {{""" + """ 253 x = ({vartype}) mVh.{method}{flavour}({this_comma}{value2});""" * REPEAT + END 254 255 256VH_GAA = VH_START + """ 257 @Test 258 public void run() {{ 259 {vartype} x; 260 {loop} {{""" + """ 261 x = ({vartype}) mVh.{method}{flavour}({this_comma}{value2});""" * REPEAT + END 262 263 264VH_GAB = VH_START + """ 265 @Test 266 public void run() {{ 267 int x; 268 {loop} {{""" + """ 269 x = ({vartype}) mVh.{method}{flavour}({this_comma}{value2});""" * REPEAT + END 270 271 272VH_START_ARRAY = BANNER + VH_IMPORTS + """ 273@RunWith(AndroidJUnit4.class) 274@LargeTest 275public class {name} {{ 276 @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); 277 static final {vartype} ELEMENT_VALUE = {value1}; 278 {vartype}[] mArray = {{ ELEMENT_VALUE }}; 279 VarHandle mVh; 280 281 public {name}() throws Throwable {{ 282 mVh = MethodHandles.arrayElementVarHandle({vartype}[].class); 283 }} 284""" 285 286 287VH_GET_A = VH_START_ARRAY + """ 288 @Before 289 public void setup() {{ 290 {vartype} v = ({vartype}) mVh.{method}{flavour}(mArray, 0); 291 if (v != ELEMENT_VALUE) {{ 292 throw new RuntimeException("array element has unexpected value: " + v); 293 }} 294 }} 295 296 @Test 297 public void run() {{ 298 {vartype}[] a = mArray; 299 {vartype} x; 300 {loop} {{""" + """ 301 x = ({vartype}) mVh.{method}{flavour}(a, 0);""" * REPEAT + END 302 303 304VH_SET_A = VH_START_ARRAY + """ 305 @After 306 public void teardown() {{ 307 if (mArray[0] != {value2}) {{ 308 throw new RuntimeException("array element has unexpected value: " + mArray[0]); 309 }} 310 }} 311 312 @Test 313 public void run() {{ 314 {vartype}[] a = mArray; 315 {vartype} x; 316 {loop} {{""" + """ 317 mVh.{method}{flavour}(a, 0, {value2});""" * REPEAT + END 318 319 320VH_START_BYTE_ARRAY_VIEW = BANNER + VH_IMPORTS + """ 321import java.util.Arrays; 322import java.nio.ByteOrder; 323 324@RunWith(AndroidJUnit4.class) 325@LargeTest 326public class {name} {{ 327 @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); 328 static final {vartype} VALUE = {value1}; 329 byte[] mArray1 = {value1_byte_array}; 330 byte[] mArray2 = {value2_byte_array}; 331 VarHandle mVh; 332 333 public {name}() throws Throwable {{ 334 mVh = MethodHandles.byteArrayViewVarHandle({vartype}[].class, ByteOrder.{byteorder}); 335 }} 336""" 337 338 339VH_GET_BAV = VH_START_BYTE_ARRAY_VIEW + """ 340 @Before 341 public void setup() {{ 342 {vartype} v = ({vartype}) mVh.{method}{flavour}(mArray1, 0); 343 if (v != VALUE) {{ 344 throw new RuntimeException("array has unexpected value: " + v); 345 }} 346 }} 347 348 @Test 349 public void run() {{ 350 byte[] a = mArray1; 351 {vartype} x; 352 {loop} {{""" + """ 353 x = ({vartype}) mVh.{method}{flavour}(a, 0);""" * REPEAT + END 354 355 356VH_SET_BAV = VH_START_BYTE_ARRAY_VIEW + """ 357 @After 358 public void teardown() {{ 359 if (!Arrays.equals(mArray2, mArray1)) {{ 360 throw new RuntimeException("array has unexpected values: " + 361 mArray2[0] + " " + mArray2[1] + " " + mArray2[2] + " " + mArray2[3]); 362 }} 363 }} 364 365 @Test 366 public void run() {{ 367 byte[] a = mArray2; 368 {loop} {{""" + """ 369 mVh.{method}{flavour}(a, 0, VALUE);""" * REPEAT + END 370 371 372REFLECT_START = BANNER + VH_IMPORTS + """ 373import java.lang.reflect.Field; 374 375@RunWith(AndroidJUnit4.class) 376@LargeTest 377public class {name} {{ 378 @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); 379 Field mField; 380 {static_kwd}{vartype} {static_prefix}Value; 381 382 public {name}() throws Throwable {{ 383 mField = this.getClass().getDeclaredField("{static_prefix}Value"); 384 }} 385""" 386 387 388REFLECT_GET = REFLECT_START + """ 389 @Test 390 public void run() throws Throwable {{ 391 {vartype} x; 392 {loop} {{""" + """ 393 x = ({vartype}) mField.{method}({this});""" * REPEAT + END 394 395 396REFLECT_SET = REFLECT_START + """ 397 @Test 398 public void run() throws Throwable {{ 399 {loop} {{""" + """ 400 mField.{method}({this_comma}{value1});""" * REPEAT + END 401 402 403UNSAFE_START = BANNER + VH_IMPORTS + """ 404import java.lang.reflect.Field; 405import jdk.internal.misc.Unsafe; 406 407@RunWith(AndroidJUnit4.class) 408@LargeTest 409public class {name} {{ 410 @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); 411 long mOffset; 412 public {static_kwd}{vartype} {static_prefix}Value = {value1}; 413 414 {name}() throws Throwable {{ 415 Field field = this.getClass().getDeclaredField("{static_prefix}Value"); 416 mOffset = get{static_name}FieldOffset(field); 417 }} 418""" 419 420 421UNSAFE_GET = UNSAFE_START + """ 422 @Test 423 public void run() throws Throwable {{ 424 {vartype} x; 425 {loop} {{""" + """ 426 x = ({vartype}) theUnsafe.{method}({this_comma}mOffset);""" * REPEAT + END 427 428 429UNSAFE_PUT = UNSAFE_START + """ 430 @Test 431 public void run() throws Throwable {{ 432 {loop} {{""" + """ 433 theUnsafe.{method}({this_comma}mOffset, {value1});""" * REPEAT + END 434 435 436UNSAFE_CAS = UNSAFE_START + """ 437 @Test 438 public void run() throws Throwable {{ 439 {loop} {{""" + """ 440 theUnsafe.{method}({this_comma}mOffset, {value1}, {value2}); 441 theUnsafe.{method}({this_comma}mOffset, {value2}, {value1});""" * REPEAT_HALF + END 442 443 444ALL_BENCHMARKS = ( 445 [BenchVHField(VH_GET, static, vartype, flavour, "get") 446 for flavour in ["", "Acquire", "Opaque", "Volatile"] 447 for static in [True, False] 448 for vartype in ["int", "String"]] + 449 [BenchVHField(VH_SET, static, vartype, flavour, "set") 450 for flavour in ["", "Volatile", "Opaque", "Release"] 451 for static in [True, False] 452 for vartype in ["int", "String"]] + 453 [BenchVHField(VH_CAS, static, vartype, flavour, "compareAndSet") 454 for flavour in [""] 455 for static in [True, False] 456 for vartype in ["int", "String"]] + 457 [BenchVHField(VH_CAS, static, vartype, flavour, "weakCompareAndSet") 458 for flavour in ["", "Plain", "Acquire", "Release"] 459 for static in [True, False] 460 for vartype in ["int", "String"]] + 461 [BenchVHField(VH_CAE, static, vartype, flavour, "compareAndExchange") 462 for flavour in ["", "Acquire", "Release"] 463 for static in [True, False] 464 for vartype in ["int", "String"]] + 465 [BenchVHField(VH_GAS, static, vartype, flavour, "getAndSet") 466 for flavour in ["", "Acquire", "Release"] 467 for static in [True, False] 468 for vartype in ["int", "String"]] + 469 [BenchVHField(VH_GAA, static, vartype, flavour, "getAndAdd") 470 for flavour in ["", "Acquire", "Release"] 471 for static in [True, False] 472 for vartype in ["int", "float"]] + 473 [BenchVHField(VH_GAB, static, vartype, flavour, "getAndBitwise") 474 for flavour in [oper + mode 475 for oper in ["Or", "Xor", "And"] 476 for mode in ["", "Acquire", "Release"]] 477 for static in [True, False] 478 for vartype in ["int"]] + 479 [BenchVHArray(VH_GET_A, vartype, flavour, "get") 480 for flavour in [""] 481 for vartype in ["int", "String"]] + 482 [BenchVHArray(VH_SET_A, vartype, flavour, "set") 483 for flavour in [""] 484 for vartype in ["int", "String"]] + 485 [BenchVHByteArrayView(VH_GET_BAV, byteorder, vartype, flavour, "get") 486 for flavour in [""] 487 for byteorder in ["BIG_ENDIAN", "LITTLE_ENDIAN"] 488 for vartype in ["int"]] + 489 [BenchVHByteArrayView(VH_SET_BAV, byteorder, vartype, flavour, "set") 490 for flavour in [""] 491 for byteorder in ["BIG_ENDIAN", "LITTLE_ENDIAN"] 492 for vartype in ["int"]] + 493 [BenchReflect(REFLECT_GET, static, vartype, "get") 494 for static in [True, False] 495 for vartype in ["int", "String"]] + 496 [BenchReflect(REFLECT_SET, static, vartype, "set") 497 for static in [True, False] 498 for vartype in ["int", "String"]]) 499 500 501 502def main(argv): 503 final_java_dir = Path(argv[1]) 504 if not final_java_dir.exists() or not final_java_dir.is_dir(): 505 print("{} is not a valid java dir".format(final_java_dir), file=sys.stderr) 506 sys.exit(1) 507 508 for bench in ALL_BENCHMARKS: 509 file_path = final_java_dir / "{}.java".format(bench.fullname()) 510 with file_path.open("w") as f: 511 print(bench.gencode(), file=f) 512 513 514if __name__ == '__main__': 515 main(sys.argv) 516