diff --git a/samples/long-benchmark/build.gradle.kts b/samples/long-benchmark/build.gradle.kts new file mode 100644 index 000000000..885bef61d --- /dev/null +++ b/samples/long-benchmark/build.gradle.kts @@ -0,0 +1,49 @@ +/* + * Copyright 2023 Bernd Busse. + * + * 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. + */ + +plugins { + java + war + id("org.teavm") +} + +configurations { + create("war") +} + +dependencies { + teavm(teavm.libs.jsoApis) + "war"(project(":stdout-helper", "war")) +} + +teavm.js { + addedToWebApp = true + obfuscated = false + sourceMap = true + mainClass = "org.teavm.samples.long_benchmark.BenchRunner" +} + +tasks.war { + dependsOn(configurations["war"]) + from(provider { configurations["war"].map { zipTree(it) } }) +} + +afterEvaluate { + tasks.withType(JavaCompile::class) { + options.compilerArgs.add("-Xlint:unchecked") + options.compilerArgs.add("-Xlint:deprecation") + } +} diff --git a/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/BenchRunner.java b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/BenchRunner.java new file mode 100644 index 000000000..03ae2e04b --- /dev/null +++ b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/BenchRunner.java @@ -0,0 +1,280 @@ +/* + * Copyright 2023 Bernd Busse. + * + * 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.teavm.samples.long_benchmark; + +import java.lang.reflect.Array; +import java.math.BigInteger; +import java.util.Random; +import java.util.function.*; +import org.teavm.samples.long_benchmark.Pairs.*; + + +public final class BenchRunner { + + private static long start = System.currentTimeMillis(); + private static final int RUNS = 100; + + private static final double longFactor = (double) 0x8000000000000000L; + + private static Random rnd = new Random(); + + private BenchRunner() { + } + + public static void main(String[] args) throws InterruptedException { + report("Start main thread"); + report(""); + + runBenchmark(); + + report("Finished main thread"); + report(""); + } + + public static void report(String message) { + var current = System.currentTimeMillis() - start; + System.out.println("[" + Thread.currentThread().getName() + "]/" + current + ": " + message); + } + + private static void runBenchmark() { + IntSupplier getInt = () -> (int) (Math.random() * 0x80000000); + LongSupplier getLong = () -> (long) (Math.random() * longFactor); + DoubleSupplier getDouble = () -> Math.random() * 100.0; + Supplier getBigInt = () -> new BigInteger(512, rnd); + Supplier> getBigIntWithSmallerBigInt = () -> { + return new Pair(getBigInt.get(), new BigInteger(256, rnd)); + }; + + IntFunction getDoublesAsString = size -> { + String[] arr = new String[size]; + for (int i = 0; i < size; ++i) { + arr[i] = Double.toString(getDouble.getAsDouble()); + } + return arr; + }; + IntFunction getBigIntsAsString = size -> { + String[] arr = new String[size]; + for (int i = 0; i < size; ++i) { + arr[i] = getBigInt.get().toString(); + } + return arr; + }; + + IntFunction getLongPairs = size -> { + LongPair[] arr = new LongPair[size]; + for (int i = 0; i < size; ++i) { + arr[i] = new LongPair(getLong.getAsLong(), getLong.getAsLong()); + } + return arr; + }; + IntFunction getLongsWithShift = size -> { + LongIntPair[] arr = new LongIntPair[size]; + for (int i = 0; i < size; ++i) { + arr[i] = new LongIntPair(getLong.getAsLong(), (int) (Math.random() * 64.0)); + } + return arr; + }; + + IntFunction getBigInts = size -> { + BigInteger[] arr = new BigInteger[size]; + for (int i = 0; i < size; ++i) { + arr[i] = getBigInt.get(); + } + return arr; + }; + + @SuppressWarnings("unchecked") + final var bigIntPairClass = (Class>) (Class) Pair.class; + @SuppressWarnings("unchecked") + final var bigIntPairWithIntClass = (Class>) (Class) PairWithInt.class; + @SuppressWarnings("unchecked") + final var bigIntTripleClass = (Class>) (Class) Triple.class; + + IntFunction[]> getBigIntPairs = size -> { + @SuppressWarnings("unchecked") + Pair[] arr = (Pair[]) + Array.newInstance(bigIntPairClass, size); + for (int i = 0; i < size; ++i) { + arr[i] = new Pair(getBigInt.get(), getBigInt.get()); + } + return arr; + }; + IntFunction[]> getBigIntsWithSmallerBigInt = size -> { + @SuppressWarnings("unchecked") + Pair[] arr = (Pair[]) + Array.newInstance(bigIntPairClass, size); + for (int i = 0; i < size; ++i) { + arr[i] = getBigIntWithSmallerBigInt.get(); + } + return arr; + }; + IntFunction[]> getBigIntsWithExp = size -> { + @SuppressWarnings("unchecked") + PairWithInt[] arr = (PairWithInt[]) + Array.newInstance(bigIntPairWithIntClass, size); + for (int i = 0; i < size; ++i) { + arr[i] = new PairWithInt(getBigInt.get(), (int) (Math.random() * 16.0)); + } + return arr; + }; + IntFunction[]> getBigIntsWithPrime = size -> { + @SuppressWarnings("unchecked") + Pair[] arr = (Pair[]) + Array.newInstance(bigIntPairClass, size); + for (int i = 0; i < size; ++i) { + arr[i] = new Pair(getBigInt.get(), new BigInteger(512, 64, rnd)); + } + return arr; + }; + IntFunction[]> getBigIntsWithSmallerBigIntAndExp = size -> { + @SuppressWarnings("unchecked") + Triple[] arr = (Triple[]) Array.newInstance(bigIntTripleClass, size); + for (int i = 0; i < size; ++i) { + arr[i] = new Triple(getBigInt.get(), getBigIntWithSmallerBigInt.get()); + } + return arr; + }; + + Benchmark bench = new LongBenchmark(RUNS); + bench.run("Long_fromInt with < 32bit literal", LongBenchmark::testSmallLongLiteral); + report(""); + bench.run("Long_create with >= 32bit literal", LongBenchmark::testLongLiteral); + report(""); + bench.run("Long_fromInt", getInt, LongBenchmark::testLongFromInt); + report(""); + bench.run("Long_lo (to 32bit integer)", getLong, LongBenchmark::testLongToInt); + report(""); + bench.run("Long_hi (to 32bit integer)", getLong, LongBenchmark::testLongToIntHigh); + report(""); + bench.run("Long_fromNumber (64bit double)", getDouble, LongBenchmark::testLongFromDouble); + report(""); + bench.run("Long_toNumber (64bit double)", getLong, LongBenchmark::testLongToDouble); + report(""); + + bench.run("Long_eq", getLongPairs, LongBenchmark::testLongEqual); + report(""); + bench.run("Long_ne", getLongPairs, LongBenchmark::testLongNotEqual); + report(""); + bench.run("Long_gt", getLongPairs, LongBenchmark::testLongGreaterThan); + report(""); + bench.run("Long_ge", getLongPairs, LongBenchmark::testLongGreaterThanEqual); + report(""); + bench.run("Long_lt", getLongPairs, LongBenchmark::testLongLessThan); + report(""); + bench.run("Long_le", getLongPairs, LongBenchmark::testLongLessThanEqual); + report(""); + bench.run("Long_compare", getLongPairs, LongBenchmark::testLongCompare); + report(""); + bench.run("Long_ucompare", getLongPairs, LongBenchmark::testLongCompareUnsigned); + report(""); + + bench.run("Long_add", getLongPairs, LongBenchmark::testLongAdd); + report(""); + bench.run("Long_neg", getLong, LongBenchmark::testLongNegate); + report(""); + bench.run("Long_sub", getLongPairs, LongBenchmark::testLongSubtract); + report(""); + + bench.run("Long_mul", getLongPairs, LongBenchmark::testLongMultiply); + report(""); + bench.run("Long_div", getLongPairs, LongBenchmark::testLongDivide); + report(""); + bench.run("Long_udiv", getLongPairs, LongBenchmark::testLongDivideUnsigned); + report(""); + bench.run("Long_rem", getLongPairs, LongBenchmark::testLongRemainder); + report(""); + bench.run("Long_urem", getLongPairs, LongBenchmark::testLongRemainderUnsigned); + report(""); + + bench.run("Long_and", getLongPairs, LongBenchmark::testLongAnd); + report(""); + bench.run("Long_or", getLongPairs, LongBenchmark::testLongOr); + report(""); + bench.run("Long_xor", getLongPairs, LongBenchmark::testLongXor); + report(""); + bench.run("Long_shl", getLongsWithShift, LongBenchmark::testLongShiftLeft); + report(""); + bench.run("Long_shl (constant)", getLong, LongBenchmark::testLongShiftLeftConstant); + report(""); + bench.run("Long_shr", getLongsWithShift, LongBenchmark::testLongShiftRight); + report(""); + bench.run("Long_shr (constant)", getLong, LongBenchmark::testLongShiftRightConstant); + report(""); + bench.run("Long_shru", getLongsWithShift, LongBenchmark::testLongShiftRightUnsigned); + report(""); + bench.run("Long_shru (constant)", getLong, LongBenchmark::testLongShiftRightUnsignedConstant); + report(""); + bench.run("Long_not", getLong, LongBenchmark::testLongNot); + report(""); + + bench.run("unsigned int -> long", getInt, LongBenchmark::testUnsignedIntToLong); + report(""); + + report("Run Classlib benchmarks..."); + + bench = new ClasslibBenchmark(RUNS); + bench.run("Long.toString", getLong, String.class, ClasslibBenchmark::testLongToString); + report(""); + bench.run("Long.bitCount", getLong, ClasslibBenchmark::testLongBitCount); + report(""); + bench.run("Double.parseDouble", getDoublesAsString, ClasslibBenchmark::testParseDouble); + report(""); + bench.run("Double.toString", getDouble, String.class, ClasslibBenchmark::testDoubleToString); + report(""); + bench.run("Double.toHexString", getDouble, String.class, ClasslibBenchmark::testDoubleToHexString); + report(""); + + report("Run BigInteger benchmarks..."); + + bench = new BigIntegerBenchmark.Easy(RUNS); + bench.run("BigInteger.doubleValue", getBigInts, BigIntegerBenchmark.Easy::testDoubleValue); + report(""); + bench.run("BigInteger.add", getBigIntPairs, BigInteger.class, BigIntegerBenchmark.Easy::testAdd); + report(""); + bench.run("BigInteger.subtract", getBigIntPairs, BigInteger.class, BigIntegerBenchmark.Easy::testSubtract); + report(""); + + bench = new BigIntegerBenchmark.Medium(RUNS); + bench.run("BigInteger.toString", getBigInts, String.class, BigIntegerBenchmark.Medium::testToString); + report(""); + bench.run("BigInteger.fromString", getBigIntsAsString, BigInteger.class, + BigIntegerBenchmark.Medium::testFromString); + report(""); + bench.run("BigInteger.multiply", getBigIntPairs, BigInteger.class, BigIntegerBenchmark.Medium::testMultiply); + report(""); + bench.run("BigInteger.sqrt", getBigInts, BigInteger.class, BigIntegerBenchmark.Medium::testSqrt); + report(""); + bench.run("BigInteger.divide", getBigIntsWithSmallerBigInt, BigInteger.class, + BigIntegerBenchmark.Medium::testDivide); + report(""); + bench.run("BigInteger.remainder", getBigIntsWithSmallerBigInt, BigInteger.class, + BigIntegerBenchmark.Medium::testRemainder); + report(""); + bench.run("BigInteger.divideAndRemainder", getBigIntsWithSmallerBigInt, BigInteger[].class, + BigIntegerBenchmark.Medium::testDivideAndRemainder); + report(""); + + bench = new BigIntegerBenchmark.Heavy(RUNS); + bench.run("BigInteger.pow", getBigIntsWithExp, BigInteger.class, BigIntegerBenchmark.Heavy::testPow); + report(""); + bench.run("BigInteger.modInverse", getBigIntsWithPrime, BigInteger.class, + BigIntegerBenchmark.Heavy::testModInverse); + report(""); + bench.run("BigInteger.modPow", getBigIntsWithSmallerBigIntAndExp, BigInteger.class, + BigIntegerBenchmark.Heavy::testModPow); + report(""); + } +} diff --git a/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/Benchmark.java b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/Benchmark.java new file mode 100644 index 000000000..35cef74f8 --- /dev/null +++ b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/Benchmark.java @@ -0,0 +1,322 @@ +/* + * Copyright 2023 Bernd Busse. + * + * 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.teavm.samples.long_benchmark; + +import java.lang.reflect.Array; +import java.text.DecimalFormat; +import java.util.function.*; +import org.teavm.jso.browser.Performance; + +abstract class Benchmark { + public int iterations = 1; + private double scale = 1; + private String range = "s"; + + private double[] runTiming; + private double total; + + private static DecimalFormat intFmt = new DecimalFormat("#,##0"); + private static DecimalFormat fmt = new DecimalFormat("#0.0##"); + + public Benchmark(int runs, int iterations, double scale) { + this.iterations = iterations; + this.scale = scale / 1000.0; + this.runTiming = new double[runs]; + + if (scale < 1e3) { + this.range = "s"; + } else if (scale < 1e6) { + this.range = "ms"; + } else if (scale < 1e9) { + this.range = "μs"; + } else { + this.range = "ns"; + } + } + + private void reportStats() { + int runs = runTiming.length; + + double mean = total / (double) runs; + double mse = 0; + for (int r = 0; r < runs; ++r) { + double err = runTiming[r] - mean; + mse += err * err; + } + double deviation = Math.sqrt(mse / ((double) runs - 1.0)); + double ciDeviation = 1.96 * deviation / Math.sqrt(runs); + + double meanPerOp = scale * mean / (double) iterations; + double msePerOp = 0; + for (int r = 0; r < runs; ++r) { + double err = scale * runTiming[r] / (double) iterations - meanPerOp; + msePerOp += err * err; + } + double deviationPerOp = Math.sqrt(msePerOp / ((double) runs - 1.0)); + double ciDeviationPerOp = 1.96 * deviationPerOp / Math.sqrt(runs); + + BenchRunner.report("Avg over " + runs + " runs: " + + fmt.format(mean) + "±" + fmt.format(ciDeviation) + " milliseconds" + + " => " + fmt.format(meanPerOp) + "±" + fmt.format(ciDeviationPerOp) + range + "/op" + + " (±" + fmt.format(ciDeviationPerOp / meanPerOp * 100.0) + "%)"); + } + + private void run(String title, IntConsumer runner) { + BenchRunner.report("Test " + title + " (" + intFmt.format(iterations).replace(",", "_") + " iterations)..."); + + // reset cumulative total + total = 0; + + // do warm up run + runner.accept(iterations); + + // measured runs + for (int r = 0; r < runTiming.length; ++r) { + double start = Performance.now(); + runner.accept(iterations); + double end = Performance.now(); + + runTiming[r] = end - start; + total += runTiming[r]; + } + + reportStats(); + try { + Thread.sleep(10); + } catch (Exception e) { + // + } finally { + Thread.yield(); + } + } + + + // LongSupplier + public void run(String title, LongSupplier benchmark) { + // initialize benchmark output buffer + long[] results = new long[iterations]; + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.getAsLong(); + } + }); + } + + // IntToLongFunction + public void run(String title, IntSupplier init, IntToLongFunction benchmark) { + // initialize benchmark inputs + int[] inputs = new int[iterations]; + for (int i = 0; i < iterations; ++i) { + inputs[i] = init.getAsInt(); + } + + // initialize benchmark output buffer + long[] results = new long[iterations]; + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.applyAsLong(inputs[i]); + } + }); + } + + // DoubleToLongFunction + public void run(String title, DoubleSupplier init, DoubleToLongFunction benchmark) { + // initialize benchmark inputs + double[] inputs = new double[iterations]; + for (int i = 0; i < iterations; ++i) { + inputs[i] = init.getAsDouble(); + } + + // initialize benchmark output buffer + long[] results = new long[iterations]; + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.applyAsLong(inputs[i]); + } + }); + } + + // LongUnaryOperator + public void run(String title, LongSupplier init, LongUnaryOperator benchmark) { + // initialize benchmark inputs + long[] inputs = new long[iterations]; + for (int i = 0; i < iterations; ++i) { + inputs[i] = init.getAsLong(); + } + + // initialize benchmark output buffer + long[] results = new long[iterations]; + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.applyAsLong(inputs[i]); + } + }); + } + + // LongToIntFunction + public void run(String title, LongSupplier init, LongToIntFunction benchmark) { + // initialize benchmark inputs + long[] inputs = new long[iterations]; + for (int i = 0; i < iterations; ++i) { + inputs[i] = init.getAsLong(); + } + + // initialize benchmark output buffer + int[] results = new int[iterations]; + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.applyAsInt(inputs[i]); + } + }); + } + + // LongToDoubleFunction + public void run(String title, LongSupplier init, LongToDoubleFunction benchmark) { + // initialize benchmark inputs + long[] inputs = new long[iterations]; + for (int i = 0; i < iterations; ++i) { + inputs[i] = init.getAsLong(); + } + + // initialize benchmark output buffer + double[] results = new double[iterations]; + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.applyAsDouble(inputs[i]); + } + }); + } + + // Predicate + public void run(String title, IntFunction init, Predicate benchmark) { + // initialize benchmark inputs + T[] inputs = init.apply(iterations); + + // initialize benchmark output buffer + boolean[] results = new boolean[iterations]; + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.test(inputs[i]); + } + }); + } + + // ToIntFunction + public void run(String title, IntFunction init, ToIntFunction benchmark) { + // initialize benchmark inputs + T[] inputs = init.apply(iterations); + + // initialize benchmark output buffer + int[] results = new int[iterations]; + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.applyAsInt(inputs[i]); + } + }); + } + + // ToDoubleFunction + public void run(String title, IntFunction init, ToDoubleFunction benchmark) { + // initialize benchmark inputs + T[] inputs = init.apply(iterations); + + // initialize benchmark output buffer + double[] results = new double[iterations]; + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.applyAsDouble(inputs[i]); + } + }); + } + + // ToLongFunction + public void run(String title, IntFunction init, ToLongFunction benchmark) { + // initialize benchmark inputs + T[] inputs = init.apply(iterations); + + // initialize benchmark output buffer + long[] results = new long[iterations]; + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.applyAsLong(inputs[i]); + } + }); + } + + // DoubleFunction + public void run(String title, DoubleSupplier init, Class cl, DoubleFunction benchmark) { + // initialize benchmark inputs + double[] inputs = new double[iterations]; + for (int i = 0; i < iterations; ++i) { + inputs[i] = init.getAsDouble(); + } + + // initialize benchmark output buffer + @SuppressWarnings("unchecked") + T[] results = (T[]) Array.newInstance(cl, iterations); + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.apply(inputs[i]); + } + }); + } + + // LongFunction + public void run(String title, LongSupplier init, Class cl, LongFunction benchmark) { + // initialize benchmark inputs + long[] inputs = new long[iterations]; + for (int i = 0; i < iterations; ++i) { + inputs[i] = init.getAsLong(); + } + + // initialize benchmark output buffer + @SuppressWarnings("unchecked") + T[] results = (T[]) Array.newInstance(cl, iterations); + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.apply(inputs[i]); + } + }); + } + + // Function + public void run(String title, IntFunction init, Class cl, Function benchmark) { + // initialize benchmark inputs + T[] inputs = init.apply(iterations); + + // initialize benchmark output buffer + @SuppressWarnings("unchecked") + R[] results = (R[]) Array.newInstance(cl, iterations); + + run(title, iterations -> { + for (int i = 0; i < iterations; ++i) { + results[i] = benchmark.apply(inputs[i]); + } + }); + } +} diff --git a/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/BigIntegerBenchmark.java b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/BigIntegerBenchmark.java new file mode 100644 index 000000000..e133a31b0 --- /dev/null +++ b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/BigIntegerBenchmark.java @@ -0,0 +1,134 @@ +/* + * Copyright 2023 Bernd Busse. + * + * 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.teavm.samples.long_benchmark; + +import java.math.BigInteger; +import org.teavm.samples.long_benchmark.Pairs.*; + +public final class BigIntegerBenchmark { + + public static final class Easy extends Benchmark { + public static final int ITERATIONS = 100_000; + public static final double SCALE = 1e6; + + Easy(int runs) { + super(runs, ITERATIONS, SCALE); + } + + static double testDoubleValue(BigInteger in) { + double value = in.doubleValue(); + return value; + } + + static BigInteger testAdd(Pair in) { + BigInteger a = in.first; + BigInteger b = in.second; + BigInteger value = a.add(b); + return value; + } + + static BigInteger testSubtract(Pair in) { + BigInteger a = in.first; + BigInteger b = in.second; + BigInteger value = a.subtract(b); + return value; + } + } + + public static final class Medium extends Benchmark { + public static final int ITERATIONS = 2_000; + public static final double SCALE = 1e6; + + Medium(int runs) { + super(runs, ITERATIONS, SCALE); + } + + static String testToString(BigInteger in) { + String value = in.toString(); + return value; + } + + static BigInteger testFromString(String in) { + BigInteger value = new BigInteger(in, 10); + return value; + } + + static BigInteger testMultiply(Pair in) { + BigInteger a = in.first; + BigInteger b = in.second; + BigInteger value = a.multiply(b); + return value; + } + + static BigInteger testSqrt(BigInteger in) { + BigInteger value = in.sqrt(); + return value; + } + + static BigInteger testDivide(Pair in) { + BigInteger a = in.first; + BigInteger b = in.second; + BigInteger value = a.divide(b); + return value; + } + + static BigInteger testRemainder(Pair in) { + BigInteger a = in.first; + BigInteger b = in.second; + BigInteger value = a.remainder(b); + return value; + } + + static BigInteger[] testDivideAndRemainder(Pair in) { + BigInteger a = in.first; + BigInteger b = in.second; + BigInteger[] value = a.divideAndRemainder(b); + return value; + } + } + + public static final class Heavy extends Benchmark { + public static final int ITERATIONS = 250; + public static final double SCALE = 1e3; + + Heavy(int runs) { + super(runs, ITERATIONS, SCALE); + } + + static BigInteger testPow(PairWithInt in) { + BigInteger a = in.first; + int b = in.second; + BigInteger value = a.pow(b); + return value; + } + + static BigInteger testModInverse(Pair in) { + BigInteger a = in.first; + BigInteger b = in.second; + BigInteger value = a.modInverse(b); + return value; + } + + static BigInteger testModPow(Triple in) { + BigInteger a = in.first; + BigInteger b = in.second; + BigInteger c = in.third; + BigInteger value = a.modPow(c, b); // a ^ c mod b, c should be half as long as a + return value; + } + } + +} diff --git a/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/ClasslibBenchmark.java b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/ClasslibBenchmark.java new file mode 100644 index 000000000..e3a802e1d --- /dev/null +++ b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/ClasslibBenchmark.java @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Bernd Busse. + * + * 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.teavm.samples.long_benchmark; + +public final class ClasslibBenchmark extends Benchmark { + public static final int ITERATIONS = 100_000; + public static final double SCALE = 1e6; + + ClasslibBenchmark(int runs) { + super(runs, ITERATIONS, SCALE); + } + + static String testLongToString(long in) { + String value = Long.toString(in); + return value; + } + + static int testLongBitCount(long in) { + int value = Long.bitCount(in); + return value; + } + + static double testParseDouble(String in) { + double value = Double.parseDouble(in); + return value; + } + + static String testDoubleToString(double in) { + String value = Double.toString(in); + return value; + } + + static String testDoubleToHexString(double in) { + String value = Double.toHexString(in); + return value; + } +} diff --git a/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/CrossOriginFilter.java b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/CrossOriginFilter.java new file mode 100644 index 000000000..f036c4d56 --- /dev/null +++ b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/CrossOriginFilter.java @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Bernd Busse. + * + * 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.teavm.samples.long_benchmark; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +public final class CrossOriginFilter implements Filter { + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) + throws IOException, ServletException { + final HttpServletResponse httpResponse = (HttpServletResponse) response; + + httpResponse.addHeader("Cross-Origin-Opener-Policy", "same-origin"); + httpResponse.addHeader("Cross-Origin-Embedder-Policy", "require-corp"); + + chain.doFilter(request, response); + } + +} diff --git a/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/LongBenchmark.java b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/LongBenchmark.java new file mode 100644 index 000000000..5d2a8273c --- /dev/null +++ b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/LongBenchmark.java @@ -0,0 +1,239 @@ +/* + * Copyright 2023 Bernd Busse. + * + * 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.teavm.samples.long_benchmark; + +import org.teavm.samples.long_benchmark.Pairs.*; + +public final class LongBenchmark extends Benchmark { + public static final int ITERATIONS = 1_000_000; + public static final double SCALE = 1e9; + + LongBenchmark(int runs) { + super(runs, ITERATIONS, SCALE); + } + + static long testSmallLongLiteral() { + long value = 127107580L; // 27 bit literal + return value; + } + + static long testLongLiteral() { + long value = 8919268947643919751L; // 63 bit literal + return value; + } + + static long testLongFromInt(int in) { + long value = (long) in; + return value; + } + + static int testLongToInt(long in) { + int value = (int) in; + return value; + } + + static int testLongToIntHigh(long in) { + int value = (int) (in >> 32); + return value; + } + + static long testLongFromDouble(double in) { + long value = (long) in; + return value; + } + + static double testLongToDouble(long in) { + double value = (double) in; + return value; + } + + static boolean testLongEqual(LongPair in) { + long a = in.first; + long b = in.second; + boolean value = a != b; // compiler optimizes this to a == b ? 0 : 1 + return value; + } + + static boolean testLongNotEqual(LongPair in) { + long a = in.first; + long b = in.second; + boolean value = a == b; // compiler optimizes this to a != b ? 0 : 1 + return value; + } + + static boolean testLongGreaterThan(LongPair in) { + long a = in.first; + long b = in.second; + boolean value = a <= b; // compiler optimizes this to a > b ? 0 : 1 + return value; + } + + static boolean testLongGreaterThanEqual(LongPair in) { + long a = in.first; + long b = in.second; + boolean value = a < b; // compiler optimizes this to a >= b ? 0 : 1 + return value; + } + + static boolean testLongLessThan(LongPair in) { + long a = in.first; + long b = in.second; + boolean value = a >= b; // compiler optimizes this to a < b ? 0 : 1 + return value; + } + + static boolean testLongLessThanEqual(LongPair in) { + long a = in.first; + long b = in.second; + boolean value = a > b; // compiler optimizes this to a <= b ? 0 : 1 + return value; + } + + static int testLongCompare(LongPair in) { + long a = in.first; + long b = in.second; + int value = (a < b) || (a > b) ? 1 : 0; + return value; + } + + static int testLongCompareUnsigned(LongPair in) { + long a = in.first; + long b = in.second; + int value = Long.compareUnsigned(a, b); + return value; + } + + static long testLongAdd(LongPair in) { + long a = in.first; + long b = in.second; + long value = a + b; + return value; + } + + static long testLongNegate(long in) { + long value = -in; + return value; + } + + static long testLongSubtract(LongPair in) { + long a = in.first; + long b = in.second; + long value = a - b; + return value; + } + + static long testLongMultiply(LongPair in) { + long a = in.first; + long b = in.second; + long value = a * b; + return value; + } + + static long testLongDivide(LongPair in) { + long a = in.first; + long b = in.second; + long value = a / b; + return value; + } + + static long testLongDivideUnsigned(LongPair in) { + long a = in.first; + long b = in.second; + long value = Long.divideUnsigned(a, b); + return value; + } + + static long testLongRemainder(LongPair in) { + long a = in.first; + long b = in.second; + long value = a % b; + return value; + } + + static long testLongRemainderUnsigned(LongPair in) { + long a = in.first; + long b = in.second; + long value = Long.remainderUnsigned(a, b); + return value; + } + + static long testLongAnd(LongPair in) { + long a = in.first; + long b = in.second; + long value = a & b; + return value; + } + + static long testLongOr(LongPair in) { + long a = in.first; + long b = in.second; + long value = a | b; + return value; + } + + static long testLongXor(LongPair in) { + long a = in.first; + long b = in.second; + long value = a ^ b; + return value; + } + + static long testLongShiftLeft(LongIntPair in) { + long a = in.first; + int b = in.second; + long value = a << b; + return value; + } + + static long testLongShiftLeftConstant(long in) { + long value = in << 23; + return value; + } + + static long testLongShiftRight(LongIntPair in) { + long a = in.first; + int b = in.second; + long value = a >> b; + return value; + } + + static long testLongShiftRightConstant(long in) { + long value = in >> 23; + return value; + } + + static long testLongShiftRightUnsigned(LongIntPair in) { + long a = in.first; + int b = in.second; + long value = a >>> b; + return value; + } + + static long testLongShiftRightUnsignedConstant(long in) { + long value = in >>> 23; + return value; + } + + static long testLongNot(long in) { + long value = ~in; + return value; + } + + static long testUnsignedIntToLong(int in) { + long value = in & 0xFFFFFFFFL; + return value; + } +} diff --git a/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/Pairs.java b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/Pairs.java new file mode 100644 index 000000000..ae4ecf990 --- /dev/null +++ b/samples/long-benchmark/src/main/java/org/teavm/samples/long_benchmark/Pairs.java @@ -0,0 +1,72 @@ +/* + * Copyright 2023 Bernd Busse. + * + * 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.teavm.samples.long_benchmark; + +final class Pairs { + + static final class LongPair { + public long first; + public long second; + + public LongPair(long first, long second) { + this.first = first; + this.second = second; + } + } + + static final class LongIntPair { + public long first; + public int second; + + public LongIntPair(long first, int second) { + this.first = first; + this.second = second; + } + } + + static final class PairWithInt { + public U first; + public int second; + + public PairWithInt(U first, int second) { + this.first = first; + this.second = second; + } + } + + static final class Pair { + public U first; + public V second; + + public Pair(U first, V second) { + this.first = first; + this.second = second; + } + } + + static final class Triple { + public U first; + public U second; + public U third; + + public Triple(U first, Pair second) { + this.first = first; + this.second = second.first; + this.third = second.second; + } + } + +} diff --git a/samples/long-benchmark/src/main/webapp/WEB-INF/web.xml b/samples/long-benchmark/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..76c572b73 --- /dev/null +++ b/samples/long-benchmark/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,30 @@ + + + + + cross-origin + org.teavm.samples.long_benchmark.CrossOriginFilter + + + cross-origin + /* + + + diff --git a/samples/long-benchmark/src/main/webapp/index.html b/samples/long-benchmark/src/main/webapp/index.html new file mode 100644 index 000000000..c3b66ac38 --- /dev/null +++ b/samples/long-benchmark/src/main/webapp/index.html @@ -0,0 +1,49 @@ + + + + + JavaScript Long-Emulation Benchmark + + + + + + +

JavaScript Long-Emulation Benchmark

+

This application tests the speed of the internal long-emulation using native BigInts.

+ +
+
+
stdout
+
+
+
+ + + diff --git a/samples/long-benchmark/src/main/webapp/style.css b/samples/long-benchmark/src/main/webapp/style.css new file mode 100644 index 000000000..c1ba9bf88 --- /dev/null +++ b/samples/long-benchmark/src/main/webapp/style.css @@ -0,0 +1,17 @@ +.block { + border: solid 2px gray; + padding: 1rem; +} +.block-title { + font-weight: bold; +} + +#impl-info { + font-style: italic; +} + +#stdout { + height: 60vh; + overflow-y: scroll; + overflow-x: auto; +} diff --git a/samples/long-benchmark/src/main/webapp/worker.js b/samples/long-benchmark/src/main/webapp/worker.js new file mode 100644 index 000000000..c6a62a919 --- /dev/null +++ b/samples/long-benchmark/src/main/webapp/worker.js @@ -0,0 +1,26 @@ +let $rt_stdoutBuffer = ""; +function $rt_putStdoutCustom(msg) { + let index = 0; + while (true) { + let next = msg.indexOf('\n', index); + if (next < 0) { + $rt_stdoutBuffer += msg; + break; + } + let line = $rt_stdoutBuffer + msg.substring(index, next); + postMessage({ type: "stdout", data: line }); + $rt_stdoutBuffer = ""; + index = next + 1; + } +} +this.$rt_putStdoutCustom = $rt_putStdoutCustom; + +importScripts("js/long-benchmark.js"); +console.log("Worker started"); + +self.onmessage = () => { + console.log("Run benchmark..."); + main(); + console.log("Benchmark finished"); + postMessage({ type: "done" }); +}; diff --git a/samples/settings.gradle.kts b/samples/settings.gradle.kts index d98625e2c..dfae0b797 100644 --- a/samples/settings.gradle.kts +++ b/samples/settings.gradle.kts @@ -55,6 +55,7 @@ include("stdout-helper") include("hello") include("async") include("benchmark") +include("long-benchmark") include("pi") include("promise") include("kotlin") @@ -93,4 +94,4 @@ gradle.afterProject { java.sourceCompatibility = JavaVersion.VERSION_11 java.targetCompatibility = JavaVersion.VERSION_11 } -} \ No newline at end of file +}