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