diff options
Diffstat (limited to 'src/main/java/org/linaro/benchmarks/micro')
15 files changed, 2843 insertions, 0 deletions
diff --git a/src/main/java/org/linaro/benchmarks/micro/ArrayAccess.java b/src/main/java/org/linaro/benchmarks/micro/ArrayAccess.java new file mode 100644 index 0000000..5480562 --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/ArrayAccess.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 Linaro Limited. + * + * 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. + */ + +/* + * Description: Tracks performance of accessing array locations using variables and constants + * as indexes. + * Main Focus: Memory accesses using arrays. + * + */ + +package org.linaro.benchmarks.micro; + +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) + +public class ArrayAccess { + + private static final int ITER_COUNT = 1000; + private int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + public static void accessArrayConstants(int[] array) { + for (int j = 0; j < 100000; j++) { + array[4]++; + array[5]++; + } + } + + public static void accessArrayVariables(int[] array, int i) { + for (int j = 0; j < 100000; j++) { + array[i]++; + array[i + 1]++; + array[i + 2]++; + array[i - 2]++; + array[i - 1]++; + } + } + + @Benchmark + public void jmhTimeAccessArrayConstants() { + for (int i = 0; i < 1000; i++) { + accessArrayConstants(array); + } + } + + @Benchmark + public void jmhTimeAccessArrayVariables() { + for (int i = 0; i < 1000; i++) { + accessArrayVariables(array, 5); + } + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/Base64.java b/src/main/java/org/linaro/benchmarks/micro/Base64.java new file mode 100644 index 0000000..df58234 --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/Base64.java @@ -0,0 +1,139 @@ +/* + * encode and decode methods from: + * https://en.wikipedia.org/wiki/Base64#Sample_Implementation_in_Java + * Available under the Creative Commons Attribution-ShareAlike License. + * http://creativecommons.org/licenses/by-sa/3.0/ + * Modifiication: added test code + */ + +/* + * Description: Uses a Base64 MIME implementation to check for regressions + in loops, array access, load/store, and string manipulation. + * Main Focus: General operations related to Base64 encoding/decoding. + * Secondary Focus: Array access, load/store, string manipulation. + * + */ + +package org.linaro.benchmarks.micro; + +import java.lang.IllegalArgumentException; +import java.lang.StringBuilder; +import java.lang.System; +import java.lang.Thread; +import java.util.Random; +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) + +public class Base64 { + private static Random rnd = new Random(); + private static final int ENC_Length = 64; + private static final int NUM_Encodings = 16; + private static String[] randomStrings = new String[NUM_Encodings]; + private static String[] randomBase64 = new String[NUM_Encodings]; + private static String[] encodeResults = new String[NUM_Encodings]; + private static String[] decodeResults = new String[NUM_Encodings]; + private static final String codes = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + private static char base64Pad = '='; + + static { + generateRandomStrings(); + generateRandomBase64(); + } + + private static void generateRandomStrings() { + for (int i = 0; i < NUM_Encodings; i++) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < ENC_Length; j++) { + sb.append(Character.valueOf((char)rnd.nextInt())); + } + randomStrings[i] = sb.toString(); + } + } + + private static void generateRandomBase64() { + for (int i = 0; i < NUM_Encodings; i++) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < ENC_Length; j++) { + sb.append(codes.charAt(rnd.nextInt(codes.length() - 1))); + } + randomBase64[i] = sb.toString(); + } + } + + private static String encode(String str) { + byte[] in = str.getBytes(); + StringBuffer out = new StringBuffer((in.length * 4) / 3); + int b; + for (int i = 0; i < in.length; i += 3) { + b = (in[i] & 0xFC) >> 2; + out.append(codes.charAt(b)); + b = (in[i] & 0x03) << 4; + if (i + 1 < in.length) { + b |= (in[i + 1] & 0xF0) >> 4; + out.append(codes.charAt(b)); + b = (in[i + 1] & 0x0F) << 2; + if (i + 2 < in.length) { + b |= (in[i + 2] & 0xC0) >> 6; + out.append(codes.charAt(b)); + b = in[i + 2] & 0x3F; + out.append(codes.charAt(b)); + } else { + out.append(codes.charAt(b)); + out.append('='); + } + } else { + out.append(codes.charAt(b)); + out.append("=="); + } + } + + return out.toString(); + } + + private static byte[] decode(String input) { + if (input.length() % 4 != 0) { + System.err.println("Invalid base64 input"); + return null; + } + int eqPos = input.indexOf('='); + int len = input.length(); + byte[] decoded = + new byte[((len * 3) / 4) - (eqPos > 0 ? (len - eqPos) : 0)]; + char[] inChars = input.toCharArray(); + int j = 0; + int[] b = new int[4]; + for (int i = 0; i < inChars.length; i += 4) { + b[0] = codes.indexOf(inChars[i]); + b[1] = codes.indexOf(inChars[i + 1]); + b[2] = codes.indexOf(inChars[i + 2]); + b[3] = codes.indexOf(inChars[i + 3]); + decoded[j++] = (byte) ((b[0] << 2) | (b[1] >> 4)); + if (b[2] < 64) { + decoded[j++] = (byte) ((b[1] << 4) | (b[2] >> 2)); + if (b[3] < 64) { + decoded[j++] = (byte) ((b[2] << 6) | b[3]); + } + } + } + return decoded; + } + + @Benchmark + public void jmhTimeEncode() { + for (int j = 0; j < NUM_Encodings; j++) { + encodeResults[j] = encode(randomStrings[j]); + } + } + + @Benchmark + public void jmhTimeDecode() { + for (int j = 0; j < NUM_Encodings; j++) { + decodeResults[j] = new String(decode(randomBase64[j])); + } + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/BitfieldRotate.java b/src/main/java/org/linaro/benchmarks/micro/BitfieldRotate.java new file mode 100644 index 0000000..f3185da --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/BitfieldRotate.java @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2000-2015 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) + * + * Modifications copyright (c) 2015 Linaro Limited. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Description: Check for regressions effecting Bouncy Castle SHA1Digest processing. + * Main Focus: Bitfield rotations. + * Secondary Focus: Loop optimizations. + */ + +package org.linaro.benchmarks.micro; + +import java.nio.ByteBuffer; +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) + +public class BitfieldRotate { + private static final String SOURCE_Text = "That though I loved them for their faults\n" + + "As much as for their good,\n" + + "My friends were enemies on stilts\n" + + "With their heads in a cunning cloud.\n"; + + private int h1; + private int h2; + private int h3; + private int h4; + private int h5; + private int[] x = new int[80]; + private int xOff; + + // + // Additive constants + // + private static final int y1 = 0x5a827999; + private static final int y2 = 0x6ed9eba1; + private static final int y3 = 0x8f1bbcdc; + private static final int y4 = 0xca62c1d6; + + private int ffunc( + int u, + int v, + int w) { + return ((u & v) | ((~u) & w)); + } + + private int hfunc( + int u, + int v, + int w) { + return (u ^ v ^ w); + } + + private int gfunc( + int u, + int v, + int w) { + return ((u & v) | (u & w) | (v & w)); + } + + protected void processBlock() { + // + // expand 16 word block into 80 word block. + // + for (int i = 16; i < 80; i++) { + int t = x[i - 3] ^ x[i - 8] ^ x[i - 14] ^ x[i - 16]; + x[i] = t << 1 | t >>> 31; + } + + // + // set up working variables. + // + int a = h1; + int b = h2; + int c = h3; + int d = h4; + int e = h5; + + // + // round 1 + // + int idx = 0; + + for (int j = 0; j < 4; j++) { + // E = rotateLeft(a, 5) + ffunc(b, c, d) + E + x[idx++] + y1 + // B = rotateLeft(b, 30) + e += (a << 5 | a >>> 27) + ffunc(b, c, d) + x[idx++] + y1; + b = b << 30 | b >>> 2; + + d += (e << 5 | e >>> 27) + ffunc(a, b, c) + x[idx++] + y1; + a = a << 30 | a >>> 2; + + c += (d << 5 | d >>> 27) + ffunc(e, a, b) + x[idx++] + y1; + e = e << 30 | e >>> 2; + + b += (c << 5 | c >>> 27) + ffunc(d, e, a) + x[idx++] + y1; + d = d << 30 | d >>> 2; + + a += (b << 5 | b >>> 27) + ffunc(c, d, e) + x[idx++] + y1; + c = c << 30 | c >>> 2; + } + + // + // round 2 + // + for (int j = 0; j < 4; j++) { + // E = rotateLeft(a, 5) + hfunc(b, c, d) + E + x[idx++] + y2 + // B = rotateLeft(b, 30) + e += (a << 5 | a >>> 27) + hfunc(b, c, d) + x[idx++] + y2; + b = b << 30 | b >>> 2; + + d += (e << 5 | e >>> 27) + hfunc(a, b, c) + x[idx++] + y2; + a = a << 30 | a >>> 2; + + c += (d << 5 | d >>> 27) + hfunc(e, a, b) + x[idx++] + y2; + e = e << 30 | e >>> 2; + + b += (c << 5 | c >>> 27) + hfunc(d, e, a) + x[idx++] + y2; + d = d << 30 | d >>> 2; + + a += (b << 5 | b >>> 27) + hfunc(c, d, e) + x[idx++] + y2; + c = c << 30 | c >>> 2; + } + + // + // round 3 + // + for (int j = 0; j < 4; j++) { + // E = rotateLeft(a, 5) + gfunc(b, c, d) + E + x[idx++] + y3 + // B = rotateLeft(b, 30) + e += (a << 5 | a >>> 27) + gfunc(b, c, d) + x[idx++] + y3; + b = b << 30 | b >>> 2; + + d += (e << 5 | e >>> 27) + gfunc(a, b, c) + x[idx++] + y3; + a = a << 30 | a >>> 2; + + c += (d << 5 | d >>> 27) + gfunc(e, a, b) + x[idx++] + y3; + e = e << 30 | e >>> 2; + + b += (c << 5 | c >>> 27) + gfunc(d, e, a) + x[idx++] + y3; + d = d << 30 | d >>> 2; + + a += (b << 5 | b >>> 27) + gfunc(c, d, e) + x[idx++] + y3; + c = c << 30 | c >>> 2; + } + + // + // round 4 + // + for (int j = 0; j <= 3; j++) { + // E = rotateLeft(a, 5) + hfunc(b, c, d) + E + x[idx++] + y4 + // B = rotateLeft(b, 30) + e += (a << 5 | a >>> 27) + hfunc(b, c, d) + x[idx++] + y4; + b = b << 30 | b >>> 2; + + d += (e << 5 | e >>> 27) + hfunc(a, b, c) + x[idx++] + y4; + a = a << 30 | a >>> 2; + + c += (d << 5 | d >>> 27) + hfunc(e, a, b) + x[idx++] + y4; + e = e << 30 | e >>> 2; + + b += (c << 5 | c >>> 27) + hfunc(d, e, a) + x[idx++] + y4; + d = d << 30 | d >>> 2; + + a += (b << 5 | b >>> 27) + hfunc(c, d, e) + x[idx++] + y4; + c = c << 30 | c >>> 2; + } + + h1 += a; + h2 += b; + h3 += c; + h4 += d; + h5 += e; + + // + // reset start of the buffer. + // + xOff = 0; + for (int i = 0; i < 16; i++) { + x[i] = 0; + } + } + + @Benchmark + public void jmhTimeSHA1DigestProcessBlock() { + h1 = h2 = h3 = h4 = h5 = 0; + processBlock(); + } + + /** + * Integer rotate right patterns. + */ + + int resultIntegerRightRegVCSubV; + int resultIntegerRightRegVNegV; + + public static int rotateIntegerRightRegVCSubV(int value, int distance) { + return (value >>> distance) | (value << (32 - distance)); + } + + public static int rotateIntegerRightRegVNegV(int value, int distance) { + return (value >>> distance) | (value << -distance); + } + + @Benchmark + public void jmhTimeIntegerRotateRight() { + for (int distance = 0; distance < Integer.SIZE; distance++) { + resultIntegerRightRegVCSubV += rotateIntegerRightRegVCSubV(0xCAFEBABE, distance); + resultIntegerRightRegVNegV += rotateIntegerRightRegVNegV(0xCAFEBABE, distance); + } + } + + /** + * Integer rotate left patterns. + */ + + int resultIntegerLeftRegCSubVV; + int resultIntegerLeftRegNegVV; + + public static int rotateIntegerLeftRegCSubVV(int value, int distance) { + return (value >>> (32 - distance)) | (value << distance); + } + + public static int rotateIntegerLeftRegNegVV(int value, int distance) { + return (value >>> -distance) | (value << distance); + } + + @Benchmark + public void jmhTimeIntegerRotateLeft() { + for (int distance = 0; distance < Integer.SIZE; distance++) { + resultIntegerLeftRegCSubVV += rotateIntegerLeftRegCSubVV(0xCAFEBABE, distance); + resultIntegerLeftRegNegVV += rotateIntegerLeftRegNegVV(0xCAFEBABE, distance); + } + } + + /** + * Long rotate right patterns. + */ + + int resultLongRightRegVCSubV; + int resultLongRightRegVNegV; + + public static long rotateLongRightRegVCSubV(long value, int distance) { + return (value >>> distance) | (value << (64 - distance)); + } + + public static long rotateLongRightRegVNegV(long value, int distance) { + return (value >>> distance) | (value << -distance); + } + + @Benchmark + public void jmhTimeLongRotateRight() { + for (int distance = 0; distance < Long.SIZE; distance++) { + resultLongRightRegVCSubV += rotateLongRightRegVCSubV(0xCAFEBABEBAADF00DL, distance); + resultLongRightRegVNegV += rotateLongRightRegVNegV(0xCAFEBABEBAADF00DL, distance); + } + } + + /** + * Long rotate left patterns. + */ + + int resultLongLeftRegCSubVV; + int resultLongLeftRegNegVV; + + public static long rotateLongLeftRegCSubVV(long value, int distance) { + return (value >>> (64 - distance)) | (value << distance); + } + + public static long rotateLongLeftRegNegVV(long value, int distance) { + return (value >>> -distance) | (value << distance); + } + + @Benchmark + public void jmhTimeLongRotateLeft() { + for (int distance = 0; distance < Long.SIZE; distance++) { + resultLongLeftRegCSubVV += rotateLongLeftRegCSubVV(0xCAFEBABEBAADF00DL, distance); + resultLongLeftRegNegVV += rotateLongLeftRegNegVV(0xCAFEBABEBAADF00DL, distance); + } + } +} + diff --git a/src/main/java/org/linaro/benchmarks/micro/ControlFlowRecursive.java b/src/main/java/org/linaro/benchmarks/micro/ControlFlowRecursive.java new file mode 100644 index 0000000..26b4bf9 --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/ControlFlowRecursive.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015, Linaro Limited. Ported to Java from: + * https://github.com/WebKit/webkit/blob/master/PerformanceTests/SunSpider/tests/sunspider-1.0.2/controlflow-recursive.js + * and added Tarai. + * + * Description: A control flow recursive micro benchmark case. + */ + + +// The Computer Language Shootout +// http://shootout.alioth.debian.org/ +// contributed by Isaac Gouy + +// http://benchmarksgame.alioth.debian.org/license.html (BSD 3-clause license) +// See NOTICE file for license. + +package org.linaro.benchmarks.micro; + +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) + +public class ControlFlowRecursive { + private int result = 0; + private final int expected = 57775; + + private int ack(int m, int n) { + if (m == 0) { + return n + 1; + } + if (n == 0) { + return ack(m - 1, 1); + } + return ack(m - 1, ack(m, n - 1)); + } + + private int fib(int n) { + if (n < 2) { + return 1; + } + return fib(n - 2) + fib(n - 1); + } + + private int tak(int x, int y, int z) { + if (y >= x) { + return z; + } + return tak(tak(x - 1, y, z), tak(y - 1, z, x), tak(z - 1, x, y)); + } + + private int tarai(int x, int y, int z) { + if (y >= x) { + return y; + } + return tarai(tarai(x - 1, y, z), tarai(y - 1, z, x), tarai(z - 1, x, y)); + } + + @Benchmark + public void jmhTimeTak() { + for (int i = 3; i <= 5; i++) { + tak(3 * i + 3, 2 * i + 2, i + 1); + } + } + + @Benchmark + public void jmhTimeTarai() { + for (int i = 3; i <= 5; i++) { + tarai(3 * i + 3, 2 * i + 2, i + 1); + } + } + + @Benchmark + public void jmhTimeControlFlowRecursive() { + result = 0; + for (int i = 3; i <= 5; i++) { + result += ack(3, i); + result += fib(17 + i); + result += tak(3 * i + 3, 2 * i + 2, i + 1); + } + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/Exceptions.java b/src/main/java/org/linaro/benchmarks/micro/Exceptions.java new file mode 100644 index 0000000..3150f42 --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/Exceptions.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 Linaro Limited. All rights reserved. + * + * 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. + * + */ + +/* + * Description: Exceptions code utilization. + * Main Focus: + * + */ + +package org.linaro.benchmarks.micro; + +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) + +public class Exceptions { + + private final int SIZE = 10; + private int[] smallArray = new int[SIZE]; + private static final int ARITHM_DOUBLE_ITERS = 20000; + + /** + * Three following examples do the same arithmetics on double, but + * - arithmDouble: just does calculations + * - arirhmDoubleException: does calculations but has a try-catch which never fires. + * - arirhmDoubleExceptionFires: does calculations but has a try-catch which always fires. + */ + private int arithmDoubleExceptionFires(int n) { + double x = 1.0 * (double)n; + double y = 2.0 * (double)n; + double z = 3.0 * (double)n; + double expr = 0.0; + int a = 0; + + for (int i = 0; i < ARITHM_DOUBLE_ITERS; i++) { + expr += x + y + z * x + y * y + x * z; + try { + a += smallArray[i%SIZE + 5]; + smallArray[i%SIZE + 5] += a; + } catch (Exception e) { + a--; + } + } + + return (int)expr + a + (int)x + (int)y + (int)z; + } + + private int arithmDoubleException(int n) { + double x = 1.0 * (double)n; + double y = 2.0 * (double)n; + double z = 3.0 * (double)n; + double expr = 0.0; + int a = 0; + + for (int i = 0; i < ARITHM_DOUBLE_ITERS; i++) { + expr += x + y + z * x + y * y + x * z; + try { + a += smallArray[i%SIZE]; + smallArray[i%SIZE] += a; + } catch (Exception e) { + a--; + } + } + + return (int)expr + a + (int)x + (int)y + (int)z; + } + + private int arithmDouble(int n) { + double x = 1.0 * (double)n; + double y = 2.0 * (double)n; + double z = 3.0 * (double)n; + double expr = 0.0; + int a = 0; + + for (int i = 0; i < ARITHM_DOUBLE_ITERS; i++) { + expr += x + y + z * x + y * y + x * z; + a += smallArray[i%SIZE]; + smallArray[i%SIZE] += a; + } + + return (int)expr + a + (int)x + (int)y + (int)z; + } + + @Benchmark + public void jmhTimeArithmDoubleException() { + //TODO (jmh) Following art-testing, use current iteration as arithmDoubleException's arg instead of 10. + arithmDoubleException(10); + } + + @Benchmark + public void jmhTimeArithmDoubleExceptionFires() { + //TODO (jmh) Following art-testing, use current iteration as arithmDoubleException's arg instead of 20. + arithmDoubleExceptionFires(20); + } + + @Benchmark + public void jmhTimeArithmDouble() { + //TODO (jmh) Following art-testing, use current iteration as arithmDoubleException's arg instead of 30. + arithmDouble(30); + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/HashMapBench.java b/src/main/java/org/linaro/benchmarks/micro/HashMapBench.java new file mode 100644 index 0000000..02ffe14 --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/HashMapBench.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015, Linaro Limited. Ported to Java from: + * http://browserbench.org/JetStream/sources/hash-map.js + * + * Description: A benchmark case for hash map + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE below for additional + * information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/******* NOTICE ********* + + Apache Harmony + Copyright 2006, 2010 The Apache Software Foundation. + + This product includes software developed at + The Apache Software Foundation (http://www.apache.org/). + + Portions of Apache Harmony were originally developed by + Intel Corporation and are licensed to the Apache Software + Foundation under the "Software Grant and Corporate Contribution + License Agreement" and for which the following copyright notices + apply + (C) Copyright 2005 Intel Corporation + (C) Copyright 2005-2006 Intel Corporation + (C) Copyright 2006 Intel Corporation + + + The following copyright notice(s) were affixed to portions of the code + with which this file is now or was at one time distributed + and are placed here unaltered. + + (C) Copyright 1997,2004 International Business Machines Corporation. + All rights reserved. + + (C) Copyright IBM Corp. 2003. + + + This software contains code derived from UNIX V7, Copyright(C) + Caldera International Inc. + + ************************/ + +package org.linaro.benchmarks.micro; + +import java.util.HashMap; +import java.util.Map; +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) + +public class HashMapBench { + + private static final int COUNT = 5000; + private static final int resultExpect = 1050000; + private static final long keySumExpect = 12497500; + private static final int valueSumExpect = 210000; + private int result = 0; + private long keySum = 0; + private int valueSum = 0; + + private Map<Integer, Integer> map = new HashMap<Integer, Integer>(); + + @Benchmark + public void jmhTimeTestHashMap() { + for (int j = 0; j < COUNT; j++) { + map.put(j, 42); + } + + result = 0; + for (int k = 0; k < 5; k++) { + for (int j = 0; j < COUNT; j++) { + result += map.get(j); + } + } + + keySum = 0; + valueSum = 0; + for (Map.Entry<Integer, Integer> entry : map.entrySet()) { + keySum += entry.getKey(); + valueSum += entry.getValue(); + } + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/PrefetchLoopedArrayAccess.java b/src/main/java/org/linaro/benchmarks/micro/PrefetchLoopedArrayAccess.java new file mode 100644 index 0000000..d70a20a --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/PrefetchLoopedArrayAccess.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2015 Linaro Limited. All rights reserved. + * + * 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. + * + */ + +/* + * Description: Tracks performance of looped array access to Java Objects. + * BitSet is an arbitrary choice. 1024 pool and update list + * sizes showed benefits when prefetching 8 references ahead + * during looped array access. Other benchmarks for different + * and mixed Object sizes would be beneficial. + * Main Focus: Looped array access to semi-random memory access patterns. + * Secondary Focus: + * + */ + +package org.linaro.benchmarks.micro; + +import java.util.BitSet; +import java.util.Random; +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) + +public class PrefetchLoopedArrayAccess { + + private static final Random rnd = new Random(0); + private static final int POOL_Size = 1024; + private static final int UPDATE_Size = 1024; + + private BitSet[] bits; + private BitSet[] updateList; + + { + initBitSets(); + initUpdateList(); + } + + private void initUpdateList() { + updateList = new BitSet[UPDATE_Size]; + for (int i = 0; i < UPDATE_Size; i++) { + updateList[i] = bits[rnd.nextInt(POOL_Size)]; + } + } + + private void initBitSets() { + bits = new BitSet[POOL_Size]; + for (int i = 0; i < POOL_Size; i++) { + bits[i] = new BitSet(); + } + } + + private void updateBitSets() { + for (int i = 0; i < UPDATE_Size; i++) { + updateList[i].set(7); + } + } + + @Benchmark + public void jmhTimeRun() { + updateBitSets(); + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/ShifterOperand.java b/src/main/java/org/linaro/benchmarks/micro/ShifterOperand.java new file mode 100644 index 0000000..b29f551 --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/ShifterOperand.java @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2015 Linaro Limited. All rights reserved. + * + * 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. + * + */ + +package org.linaro.benchmarks.micro; + +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) + + +/* + * Description: Tests around binary operations taking shifts or type conversions + * as inputs that can be merged into the shifter operand. + * + * Main Focus: shifter operand + * + * Secondary Focus: instruction scheduling + * + * Some comments in this file assume that shift or extend operations are merged + * into the shifter operand of the binary operations using them. + */ + +// We declare many temporary local variables with similar names. Avoid the extra +// lines that would be required with one declaration per line. +// CHECKSTYLE.OFF: MultipleVariableDeclarations + +public class ShifterOperand { + + private static final int LOOP_SIZE = 32; + + @Benchmark + public static int jmhTimeIntSingleUseLatency1() { + int t1 = 0; + for (int iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has a single use. + // Results are used one instruction after being produced. + t1 |= iter >> 1; + t1 &= iter << 2; + t1 ^= iter >>> 3; + t1 += (byte)iter; + t1 -= (char)iter; + t1 += (short)iter; + } + return t1; + } + + // The `ThreadN` suffix indicates that the loop contains `N` independent + // computation threads that execute (almost exactly) the same thing. The + // multiple threads allow for instruction scheduling to kick in. + + @Benchmark + public static int jmhTimeIntSingleUseLatency1Thread2() { + int t1 = 0, t2 = 1; + for (int iter1 = 0, iter2 = 1; + iter1 < LOOP_SIZE; + iter1++, iter2++) { + // Each bitfield operation has a single use. + // Results are used one instruction after being produced. + // Computation thread 1. + t1 |= iter1 >> 1; + t1 &= iter1 << 2; + t1 ^= iter1 >>> 3; + t1 += (byte)iter1; + t1 -= (char)iter1; + t1 += (short)iter1; + // Computation thread 2. + t2 |= iter2 >> 1; + t2 &= iter2 << 2; + t2 ^= iter2 >>> 3; + t2 += (byte)iter2; + t2 -= (char)iter2; + t2 += (short)iter2; + } + return t1; + } + + @Benchmark + public static int jmhTimeIntSingleUseLatency1Thread3() { + int t1 = 0, t2 = 1, t3 = 2; + // Each bitfield operation has a single use. + // Results are used one instruction after being produced. + for (int iter1 = 0, iter2 = 1, iter3 = 2; + iter1 < LOOP_SIZE; + iter1++, iter2++, iter3++) { + // Computation thread 1. + t1 |= iter1 >> 1; + t1 &= iter1 << 2; + t1 ^= iter1 >>> 3; + t1 += (byte)iter1; + t1 -= (char)iter1; + t1 += (short)iter1; + // Computation thread 2. + t2 |= iter2 >> 1; + t2 &= iter2 << 2; + t2 ^= iter2 >>> 3; + t2 += (byte)iter2; + t2 -= (char)iter2; + t2 += (short)iter2; + // Computation thread 3. + t3 |= iter3 >> 1; + t3 &= iter3 << 2; + t3 ^= iter3 >>> 3; + t3 += (byte)iter3; + t3 -= (char)iter3; + t3 += (short)iter3; + } + return t1; + } + + @Benchmark + public static int jmhTimeIntSingleUseLatency1Thread4() { + int t1 = 0, t2 = 1, t3 = 2, t4 = 3; + // Each bitfield operation has a single use. + // Results are used one instruction after being produced. + + for (int iter1 = 0, iter2 = 1, iter3 = 2, iter4 = 3; + iter1 < LOOP_SIZE; + iter1++, iter2++, iter3++, iter4++) { + // Computation thread 1. + t1 |= iter1 >> 1; + t1 &= iter1 << 2; + t1 ^= iter1 >>> 3; + t1 += (byte)iter1; + t1 -= (char)iter1; + t1 += (short)iter1; + // Computation thread 2. + t2 |= iter2 >> 1; + t2 &= iter2 << 2; + t2 ^= iter2 >>> 3; + t2 += (byte)iter2; + t2 -= (char)iter2; + t2 += (short)iter2; + // Computation thread 3. + t3 |= iter3 >> 1; + t3 &= iter3 << 2; + t3 ^= iter3 >>> 3; + t3 += (byte)iter3; + t3 -= (char)iter3; + t3 += (short)iter3; + // Computation thread 4. + t4 |= iter4 >> 1; + t4 &= iter4 << 2; + t4 ^= iter4 >>> 3; + t4 += (byte)iter4; + t4 -= (char)iter4; + t4 += (short)iter4; + } + return t1; + } + + @Benchmark + public static int jmhTimeIntSingleUseLatency2() { + int t1 = 0, t2 = 0; + for (int iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has a single use. + // Results are used two instructions after being produced. + t1 |= iter >> 1; + t2 &= iter << 2; + t1 ^= iter >>> 3; + t2 += (byte)iter; + t1 -= (char)iter; + t2 += (short)iter; + } + return t1 | t2; + } + + @Benchmark + public static int jmhTimeIntSingleUseLatency3() { + int t1 = 0, t2 = 0, t3 = 0; + for (int iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has a single use. + // Results are used three instructions after being produced. + t1 |= iter >> 1; + t2 &= iter << 2; + t3 ^= iter >>> 3; + t1 += (byte)iter; + t2 -= (char)iter; + t3 += (short)iter; + } + return t1 | t2; + } + + @Benchmark + public static int jmhTimeIntSingleUseLatencyLoop() { + int t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0; + for (int iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has a single use. + // Results are only used in the next iteration of the loop. + t1 |= iter >> 1; + t2 &= iter << 2; + t3 ^= iter >>> 3; + t4 += (byte)iter; + t5 -= (char)iter; + t6 += (short)iter; + } + return t1 | t2 | t3 | t4 | t5 | t6; + } + + @Benchmark + public static long jmhTimeLongSingleUseLatency1() { + long t1 = 0; + for (long iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has a single use. + // Results are used one instruction after being produced. + t1 |= iter >> 1; + t1 &= iter << 2; + t1 ^= iter >>> 3; + t1 += (byte)iter; + t1 -= (char)iter; + t1 += (short)iter; + } + return t1; + } + + // The `ThreadN` suffix indicates that the loop contains `N` independent + // computation threads that execute (almost exactly) the same thing. The + // multiple threads allow for instruction scheduling to kick in. + @Benchmark + public static long jmhTimeLongSingleUseLatency1Thread2() { + long t1 = 0, t2 = 1; + for (long iter1 = 0, iter2 = 1; + iter1 < LOOP_SIZE; + iter1++, iter2++) { + // Each bitfield operation has a single use. + // Results are used one instruction after being produced. + // Computation thread 1. + t1 |= iter1 >> 1; + t1 &= iter1 << 2; + t1 ^= iter1 >>> 3; + t1 += (byte)iter1; + t1 -= (char)iter1; + t1 += (short)iter1; + // Computation thread 2. + t2 |= iter2 >> 1; + t2 &= iter2 << 2; + t2 ^= iter2 >>> 3; + t2 += (byte)iter2; + t2 -= (char)iter2; + t2 += (short)iter2; + } + return t1; + } + + @Benchmark + public static long jmhTimeLongSingleUseLatency1Thread3() { + long t1 = 0, t2 = 1, t3 = 2; + // Each bitfield operation has a single use. + // Results are used one instruction after being produced. + for (long iter1 = 0, iter2 = 1, iter3 = 2; + iter1 < LOOP_SIZE; + iter1++, iter2++, iter3++) { + // Computation thread 1. + t1 |= iter1 >> 1; + t1 &= iter1 << 2; + t1 ^= iter1 >>> 3; + t1 += (byte)iter1; + t1 -= (char)iter1; + t1 += (short)iter1; + // Computation thread 2. + t2 |= iter2 >> 1; + t2 &= iter2 << 2; + t2 ^= iter2 >>> 3; + t2 += (byte)iter2; + t2 -= (char)iter2; + t2 += (short)iter2; + // Computation thread 3. + t3 |= iter3 >> 1; + t3 &= iter3 << 2; + t3 ^= iter3 >>> 3; + t3 += (byte)iter3; + t3 -= (char)iter3; + t3 += (short)iter3; + } + return t1; + } + + @Benchmark + public static long jmhTimeLongSingleUseLatency1Thread4() { + long t1 = 0, t2 = 1, t3 = 2, t4 = 3; + // Each bitfield operation has a single use. + // Results are used one instruction after being produced. + + for (long iter1 = 0, iter2 = 1, iter3 = 2, iter4 = 3; + iter1 < LOOP_SIZE; + iter1++, iter2++, iter3++, iter4++) { + // Computation thread 1. + t1 |= iter1 >> 1; + t1 &= iter1 << 2; + t1 ^= iter1 >>> 3; + t1 += (byte)iter1; + t1 -= (char)iter1; + t1 += (short)iter1; + // Computation thread 2. + t2 |= iter2 >> 1; + t2 &= iter2 << 2; + t2 ^= iter2 >>> 3; + t2 += (byte)iter2; + t2 -= (char)iter2; + t2 += (short)iter2; + // Computation thread 3. + t3 |= iter3 >> 1; + t3 &= iter3 << 2; + t3 ^= iter3 >>> 3; + t3 += (byte)iter3; + t3 -= (char)iter3; + t3 += (short)iter3; + // Computation thread 4. + t4 |= iter4 >> 1; + t4 &= iter4 << 2; + t4 ^= iter4 >>> 3; + t4 += (byte)iter4; + t4 -= (char)iter4; + t4 += (short)iter4; + } + return t1; + } + + @Benchmark + public static long jmhTimeLongSingleUseLatency2() { + long t1 = 0, t2 = 0; + for (long iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has a single use. + // Results are used two instructions after being produced. + t1 |= iter >> 1; + t2 &= iter << 2; + t1 ^= iter >>> 3; + t2 += (byte)iter; + t1 -= (char)iter; + t2 += (short)iter; + } + return t1 | t2; + } + + @Benchmark + public static long jmhTimeLongSingleUseLatency3() { + long t1 = 0, t2 = 0, t3 = 0; + for (long iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has a single use. + // Results are used three instructions after being produced. + t1 |= iter >> 1; + t2 &= iter << 2; + t3 ^= iter >>> 3; + t1 += (byte)iter; + t2 -= (char)iter; + t3 += (short)iter; + } + return t1 | t2; + } + + @Benchmark + public static long jmhTimeLongSingleUseLatencyLoop() { + long t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0; + for (long iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has a single use. + // Results are only used in the next iteration of the loop. + t1 |= iter >> 1; + t2 &= iter << 2; + t3 ^= iter >>> 3; + t4 += (byte)iter; + t5 -= (char)iter; + t6 += (short)iter; + } + return t1 | t2 | t3 | t4 | t5 | t6; + } + + @Benchmark + public static int jmhTimeMultipleUsesDifferentPathInt() { + int res = 0; + for (int iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has two uses on different paths. + int temp1 = iter >> 1; + int temp2 = iter << 2; + int temp3 = iter >>> 3; + int temp4 = (byte)iter; + int temp5 = (char)iter; + int temp6 = (short)iter; + int temp7 = (int)iter; + // The condition is true most of the time, so the branch predictor should + // predict it correctly. + if (iter > 1) { + res += (((((temp1 | temp2) & temp3) ^ temp4) + temp5) - temp6) + temp7; + } else { + res += (((((temp1 + temp2) | temp3) & temp4) & temp5) + temp6) - temp7; + } + } + return res; + } + + @Benchmark + public static long jmhTimeMultipleUsesDifferentPathLong() { + long res = 0; + for (long iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has two uses on different paths. + long temp1 = iter >> 1; + long temp2 = iter << 2; + long temp3 = iter >>> 3; + long temp4 = (byte)iter; + long temp5 = (char)iter; + long temp6 = (short)iter; + long temp7 = (int)iter; + // The condition is true most of the time, so the branch predictor should + // predict it correctly. + if (iter > 1) { + res += (((((temp1 | temp2) & temp3) ^ temp4) + temp5) - temp6) + temp7; + } else { + res += (((((temp1 + temp2) | temp3) & temp4) & temp5) + temp6) - temp7; + } + } + return res; + } + + @Benchmark + public static int jmhTimeMultipleSuccessiveUsesSamePathInt() { + int res = 0; + for (int iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has two successive uses on the same path. + int temp1 = iter >> 1; + res += temp1; + res += temp1; + int temp2 = iter << 2; + res += temp2; + res += temp2; + int temp3 = iter >>> 3; + res += temp3; + res += temp3; + int temp4 = (byte)iter; + res += temp4; + res += temp4; + int temp5 = (char)iter; + res += temp5; + res += temp5; + int temp6 = (short)iter; + res += temp6; + res += temp6; + int temp7 = (int)iter; + res += temp7; + res += temp7; + } + return res; + } + + @Benchmark + public static long jmhTimeMultipleSuccessiveUsesSamePathLong() { + long res = 0; + for (long iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has two successive uses on the same path. + long temp1 = iter >> 1; + res += temp1; + res += temp1; + long temp2 = iter << 2; + res += temp2; + res += temp2; + long temp3 = iter >>> 3; + res += temp3; + res += temp3; + long temp4 = (byte)iter; + res += temp4; + res += temp4; + long temp5 = (char)iter; + res += temp5; + res += temp5; + long temp6 = (short)iter; + res += temp6; + res += temp6; + long temp7 = (int)iter; + res += temp7; + res += temp7; + } + return res; + } + + @Benchmark + public static int jmhTimeMultipleSeparatedUsesSamePathInt() { + int res = 0; + for (int iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has two separated uses on the same path. + int temp1 = iter >> 1; + res += temp1; + int temp2 = iter << 2; + res += temp2; + res += temp1; + int temp3 = iter >>> 3; + res += temp3; + res += temp2; + int temp4 = (byte)iter; + res += temp4; + res += temp3; + int temp5 = (char)iter; + res += temp5; + res += temp4; + int temp6 = (short)iter; + res += temp6; + res += temp5; + int temp7 = (int)iter; + res += temp7; + res += temp6; + res += temp7; + } + return res; + } + + @Benchmark + public static long jmhTimeMultipleSeparatedUsesSamePathLong() { + long res = 0; + for (long iter = 0; iter < LOOP_SIZE; iter++) { + // Each bitfield operation has two separated uses on the same path. + long temp1 = iter >> 1; + res += temp1; + long temp2 = iter << 2; + res += temp2; + res += temp1; + long temp3 = iter >>> 3; + res += temp3; + res += temp2; + long temp4 = (byte)iter; + res += temp4; + res += temp3; + long temp5 = (char)iter; + res += temp5; + res += temp4; + long temp6 = (short)iter; + res += temp6; + res += temp5; + long temp7 = (int)iter; + res += temp7; + res += temp6; + res += temp7; + } + return res; + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/StringEquals.java b/src/main/java/org/linaro/benchmarks/micro/StringEquals.java new file mode 100644 index 0000000..e0dbc72 --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/StringEquals.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 Linaro Limited. All rights reserved. + * + * 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. + * + */ + +/* + * Description: Tracks performance of String.equals when working with + * cache-limiting amounts of character data. Some prefetch + * hint investigations have shown a performance gain here. + * Main Focus: Comparison of many, very large (re: L1 cache) strings. + * Secondary Focus: + * + */ + +/* + * TODO: Test various sizes of strings. + * TODO: Benchmark other cases, like comparing strings of different + * sizes together (probably the most command case), or similar + * strings that have a run of equal characters to begin with. + */ + +package org.linaro.benchmarks.micro; + +import java.lang.StringBuilder; +import java.util.Random; +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) + +public class StringEquals { + private static Random rnd = new Random(0); + private static final int NUM_Equals = 1024; + private static final int STR_Length = 512; + private static String[] randomStrings = new String[NUM_Equals]; + private static Boolean[] equalsResults = new Boolean[NUM_Equals - 1]; + + @Setup + public void setup() { + generateRandomStrings(); + } + + private static void generateRandomStrings() { + for (int i = 0; i < NUM_Equals; i++) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < STR_Length; j++) { + sb.append(Character.valueOf((char)(rnd.nextInt(25) + 65))); + } + randomStrings[i] = sb.toString(); + } + } + + @Benchmark + public void jmhTimeEquals() { + for (int j = 0; j < NUM_Equals - 1; j++) { + equalsResults[j] = randomStrings[j].equals(randomStrings[j + 1]); + } + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/StringOps.java b/src/main/java/org/linaro/benchmarks/micro/StringOps.java new file mode 100644 index 0000000..5851c36 --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/StringOps.java @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2016 Linaro Limited. All rights reserved. + * + * 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. + * + */ + +/* + * Description: Tracks performance of String intrinsics, Java, and native methods. + * Main Focus: Looped memory compare. + * Secondary Focus: Array access. + * + */ + +package org.linaro.benchmarks.micro; + +import java.lang.StringBuilder; +import java.lang.System; +import java.util.Random; +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) + +public class StringOps { + + private static int RANDOM_STRING_8 = 0; + private static int RANDOM_STRING_16 = 1; + private static int RANDOM_STRING_32 = 2; + private static int RANDOM_STRING_128 = 3; + private static int RANDOM_STRING_512 = 4; + private static int NUM_LENGTH_TESTS = 5; + + private static char MIN_RANDOM_CHAR = 65; + private static char MAX_RANDOM_CHAR = 123; + private static char searchChar; + + /* Intentionally use the same seed each time for consistency across benchmark runs. */ + private static int SAME_SEED = 0; + + /* Random string data. */ + private static Random rnd = new Random(SAME_SEED); + private static String[] stringData = new String[NUM_LENGTH_TESTS]; + + /* Same random string data as above for comparing different instances of the same char data. */ + private static Random rndAlt = new Random(SAME_SEED); + private static String[] stringDataAlt = new String[NUM_LENGTH_TESTS]; + + /* Benchmark results cache for preventing DCE. */ + private static boolean[] stringEqualsResults = new boolean[NUM_LENGTH_TESTS]; + private static boolean[] stringEqualsIgnoreCaseResults = new boolean[NUM_LENGTH_TESTS]; + private static boolean[] stringContentEqualsResults = new boolean[NUM_LENGTH_TESTS]; + private static int[] stringCompareToResults = new int[NUM_LENGTH_TESTS]; + private static int[] stringCompareToIgnoreCaseResults = new int[NUM_LENGTH_TESTS]; + private static boolean[] stringRegionMatchesResults = new boolean[NUM_LENGTH_TESTS]; + private static boolean[] stringRegionMatchesIgnoreCaseResults = new boolean[NUM_LENGTH_TESTS]; + private static char stringCharAtResult; + private static int stringIndexOfResult; + private static int stringIndexOfAfterResult; + private static String[] stringNewStringFromBytesResult = new String[NUM_LENGTH_TESTS]; + private static String[] stringNewStringFromCharsResult = new String[NUM_LENGTH_TESTS]; + private static String[] stringNewStringFromStringResult = new String[NUM_LENGTH_TESTS]; + private static String[] stringGetCharsNoCheckResult = new String[NUM_LENGTH_TESTS]; + + private static char []chars_8 = new char[8]; + private static char []chars_16 = new char[16]; + private static char []chars_32 = new char[32]; + private static char []chars_128 = new char[128]; + private static char []chars_512 = new char[512]; + + private static String generateRandomString(int len, Random rnd) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < len - 1; i++) { + /* Compose random string data from upper and lower case english alphabet entries plus a few + * harmless characters in-between. */ + sb.append(Character.valueOf((char)(MIN_RANDOM_CHAR + + rnd.nextInt(MAX_RANDOM_CHAR - MIN_RANDOM_CHAR)))); + } + sb.append(Character.valueOf(MAX_RANDOM_CHAR)); + return sb.toString(); + } + + private static void generateRandomStrings(Random rnd, String[] output) { + output[RANDOM_STRING_8] = generateRandomString(8, rnd); + output[RANDOM_STRING_16] = generateRandomString(16, rnd); + output[RANDOM_STRING_32] = generateRandomString(32, rnd); + output[RANDOM_STRING_128] = generateRandomString(128, rnd); + output[RANDOM_STRING_512] = generateRandomString(512, rnd); + } + + static { + searchChar = MAX_RANDOM_CHAR; + generateRandomStrings(rnd, stringData); + generateRandomStrings(rndAlt, stringDataAlt); + } + + /** + * String.equals + */ + + @Benchmark + public void jmhTimeStringEqualsSmall() { + stringEqualsResults[RANDOM_STRING_8] + ^= stringData[RANDOM_STRING_8].equals(stringDataAlt[RANDOM_STRING_8]); + stringEqualsResults[RANDOM_STRING_16] + ^= stringData[RANDOM_STRING_16].equals(stringDataAlt[RANDOM_STRING_16]); + stringEqualsResults[RANDOM_STRING_32] + ^= stringData[RANDOM_STRING_32].equals(stringDataAlt[RANDOM_STRING_32]); + } + + @Benchmark + public void jmhTimeStringEqualsLarge() { + stringEqualsResults[RANDOM_STRING_128] + ^= stringData[RANDOM_STRING_128].equals(stringDataAlt[RANDOM_STRING_128]); + stringEqualsResults[RANDOM_STRING_512] + ^= stringData[RANDOM_STRING_512].equals(stringDataAlt[RANDOM_STRING_512]); + } + + /** + * String.equalsIgnoreCase + */ + + @Benchmark + public void jmhTimeStringEqualsIgnoreCaseSmall() { + stringEqualsIgnoreCaseResults[RANDOM_STRING_8] + ^= stringData[RANDOM_STRING_8].equalsIgnoreCase( + stringDataAlt[RANDOM_STRING_8]); + stringEqualsIgnoreCaseResults[RANDOM_STRING_16] + ^= stringData[RANDOM_STRING_16].equalsIgnoreCase( + stringDataAlt[RANDOM_STRING_16]); + stringEqualsIgnoreCaseResults[RANDOM_STRING_32] + ^= stringData[RANDOM_STRING_32].equalsIgnoreCase( + stringDataAlt[RANDOM_STRING_32]); + } + + @Benchmark + public void jmhTimeStringEqualsIgnoreCaseLarge() { + stringEqualsIgnoreCaseResults[RANDOM_STRING_128] + ^= stringData[RANDOM_STRING_128].equalsIgnoreCase( + stringDataAlt[RANDOM_STRING_128]); + stringEqualsIgnoreCaseResults[RANDOM_STRING_512] + ^= stringData[RANDOM_STRING_512].equalsIgnoreCase( + stringDataAlt[RANDOM_STRING_512]); + } + + /** + * String.contentEquals + */ + + @Benchmark + public void jmhTimeStringContentEqualsSmall() { + stringContentEqualsResults[RANDOM_STRING_8] + ^= stringData[RANDOM_STRING_8].contentEquals( + stringDataAlt[RANDOM_STRING_8]); + stringContentEqualsResults[RANDOM_STRING_16] + ^= stringData[RANDOM_STRING_16].contentEquals( + stringDataAlt[RANDOM_STRING_16]); + stringContentEqualsResults[RANDOM_STRING_32] + ^= stringData[RANDOM_STRING_32].contentEquals( + stringDataAlt[RANDOM_STRING_32]); + } + + @Benchmark + public void jmhTimeStringContentEqualsLarge() { + stringContentEqualsResults[RANDOM_STRING_128] + ^= stringData[RANDOM_STRING_128].contentEquals( + stringDataAlt[RANDOM_STRING_128]); + stringContentEqualsResults[RANDOM_STRING_512] + ^= stringData[RANDOM_STRING_512].contentEquals( + stringDataAlt[RANDOM_STRING_512]); + } + + /** + * String.compareTo + */ + + @Benchmark + public void jmhTimeStringCompareToSmall() { + stringCompareToResults[RANDOM_STRING_8] + += stringData[RANDOM_STRING_8].compareTo(stringDataAlt[RANDOM_STRING_8]); + stringCompareToResults[RANDOM_STRING_16] + += stringData[RANDOM_STRING_16].compareTo(stringDataAlt[RANDOM_STRING_16]); + stringCompareToResults[RANDOM_STRING_32] + += stringData[RANDOM_STRING_32].compareTo(stringDataAlt[RANDOM_STRING_32]); + } + + @Benchmark + public void jmhTimeStringCompareToLarge() { + stringCompareToResults[RANDOM_STRING_128] + += stringData[RANDOM_STRING_128].compareTo(stringDataAlt[RANDOM_STRING_128]); + stringCompareToResults[RANDOM_STRING_512] + += stringData[RANDOM_STRING_512].compareTo(stringDataAlt[RANDOM_STRING_512]); + } + + /** + * String.compareToIgnoreCase + */ + + @Benchmark + public void jmhTimeStringCompareToIgnoreCaseSmall() { + stringCompareToIgnoreCaseResults[RANDOM_STRING_8] + += stringData[RANDOM_STRING_8].compareToIgnoreCase( + stringDataAlt[RANDOM_STRING_8]); + stringCompareToIgnoreCaseResults[RANDOM_STRING_16] + += stringData[RANDOM_STRING_16].compareToIgnoreCase( + stringDataAlt[RANDOM_STRING_16]); + stringCompareToIgnoreCaseResults[RANDOM_STRING_32] + += stringData[RANDOM_STRING_32].compareToIgnoreCase( + stringDataAlt[RANDOM_STRING_32]); + } + + @Benchmark + public void jmhTimeStringCompareToIgnoreCaseLarge() { + stringCompareToIgnoreCaseResults[RANDOM_STRING_128] + += stringData[RANDOM_STRING_128].compareToIgnoreCase( + stringDataAlt[RANDOM_STRING_128]); + stringCompareToIgnoreCaseResults[RANDOM_STRING_512] + += stringData[RANDOM_STRING_512].compareToIgnoreCase( + stringDataAlt[RANDOM_STRING_512]); + } + + /** + * String.regionMatches + */ + + @Benchmark + public void jmhTimeStringRegionMatchesSmall() { + stringRegionMatchesResults[RANDOM_STRING_8] + ^= stringData[RANDOM_STRING_8].regionMatches( + 0, stringDataAlt[RANDOM_STRING_8], 0, 8); + stringRegionMatchesResults[RANDOM_STRING_16] + ^= stringData[RANDOM_STRING_16].regionMatches( + 0, stringDataAlt[RANDOM_STRING_16], 0, 16); + stringRegionMatchesResults[RANDOM_STRING_32] + ^= stringData[RANDOM_STRING_32].regionMatches( + 0, stringDataAlt[RANDOM_STRING_32], 0, 32); + } + + @Benchmark + public void jmhTimeStringRegionMatchesLarge() { + stringRegionMatchesResults[RANDOM_STRING_128] + ^= stringData[RANDOM_STRING_128].regionMatches( + 0, stringDataAlt[RANDOM_STRING_128], 0, 128); + stringRegionMatchesResults[RANDOM_STRING_512] + ^= stringData[RANDOM_STRING_512].regionMatches( + 0, stringDataAlt[RANDOM_STRING_512], 0, 512); + } + + /** + * String.regionMatches + */ + + @Benchmark + public void jmhTimeStringRegionMatchesIgnoreCaseSmall() { + stringRegionMatchesIgnoreCaseResults[RANDOM_STRING_8] + ^= stringData[RANDOM_STRING_8].regionMatches( + true, 0, stringDataAlt[RANDOM_STRING_8], 0, 8); + stringRegionMatchesIgnoreCaseResults[RANDOM_STRING_16] + ^= stringData[RANDOM_STRING_16].regionMatches( + true, 0, stringDataAlt[RANDOM_STRING_16], 0, 16); + stringRegionMatchesIgnoreCaseResults[RANDOM_STRING_32] + ^= stringData[RANDOM_STRING_32].regionMatches( + true, 0, stringDataAlt[RANDOM_STRING_32], 0, 32); + } + + @Benchmark + public void jmhTimeStringRegionMatchesIgnoreCaseLarge() { + stringRegionMatchesIgnoreCaseResults[RANDOM_STRING_128] + ^= stringData[RANDOM_STRING_128].regionMatches( + true, 0, stringDataAlt[RANDOM_STRING_128], 0, 128); + stringRegionMatchesIgnoreCaseResults[RANDOM_STRING_512] + ^= stringData[RANDOM_STRING_512].regionMatches( + true, 0, stringDataAlt[RANDOM_STRING_512], 0, 512); + } + + /** + * String.charAt + */ + + @Benchmark + public void jmhTimeStringCharAt() { + for (int j = 0; j < 512; j++) { + stringCharAtResult = stringData[RANDOM_STRING_512].charAt(j); + } + } + + /** + * String.indexOf + */ + + @Benchmark + public void jmhTimeStringIndexOfSmall() { + stringIndexOfResult += stringData[RANDOM_STRING_8].indexOf(searchChar); + stringIndexOfResult += stringData[RANDOM_STRING_16].indexOf(searchChar); + stringIndexOfResult += stringData[RANDOM_STRING_32].indexOf(searchChar); + } + + @Benchmark + public void jmhTimeStringIndexOfLarge() { + stringIndexOfResult += stringData[RANDOM_STRING_128].indexOf(searchChar); + stringIndexOfResult += stringData[RANDOM_STRING_512].indexOf(searchChar); + } + + /** + * String.indexOfAfter + */ + + @Benchmark + public void jmhTimeStringIndexOfAfterSmall() { + stringIndexOfAfterResult += stringData[RANDOM_STRING_8].indexOf(searchChar, 1); + stringIndexOfAfterResult += stringData[RANDOM_STRING_16].indexOf(searchChar, 1); + stringIndexOfAfterResult += stringData[RANDOM_STRING_32].indexOf(searchChar, 1); + } + + @Benchmark + public void jmhTimeStringIndexOfAfterLarge() { + stringIndexOfAfterResult += stringData[RANDOM_STRING_128].indexOf(searchChar, 1); + stringIndexOfAfterResult += stringData[RANDOM_STRING_512].indexOf(searchChar, 1); + } + + /** + * NewStringFromBytes + */ + + @Benchmark + public void jmhTimeStringNewStringFromBytesSmall() { + byte[] bytes = stringData[RANDOM_STRING_8].getBytes(); + stringNewStringFromBytesResult[RANDOM_STRING_8] = new String(bytes); + byte[] bytes2 = stringData[RANDOM_STRING_16].getBytes(); + stringNewStringFromBytesResult[RANDOM_STRING_16] = new String(bytes2); + byte[] bytes3 = stringData[RANDOM_STRING_32].getBytes(); + stringNewStringFromBytesResult[RANDOM_STRING_32] = new String(bytes3); + } + + @Benchmark + public void jmhTimeStringNewStringFromBytesLarge() { + byte[] bytes = stringData[RANDOM_STRING_128].getBytes(); + stringNewStringFromBytesResult[RANDOM_STRING_128] = new String(bytes); + byte[] bytes2 = stringData[RANDOM_STRING_512].getBytes(); + stringNewStringFromBytesResult[RANDOM_STRING_512] = new String(bytes2); + } + + /** + * NewStringFromChars + */ + + @Benchmark + public void jmhTimeStringNewStringFromCharsSmall() { + char[] chars = new char[8]; + stringData[RANDOM_STRING_8].getChars(0, 8, chars, 0); + stringNewStringFromCharsResult[RANDOM_STRING_8] = new String(chars); + char[] chars2 = new char[16]; + stringData[RANDOM_STRING_16].getChars(0, 16, chars2, 0); + stringNewStringFromCharsResult[RANDOM_STRING_16] = new String(chars2); + char[] chars3 = new char[32]; + stringData[RANDOM_STRING_32].getChars(0, 32, chars3, 0); + stringNewStringFromCharsResult[RANDOM_STRING_32] = new String(chars3); + } + + @Benchmark + public void jmhTimeStringNewStringFromCharsLarge() { + char[] chars = new char[128]; + stringData[RANDOM_STRING_128].getChars(0, 128, chars, 0); + stringNewStringFromCharsResult[RANDOM_STRING_128] = new String(chars); + char[] chars2 = new char[512]; + stringData[RANDOM_STRING_512].getChars(0, 512, chars2, 0); + stringNewStringFromCharsResult[RANDOM_STRING_512] = new String(chars2); + } + + /** + * NewStringFromString + */ + + @Benchmark + public void jmhTimeStringNewStringFromStringSmall() { + stringNewStringFromStringResult[RANDOM_STRING_8] = + new String(stringData[RANDOM_STRING_8]); + stringNewStringFromStringResult[RANDOM_STRING_16] = + new String(stringData[RANDOM_STRING_16]); + stringNewStringFromStringResult[RANDOM_STRING_32] = + new String(stringData[RANDOM_STRING_32]); + } + + @Benchmark + public void jmhTimeStringNewStringFromStringLarge() { + stringNewStringFromStringResult[RANDOM_STRING_128] = + new String(stringData[RANDOM_STRING_128]); + stringNewStringFromStringResult[RANDOM_STRING_512] = + new String(stringData[RANDOM_STRING_512]); + } + + /** + * String.getCharsNoCheck + */ + + @Benchmark + public void jmhTimeStringGetCharsNoCheckSmall() { + stringData[RANDOM_STRING_8].getChars(0, 8, chars_8, 0); + stringGetCharsNoCheckResult[RANDOM_STRING_8] = new String(chars_8); + stringData[RANDOM_STRING_16].getChars(0, 16, chars_16, 0); + stringGetCharsNoCheckResult[RANDOM_STRING_16] = new String(chars_16); + stringData[RANDOM_STRING_32].getChars(0, 32, chars_32, 0); + stringGetCharsNoCheckResult[RANDOM_STRING_32] = new String(chars_32); + } + + @Benchmark + public void jmhTimeStringGetCharsNoCheckLarge() { + stringData[RANDOM_STRING_128].getChars(0, 128, chars_128, 0); + stringGetCharsNoCheckResult[RANDOM_STRING_128] = new String(chars_128); + stringData[RANDOM_STRING_512].getChars(0, 512, chars_512, 0); + stringGetCharsNoCheckResult[RANDOM_STRING_512] = new String(chars_512); + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/SystemArrayCopy.java b/src/main/java/org/linaro/benchmarks/micro/SystemArrayCopy.java new file mode 100644 index 0000000..230bd4b --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/SystemArrayCopy.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2016 Linaro Limited. All rights reserved. + * + * 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. + * + */ + +/* + * Description: Tracks performance of System.arraycopy intrinsics. + * Main Focus: Looped load store for varying copy lengths. + * Secondary Focus: + * + */ + +package org.linaro.benchmarks.micro; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.lang.StringBuilder; +import java.lang.System; +import java.util.Random; +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) + +public class SystemArrayCopy { + + private static Random rnd = new Random(); + private static int ARRAY_COPY_SMALL = 16; + private static int ARRAY_COPY_MEDIUM = 128; + private static int ARRAY_COPY_LARGE = 1024; + private static int MAX_BUFFER_BYTES = 8192; + private static int ARRAY_LENGTH = 1024; + private static String RANDOM_STRING = generateRandomString(MAX_BUFFER_BYTES); + private static char[] cbuf = new char[MAX_BUFFER_BYTES]; + private static char arrayCopyCharBufferedReadSmallResult; + private static char arrayCopyCharBufferedReadMediumResult; + private static char arrayCopyCharBufferedReadLargeResult; + + private static String[] stringArray = new String[ARRAY_LENGTH]; + private static String[] stringArraySmall = new String[ARRAY_LENGTH]; + private static String[] stringArrayMedium = new String[ARRAY_LENGTH]; + private static String[] stringArrayLarge = new String[ARRAY_LENGTH]; + + private static String generateRandomString(int sz) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < sz; i++) { + sb.append(Character.valueOf((char)rnd.nextInt())); + } + return sb.toString(); + } + + static { + for (int i = 0; i < ARRAY_LENGTH; i++) { + stringArray[i] = String.valueOf(i); + } + } + + private void bufferedReadLoop(char[] cbuf, int copyLength) throws IOException { + BufferedReader reader = new BufferedReader(new StringReader(RANDOM_STRING)); + int offset = 0; + String s; + /* Read 16Kb RANDOM_STRING in chunks of copyLength chars until EOF */ + while (offset < MAX_BUFFER_BYTES && (reader.read(cbuf, offset, copyLength)) != -1) { + offset += copyLength; + } + } + + @Benchmark + public void jmhTimeArrayCopyCharBufferedReadSmall() throws IOException { + bufferedReadLoop(cbuf, ARRAY_COPY_SMALL); + arrayCopyCharBufferedReadSmallResult = cbuf[MAX_BUFFER_BYTES - 1]; + } + + @Benchmark + public void jmhTimeArrayCopyCharBufferedReadMedium() throws IOException { + bufferedReadLoop(cbuf, ARRAY_COPY_MEDIUM); + arrayCopyCharBufferedReadMediumResult = cbuf[MAX_BUFFER_BYTES - 1]; + } + + @Benchmark + public void jmhTimeArrayCopyCharBufferedReadLarge() throws IOException { + bufferedReadLoop(cbuf, ARRAY_COPY_LARGE); + arrayCopyCharBufferedReadLargeResult = cbuf[MAX_BUFFER_BYTES - 1]; + } + + @Benchmark + public void jmhTimeArrayCopySmall() { + System.arraycopy(stringArray, 0, stringArraySmall, 0, ARRAY_COPY_SMALL); + } + + @Benchmark + public void jmhTimeArrayCopyMedium() { + System.arraycopy(stringArray, 0, stringArrayMedium, 0, ARRAY_COPY_MEDIUM); + } + + @Benchmark + public void jmhTimeArrayCopyLarge() { + System.arraycopy(stringArray, 0, stringArrayLarge, 0, ARRAY_COPY_LARGE); + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/VectorCalc.java b/src/main/java/org/linaro/benchmarks/micro/VectorCalc.java new file mode 100644 index 0000000..0c2ad2a --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/VectorCalc.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 Linaro Limited. All rights reserved. + * + * 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. + * + */ + +/* + * Description: Some generic vector operations. + * Main Focus: Boundary checks in loops + * Secondary Focus: Array access, load/store. + * + */ + +package org.linaro.benchmarks.micro; + +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) + +public class VectorCalc { + + public static final int ARRAY_SIZE = 500; + public static final int COEFFICIENT = 2; + public static final int MAIN_ITERATIONS = 80000; + public int[] array1 = new int[ARRAY_SIZE]; + public int[] array2 = new int[ARRAY_SIZE]; + public int[] array3 = new int[ARRAY_SIZE]; + + // index : loop induction variable + loop invariable + public void setArrayValues(int[] array, int start, int length, int startValue, int step) { + for (int i = 0, value = startValue; i < length; ++i, value += step) { + array[i + start] = value; + } + } + + // index : loop induction variable + // multiple arrays + public void copyArray(int[] dst, int[] src, int start, int length) { + for (int i = start, end = start + length; i < end; ++i) { + dst[i] = src[i]; + } + } + + // index : loop induction variable + loop invariable + // multiple arrays + public void scaleProduct(int[] dst, int[] src, int start, int length, int coefficient) { + for (int i = 0; i < length; ++i) { + dst[i + start] = coefficient * src[i + start]; + } + } + + // index-1 : loop induction variable + // index-2 : loop invariable + // multiple arrays + public void dotProduct(int[] src1, int[] src2, int start, int length, int[] output, + int outputOffset) { + for (int i = start, end = start + length; i < end; ++i) { + output[outputOffset] += src1[i] * src2[i]; + } + } + + @Benchmark + public void jmhTimeRun() { + setArrayValues(array1, 0, ARRAY_SIZE, 0, 1); + copyArray(array2, array1, 0, ARRAY_SIZE); + scaleProduct(array3, array1, 0, ARRAY_SIZE, COEFFICIENT); + dotProduct(array2, array3, 0, ARRAY_SIZE, array1, 0); + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/intrinsics/CRC32Bench.java b/src/main/java/org/linaro/benchmarks/micro/intrinsics/CRC32Bench.java new file mode 100644 index 0000000..3d8d817 --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/intrinsics/CRC32Bench.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 Linaro Limited. All rights reserved. + * + * 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. + * + */ + +/* + * Description: Simple loops around CRC32 intrinsics. + * Main Focus: CRC32-related intrinsics. + */ + +package org.linaro.benchmarks.micro.intrinsics; + +import java.util.Random; +import java.util.zip.CRC32; +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) + +public class CRC32Bench { + private byte []bytes = new byte[8192]; + private CRC32 crc32 = new CRC32(); + private static final int iterCount = 100; + private static final int loopSize = 3000; + + public CRC32Bench() { + Random rnd = new Random(0); + rnd.nextBytes(bytes); + } + + @Benchmark + public void jmhTimeUpdateInt() { + benchUpdateInt(); + } + + @Benchmark + public void jmhTimeUpdateBytes() { + benchUpdateBytes(); + } + + public void benchUpdateInt() { + int count = loopSize * 230; + crc32.reset(); + for (int i = 0; i < count; i++) { + crc32.update(i); + } + } + + public void benchUpdateBytes() { + crc32.reset(); + + for (int i = 0; i < loopSize*100; i++) { + crc32.update(bytes); + } + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/intrinsics/Intrinsics.java b/src/main/java/org/linaro/benchmarks/micro/intrinsics/Intrinsics.java new file mode 100644 index 0000000..f43d977 --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/intrinsics/Intrinsics.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2015, ARM Limited. All rights reserved. + * + * 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. + * + */ + +package org.linaro.benchmarks.micro.intrinsics; + +import java.lang.System; +import java.util.EnumMap; +import java.util.Map; +import java.util.Random; +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) + +public class Intrinsics { + + /* Invoke each intrinsic in question the same no. of times */ + private static final int NUM_INVOKES = 128; + + /* Random pool size. + * Use a power of two to make the modulo operations below fast. */ + private static final int NUM_RANDS = 1024; + + /* Pre-allocated pool of random integers from [0, Integer.MAX_VALUE) */ + private static final int[] rand = new int[NUM_RANDS]; + + static { + // Allocate a pool of random integers to use in benchmarks that + // want to try and force branch mispredicts without the overhead + // of calling random.nextInt in times code. Same seed every time. + Random random = new Random(42L); + for (int i = 0; i < NUM_RANDS; i++) { + rand[i] = random.nextInt(Integer.MAX_VALUE); + } + } + + /** + * NumberOfLeadingZeros. + **/ + + private static int[] resultsNumberOfLeadingZerosInteger = new int[NUM_INVOKES]; + private static int[] resultsNumberOfLeadingZerosLong = new int[NUM_INVOKES]; + private static int[] resultsNumberOfLeadingZerosIntegerRandom = new int[NUM_INVOKES]; + private static int[] resultsNumberOfLeadingZerosLongRandom = new int[NUM_INVOKES]; + + @Benchmark + public void jmhTimeNumberOfLeadingZerosInteger() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsNumberOfLeadingZerosInteger[i] = + Integer.numberOfLeadingZeros(0x80000000 >>> i); + } + } + + @Benchmark + public void jmhTimeNumberOfLeadingZerosLong() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsNumberOfLeadingZerosLong[i] = + Long.numberOfLeadingZeros(0x8000000000000000L >>> i); + } + } + + @Benchmark + public void jmhTimeNumberOfLeadingZerosIntegerRandom() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsNumberOfLeadingZerosIntegerRandom[i] = + Integer.numberOfLeadingZeros(rand[i % NUM_RANDS] >>> rand[i % NUM_RANDS]); + } + } + + @Benchmark + public void jmhTimeNumberOfLeadingZerosLongRandom() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsNumberOfLeadingZerosLongRandom[i] = + Long.numberOfLeadingZeros(1 << (rand[i % NUM_RANDS] % Long.SIZE)); + } + } + + /** + * NumberOfTrailingZeros. + **/ + + private static int[] resultsNumberOfTrailingZerosInteger = new int[NUM_INVOKES]; + private static int[] resultsNumberOfTrailingZerosLong = new int[NUM_INVOKES]; + private static int[] resultsNumberOfTrailingZerosIntegerRandom = new int[NUM_INVOKES]; + private static int[] resultsNumberOfTrailingZerosLongRandom = new int[NUM_INVOKES]; + + @Benchmark + public void jmhTimeNumberOfTrailingZerosInteger() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsNumberOfTrailingZerosInteger[i] = + Integer.numberOfTrailingZeros(0x80000000 >>> i); + } + } + + @Benchmark + public void jmhTimeNumberOfTrailingZerosLong() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsNumberOfTrailingZerosLong[i] = + Long.numberOfTrailingZeros(0x8000000000000000L >>> i); + } + } + + @Benchmark + public void jmhTimeNumberOfTrailingZerosIntegerRandom() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsNumberOfTrailingZerosIntegerRandom[i] = + Integer.numberOfTrailingZeros(rand[i % NUM_RANDS] >>> rand[i % NUM_RANDS]); + } + } + + @Benchmark + public void jmhTimeNumberOfTrailingZerosLongRandom() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsNumberOfTrailingZerosLongRandom[i] = + Long.numberOfTrailingZeros(1 << (rand[i % NUM_RANDS] % Long.SIZE)); + } + } + + /** + * BitCount. + **/ + + // both Integer and Long's bitCount() return int type. + private static int[] resultsBitCountInteger = new int[NUM_INVOKES]; + private static int[] resultsBitCountLong = new int[NUM_INVOKES]; + private static int[] resultsBitCountIntegerRandom = new int[NUM_INVOKES]; + private static int[] resultsBitCountLongRandom = new int[NUM_INVOKES]; + + @Benchmark + public void jmhTimeBitCountInteger() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsBitCountInteger[i] = Integer.bitCount(0x1234abcd); + } + } + + @Benchmark + public void jmhTimeBitCountLong() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsBitCountLong[i] = Long.bitCount(0x1234abcd1234abcdL); + } + } + + @Benchmark + public void jmhTimeBitCountIntegerRandom() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsBitCountIntegerRandom[i] = Integer.bitCount(rand[i % NUM_RANDS]); + } + } + + @Benchmark + public void jmhTimeBitCountLongRandom() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsBitCountLongRandom[i] = + Long.bitCount((long)rand[i % NUM_RANDS] << 32 + rand[i % NUM_RANDS]); + } + } + + /** + * RotateRight. + **/ + + private static int[] resultsRotateRightInteger = new int[NUM_INVOKES]; + private static int[] resultsRotateRightIntegerConstant = new int[NUM_INVOKES]; + private static long[] resultsRotateRightLong = new long[NUM_INVOKES]; + private static long[] resultsRotateRightLongConstant = new long[NUM_INVOKES]; + + @Benchmark + public void jmhTimeRotateRightInteger() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsRotateRightInteger[i] = Integer.rotateRight(0xFF0000DD, i); + } + } + + @Benchmark + public void jmhTimeRotateRightIntegerConstant() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsRotateRightIntegerConstant[i] = Integer.rotateRight(0xFF0000DD, 16); + } + } + + @Benchmark + public void jmhTimeRotateRightLong() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsRotateRightLong[i] = Long.rotateRight(0xBBAAAADDFF0000DDL, i); + } + } + + @Benchmark + public void jmhTimeRotateRightLongConstant() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsRotateRightLongConstant[i] = Long.rotateRight(0xBBAAAADDFF0000DDL, 48); + } + } + + /** + * RotateLeft. + **/ + + private static int[] resultsRotateLeftInteger = new int[NUM_INVOKES]; + private static int[] resultsRotateLeftIntegerConstant = new int[NUM_INVOKES]; + private static long[] resultsRotateLeftLong = new long[NUM_INVOKES]; + private static long[] resultsRotateLeftLongConstant = new long[NUM_INVOKES]; + + @Benchmark + public void jmhTimeRotateLeftInteger() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsRotateLeftInteger[i] = Integer.rotateLeft(0xFF0000DD, i); + } + } + + @Benchmark + public void jmhTimeRotateLeftIntegerConstant() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsRotateLeftIntegerConstant[i] = Integer.rotateLeft(0xFF0000DD, 16); + } + } + + @Benchmark + public void jmhTimeRotateLeftLong() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsRotateLeftLong[i] = Long.rotateLeft(0xBBAAAADDFF0000DDL, i); + } + } + + @Benchmark + public void jmhTimeRotateLeftLongConstant() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsRotateLeftLongConstant[i] = Long.rotateLeft(0xBBAAAADDFF0000DDL, 48); + } + } + + /** + * RotateRandom. + **/ + + private static int[] resultsRotateRandomInteger = new int[NUM_INVOKES]; + private static long[] resultsRotateRandomLong = new long[NUM_INVOKES]; + + @Benchmark + public void jmhTimeRotateRandomInteger() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsRotateRandomInteger[i] = (rand[i % NUM_RANDS] % 2 > 0) + ? Integer.rotateLeft(0xFF0000DD, rand[i % NUM_RANDS]) + : Integer.rotateRight(0xFF0000DD, rand[i % NUM_RANDS]); + } + } + + @Benchmark + public void jmhTimeRotateRandomLong() { + for (int i = 0; i < NUM_INVOKES; ++i) { + resultsRotateRandomLong[i] = (rand[i % NUM_RANDS] % 2 > 0) + ? Long.rotateLeft(0xBBAAAADDFF0000DDL, rand[i % NUM_RANDS]) + : Long.rotateRight(0xBBAAAADDFF0000DDL, rand[i % NUM_RANDS]); + } + } +} diff --git a/src/main/java/org/linaro/benchmarks/micro/intrinsics/MathIntrinsics.java b/src/main/java/org/linaro/benchmarks/micro/intrinsics/MathIntrinsics.java new file mode 100644 index 0000000..a1a86a6 --- /dev/null +++ b/src/main/java/org/linaro/benchmarks/micro/intrinsics/MathIntrinsics.java @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2016, Linaro Limited. All rights reserved. + * + * 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. + * + */ + +/* + * Description: Simple loops around math-related intrinsics: rounding, min/max, etc. + * Main Focus: Math-related intrinsics. + */ + +package org.linaro.benchmarks.micro.intrinsics; + +import java.lang.System; +import java.util.Random; +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) + +public class MathIntrinsics { + // The java.util.Random only generates random float/double numbers between 0 and 1. + // So we implement our own random floating point numbers here, + // based on a well known quick and dirty approach. + static final class MyRandom extends Random { + static int im = 139968; + static int ia = 3877; + static int ic = 29573; + static int seed = 0; + + public double nextDouble() { + double scale = 1000.0d / im; + seed = (seed * ia + ic) % im; + return scale * seed; + } + + public float nextFloat() { + float scale = 1000.0f / im; + seed = (seed * ia + ic) % im; + return scale * seed; + } + } + + /* Invoke each intrinsic in question the same no. of times */ + private static final int NUM_INVOKES = 64; + + /* Random pool size. + * Use a power of two to make the modulo operations below fast. */ + private static final int NUM_RANDS = 1024; + + /* Pre-allocated pool of random numbers. */ + private static final float[] rand_float = new float[NUM_RANDS]; + private static final double[] rand_double = new double[NUM_RANDS]; + + private static final float[] rand_pos_float = new float[NUM_RANDS]; + private static final double[] rand_pos_double = new double[NUM_RANDS]; + + private static final float[] rand_neg_float = new float[NUM_RANDS]; + private static final double[] rand_neg_double = new double[NUM_RANDS]; + + private static final int[] rand_int = new int[NUM_RANDS]; + private static final long[] rand_long = new long[NUM_RANDS]; + + // These are written but not read. The computation routines below store their + // result to these static variables to ensure the computation code is not + // removed by DCE. + private static float res_float; + private static double res_double; + private static int res_int; + private static long res_long; + + static { + MyRandom rand = new MyRandom(); + + for (int i = 0; i < NUM_RANDS; i++) { + rand_int[i] = rand.nextInt(); + rand_long[i] = rand.nextLong(); + + rand_pos_float[i] = rand.nextFloat(); + rand_pos_double[i] = rand.nextDouble(); + + rand_neg_float[i] = rand.nextFloat() * -1f; + rand_neg_double[i] = rand.nextDouble() * -1f; + + if (rand.nextInt() % 2 == 0) { + rand_float[i] = rand_pos_float[i]; + rand_double[i] = rand_pos_double[i]; + } else { + rand_float[i] = rand_neg_float[i]; + rand_double[i] = rand_neg_double[i]; + } + } + } + + @Benchmark + public void jmhTimeRoundPositiveFloat() { + int res = 0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.round(rand_pos_float[i % NUM_RANDS]); + } + res_int = res; + } + + @Benchmark + public void jmhTimeRoundNegativeFloat() { + int res = 0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.round(rand_neg_float[i % NUM_RANDS]); + } + res_int = res; + } + + @Benchmark + public void jmhTimeRoundFloat() { + int res = 0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.round(rand_float[i % NUM_RANDS]); + } + res_int = res; + } + + @Benchmark + public void jmhTimeRoundPositiveDouble() { + long res = 0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.round(rand_pos_double[i % NUM_RANDS]); + } + res_double = res; + } + + @Benchmark + public void jmhTimeRoundNegativeDouble() { + long res = 0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.round(rand_neg_double[i % NUM_RANDS]); + } + res_long = res; + } + + @Benchmark + public void jmhTimeRoundDouble() { + long res = 0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.round(rand_double[i % NUM_RANDS]); + } + res_double = res; + } + + @Benchmark + public void jmhTimeFloorPositiveFloat() { + float res = 0.0f; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.floor(rand_pos_float[i % NUM_RANDS]); + } + res_float = res; + } + + @Benchmark + public void jmhTimeFloorNegativeFloat() { + float res = 0.0f; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.floor(rand_neg_float[i % NUM_RANDS]); + } + res_float = res; + } + + @Benchmark + public void jmhTimeFloorFloat() { + float res = 0.0f; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.floor(rand_float[i % NUM_RANDS]); + } + res_float = res; + } + + @Benchmark + public void jmhTimeFloorPositiveDouble() { + double res = 0.0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.floor(rand_pos_double[i % NUM_RANDS]); + } + res_double = res; + } + + @Benchmark + public void jmhTimeFloorNegativeDouble() { + double res = 0.0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.floor(rand_neg_double[i % NUM_RANDS]); + } + res_double = res; + } + + @Benchmark + public void jmhTimeFloorDouble() { + double res = 0.0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.floor(rand_double[i % NUM_RANDS]); + } + res_double = res; + } + + @Benchmark + public void jmhTimeCeilPositiveFloat() { + float res = 0.0f; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.ceil(rand_pos_float[i % NUM_RANDS]); + } + res_float = res; + } + + @Benchmark + public void jmhTimeCeilNegativeFloat() { + float res = 0.0f; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.ceil(rand_neg_float[i % NUM_RANDS]); + } + res_float = res; + } + + @Benchmark + public void jmhTimeCeilFloat() { + float res = 0.0f; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.ceil(rand_float[i % NUM_RANDS]); + } + res_float = res; + } + + @Benchmark + public void jmhTimeCeilPositiveDouble() { + double res = 0.0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.ceil(rand_pos_double[i % NUM_RANDS]); + } + res_double = res; + } + + + @Benchmark + public void jmhTimeCeilNegativeDouble() { + double res = 0.0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.ceil(rand_neg_double[i % NUM_RANDS]); + } + res_double = res; + } + + @Benchmark + public void jmhTimeCeilDouble() { + double res = 0.0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.ceil(rand_double[i % NUM_RANDS]); + } + res_double = res; + } + + @Benchmark + public void jmhTimeRint() { + double res = 0.0; + for (int i = 0; i < NUM_INVOKES; ++i) { + res += Math.rint(rand_double[i % NUM_RANDS]); + } + res_double = res; + } + + @Benchmark + public void jmhTimeMinDouble() { + double res = 0.0; + for (int i = 0; i < NUM_INVOKES; ++i) { + double a = rand_double[i % NUM_RANDS]; + double b = rand_double[(i + 1) % NUM_RANDS]; + res += Math.min(a, b); + } + res_double = res; + } + + @Benchmark + public void jmhTimeMaxDouble() { + double res = 0.0; + for (int i = 0; i < NUM_INVOKES; ++i) { + double a = rand_double[i % NUM_RANDS]; + double b = rand_double[(i + 1) % NUM_RANDS]; + res += Math.max(a, b); + } + res_double = res; + } + + @Benchmark + public void jmhTimeMinFloat() { + float res = 0.0f; + for (int i = 0; i < NUM_INVOKES; ++i) { + float a = rand_float[i % NUM_RANDS]; + float b = rand_float[(i + 1) % NUM_RANDS]; + res += Math.min(a, b); + } + res_float = res; + } + + @Benchmark + public void jmhTimeMaxFloat() { + float res = 0.0f; + for (int i = 0; i < NUM_INVOKES; ++i) { + float a = rand_float[i % NUM_RANDS]; + float b = rand_float[(i + 1) % NUM_RANDS]; + res += Math.max(a, b); + } + res_float = res; + } + + @Benchmark + public void jmhTimeMinLong() { + long res = 0; + for (int i = 0; i < NUM_INVOKES; ++i) { + long a = rand_long[i % NUM_RANDS]; + long b = rand_long[(i + 1) % NUM_RANDS]; + res += Math.min(a, b); + } + res_long = res; + } + + @Benchmark + public void jmhTimeMaxLong() { + long res = 0; + for (int i = 0; i < NUM_INVOKES; ++i) { + long a = rand_long[i % NUM_RANDS]; + long b = rand_long[(i + 1) % NUM_RANDS]; + res += Math.max(a, b); + } + res_long = res; + } + + @Benchmark + public void jmhTimeMinInt() { + int res = 0; + for (int i = 0; i < NUM_INVOKES; ++i) { + int a = rand_int[i % NUM_RANDS]; + int b = rand_int[(i + 1) % NUM_RANDS]; + res += Math.min(a, b); + } + res_int = res; + } + + @Benchmark + public void jmhTimeMaxInt() { + int res = 0; + for (int i = 0; i < NUM_INVOKES; ++i) { + int a = rand_int[i % NUM_RANDS]; + int b = rand_int[(i + 1) % NUM_RANDS]; + res += Math.max(a, b); + } + res_int = res; + } +} |