|  | /* | 
|  | * Copyright (C) 2020 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | package benchmarks; | 
|  |  | 
|  | import java.math.BigInteger; | 
|  |  | 
|  | /** | 
|  | * Tries to measure important BigInteger operations across a variety of BigInteger sizes. | 
|  | * Note that BigInteger implementations commonly need to use wildly different algorithms | 
|  | * for different sizes, so relative performance may change substantially depending on the | 
|  | * size of the integer. | 
|  | * This is not structured as a proper benchmark; just run main(), e.g. with | 
|  | * vogar libcore/benchmarks/src/benchmarks/BigIntegerBenchmark.java. | 
|  | */ | 
|  | public class BigIntegerBenchmark { | 
|  | private static final boolean PRINT_TIMES = true; | 
|  |  | 
|  | private static long getStartTime() { | 
|  | if (PRINT_TIMES) { | 
|  | return System.nanoTime(); | 
|  | } else { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void printTime(String s, long startTime, int reps) { | 
|  | if (PRINT_TIMES) { | 
|  | System.out.println(s | 
|  | + (double)(System.nanoTime() - startTime) / 1000.0 / reps + " usecs / iter"); | 
|  | } | 
|  | } | 
|  |  | 
|  | // A simple sum of products computation, mostly so we can check timing in the | 
|  | // absence of any division. Computes the sum from 1 to n of ((10^prec) << 30) + 1)^2, | 
|  | // repeating the multiplication, but not addition of 1, each time through the loop. | 
|  | // Check the last few bits of the result as we go. Assumes n < 2^30. | 
|  | // Note that we're actually squaring values in computing the product. | 
|  | // That affects the algorithm used by some implementations. | 
|  | private static void inner(int n, int prec) { | 
|  | BigInteger big = BigInteger.TEN.pow(prec).shiftLeft(30).add(BigInteger.ONE); | 
|  | BigInteger sum = BigInteger.ZERO; | 
|  | for (int i = 0; i < n; ++i) { | 
|  | sum = sum.add(big.multiply(big)); | 
|  | } | 
|  | if (sum.and(BigInteger.valueOf(0x3fffffff)).intValue() != n) { | 
|  | System.out.println("inner() got " + sum.and(BigInteger.valueOf(0x3fffffff)) | 
|  | + " instead of " + n); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Execute the above rep times, optionally timing it. | 
|  | private static void repeatInner(int n, int prec, int rep) { | 
|  | long startTime = getStartTime(); | 
|  | for (int i = 0; i < rep; ++i) { | 
|  | inner(n, prec); | 
|  | } | 
|  | printTime("inner(" + n + "," + prec + ") took ", startTime, rep); | 
|  | } | 
|  |  | 
|  | // Approximate the sum of the first 1000 terms of the harmonic series (sum of 1/m as m | 
|  | // goes from 1 to n) to about prec digits. The result has an implicit decimal point | 
|  | // prec digits from the right. | 
|  | private static BigInteger harmonic1000(int prec) { | 
|  | BigInteger scaledOne = BigInteger.TEN.pow(prec); | 
|  | BigInteger sum = BigInteger.ZERO; | 
|  | for (int i = 1; i <= 1000; ++i) { | 
|  | sum = sum.add(scaledOne.divide(BigInteger.valueOf(i))); | 
|  | } | 
|  | return sum; | 
|  | } | 
|  |  | 
|  | // Execute the above rep times, optionally timing it. | 
|  | // Check results for equality, and print one, to compaare against reference. | 
|  | private static void repeatHarmonic1000(int prec, int rep) { | 
|  | long startTime = getStartTime(); | 
|  | BigInteger refRes = harmonic1000(prec); | 
|  | for (int i = 1; i < rep; ++i) { | 
|  | BigInteger newRes = harmonic1000(prec); | 
|  | if (!newRes.equals(refRes)) { | 
|  | throw new AssertionError(newRes + " != " + refRes); | 
|  | } | 
|  | } | 
|  | printTime("harmonic(1000) to " + prec + " digits took ", startTime, rep); | 
|  | if (prec >= 50 && !refRes.toString() | 
|  | .startsWith("748547086055034491265651820433390017652167916970")) { | 
|  | throw new AssertionError("harmanic(" + prec + ") incorrectly produced " + refRes); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Repeatedly execute just the base conversion from the last test, allowing | 
|  | // us to time and check it for consistency as well. | 
|  | private static void repeatToString(int prec, int rep) { | 
|  | BigInteger refRes = harmonic1000(prec); | 
|  | long startTime = getStartTime(); | 
|  | String refString = refRes.toString(); | 
|  | for (int i = 1; i < rep; ++i) { | 
|  | // Disguise refRes to avoid compiler optimization issues. | 
|  | BigInteger newRes = refRes.shiftLeft(30).add(BigInteger.valueOf(i)).shiftRight(30); | 
|  | // The time-consuming part: | 
|  | String newString = newRes.toString(); | 
|  | if (!newString.equals(refString)) { | 
|  | System.out.println(newString + " != " + refString); | 
|  | } | 
|  | } | 
|  | printTime("toString(" + prec + ") took ", startTime, rep); | 
|  | } | 
|  |  | 
|  | // Compute base^exp, where base and result are scaled/multiplied by scaleBy to make them | 
|  | // integers. exp >= 0 . | 
|  | private static BigInteger myPow(BigInteger base, int exp, BigInteger scaleBy) { | 
|  | if (exp == 0) { | 
|  | return scaleBy; // Return one. | 
|  | } else if ((exp & 1) != 0) { | 
|  | BigInteger tmp = myPow(base, exp - 1, scaleBy); | 
|  | return tmp.multiply(base).divide(scaleBy); | 
|  | } else { | 
|  | BigInteger tmp = myPow(base, exp / 2, scaleBy); | 
|  | return tmp.multiply(tmp).divide(scaleBy); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Approximate e by computing (1 + 1/n)^n to prec decimal digits. | 
|  | // This isn't necessarily a very good approximation to e. | 
|  | // Return the result, scaled by 10^prec. | 
|  | private static BigInteger eApprox(int n, int prec) { | 
|  | BigInteger scaledOne = BigInteger.TEN.pow(prec); | 
|  | BigInteger base = scaledOne.add(scaledOne.divide(BigInteger.valueOf(n))); | 
|  | return myPow(base, n, scaledOne); | 
|  | } | 
|  |  | 
|  | // Repeatedly execute and check the above, printing one of the results | 
|  | // to compare to reference. | 
|  | private static void repeatEApprox(int n, int prec, int rep) { | 
|  | long startTime = getStartTime(); | 
|  | BigInteger refRes = eApprox(n, prec); | 
|  | for (int i = 1; i < rep; ++i) { | 
|  | BigInteger newRes = eApprox(n, prec); | 
|  | if (!newRes.equals(refRes)) { | 
|  | throw new AssertionError(newRes + " != " + refRes); | 
|  | } | 
|  | } | 
|  | printTime("eApprox(" + n + "," + prec + ") took ", startTime, rep); | 
|  | if (n >= 100000 && prec >= 10 && !refRes.toString().startsWith("271826")) { | 
|  | throw new AssertionError("eApprox(" + n + "," + prec + ") incorrectly produced " | 
|  | + refRes); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test / time modPow() | 
|  | private static void repeatModPow(int len, int rep) { | 
|  | BigInteger odd1 = BigInteger.TEN.pow(len / 2).add(BigInteger.ONE); | 
|  | BigInteger odd2 = BigInteger.TEN.pow(len / 2).add(BigInteger.valueOf(17)); | 
|  | BigInteger product = odd1.multiply(odd2); | 
|  | BigInteger exponent = BigInteger.TEN.pow(len / 2 - 1); | 
|  | BigInteger base = BigInteger.TEN.pow(len / 4); | 
|  | long startTime = getStartTime(); | 
|  | BigInteger lastRes = null; | 
|  | for (int i = 0; i < rep; ++i) { | 
|  | BigInteger newRes = base.modPow(exponent, product); | 
|  | if (i != 0 && !newRes.equals(lastRes)) { | 
|  | System.out.println(newRes + " != " + lastRes); | 
|  | } | 
|  | lastRes = newRes; | 
|  | } | 
|  | printTime("ModPow() at decimal length " + len + " took ", startTime, rep); | 
|  | if (!lastRes.mod(odd1).equals(base.modPow(exponent, odd1))) { | 
|  | throw new AssertionError("ModPow() result incorrect mod odd1:" + odd1 | 
|  | + "; lastRes.mod(odd1)=" + lastRes.mod(odd1) + " vs. " | 
|  | + "base.modPow(exponent, odd1)=" + base.modPow(exponent, odd1) + " base=" | 
|  | + base + " exponent=" + exponent); | 
|  | } | 
|  | if (!lastRes.mod(odd2).equals(base.modPow(exponent, odd2))) { | 
|  | throw new AssertionError("ModPow() result incorrect mod odd2"); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test / time modInverse() | 
|  | private static void repeatModInverse(int len, int rep) { | 
|  | BigInteger odd1 = BigInteger.TEN.pow(len / 2).add(BigInteger.ONE); | 
|  | BigInteger odd2 = BigInteger.TEN.pow(len / 2).add(BigInteger.valueOf(17)); | 
|  | BigInteger product = odd1.multiply(odd2); | 
|  | BigInteger arg = BigInteger.ONE.shiftLeft(len / 4); | 
|  | long startTime = getStartTime(); | 
|  | BigInteger lastRes = null; | 
|  | for (int i = 0; i < rep; ++i) { | 
|  | BigInteger newRes = arg.modInverse(product); | 
|  | if (i != 0 && !newRes.equals(lastRes)) { | 
|  | System.out.println(newRes + " != " + lastRes); | 
|  | } | 
|  | lastRes = newRes; | 
|  | } | 
|  | printTime("ModInverse() at decimal length " + len + " took ", startTime, rep); | 
|  | if (!lastRes.mod(odd1).equals(arg.modInverse(odd1))) { | 
|  | throw new AssertionError("ModInverse() result incorrect mod odd1"); | 
|  | } | 
|  | if (!lastRes.mod(odd2).equals(arg.modInverse(odd2))) { | 
|  | throw new AssertionError("ModInverse() result incorrect mod odd2"); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static void main(String[] args) throws Exception { | 
|  | for (int i = 10; i <= 10_000; i *= 10) { | 
|  | repeatInner(1000, i, PRINT_TIMES ? Math.min(20_000 / i, 3_000) : 2); | 
|  | } | 
|  | for (int i = 5; i <= 5_000; i *= 10) { | 
|  | repeatHarmonic1000(i, PRINT_TIMES ? Math.min(20_000 / i, 3_000) : 2); | 
|  | } | 
|  | for (int i = 5; i <= 5_000; i *= 10) { | 
|  | repeatToString(i, PRINT_TIMES ? Math.min(20_000 / i, 3_000) : 2); | 
|  | } | 
|  | for (int i = 10; i <= 10_000; i *= 10) { | 
|  | repeatEApprox(100_000, i, PRINT_TIMES ? 50_000 / i : 2); | 
|  | } | 
|  | for (int i = 5; i <= 5_000; i *= 10) { | 
|  | repeatModPow(i, PRINT_TIMES ? 10_000 / i : 2); | 
|  | } | 
|  | for (int i = 10; i <= 10_000; i *= 10) { | 
|  | repeatModInverse(i, PRINT_TIMES ? 20_000 / i : 2); | 
|  | } | 
|  | } | 
|  | } |