From cd9db17d73ee4d34d1130a9a76cb3d4db481f9d8 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 16 Oct 2023 17:26:05 +0200 Subject: [PATCH] JS: improve performance float<->int reinterpretation conversion, improve performance of Double.equals. --- .../classlib/java/lang/DoubleGenerator.java | 8 ++++ .../org/teavm/classlib/java/lang/TDouble.java | 14 +++++- .../org/teavm/classlib/java/lang/TFloat.java | 6 ++- .../org/teavm/backend/javascript/runtime.js | 46 +++++++++++++------ 4 files changed, 59 insertions(+), 15 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/DoubleGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/DoubleGenerator.java index bf284dfc6..aba3590fd 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/DoubleGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/DoubleGenerator.java @@ -23,6 +23,14 @@ import org.teavm.model.MethodReference; public class DoubleGenerator implements Injector { @Override public void generate(InjectorContext context, MethodReference methodRef) throws IOException { + if (methodRef.getName().equals("doubleEqualsJs")) { + context.getWriter().appendFunction("$rt_equalDoubles").append("("); + context.writeExpr(context.getArgument(0)); + context.getWriter().append(',').ws(); + context.writeExpr(context.getArgument(1)); + context.getWriter().append(')'); + return; + } context.getWriter().append("$rt_").append(methodRef.getName()).append("("); context.writeExpr(context.getArgument(0)); context.getWriter().append(")"); diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java index b9e5d5130..4c4f3d362 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java @@ -16,6 +16,7 @@ package org.teavm.classlib.java.lang; import org.teavm.backend.javascript.spi.InjectedBy; +import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.impl.text.DoubleSynthesizer; import org.teavm.interop.Import; import org.teavm.interop.NoSideEffects; @@ -201,7 +202,18 @@ public class TDouble extends TNumber implements TComparable { if (this == other) { return true; } - return other instanceof TDouble && doubleToLongBits(((TDouble) other).value) == doubleToLongBits(value); + return other instanceof TDouble && equals(value, ((TDouble) other).value); + } + + private static boolean equals(double a, double b) { + return PlatformDetector.isJavaScript() ? doubleEqualsJs(a, b) : equalsWithBits(a, b); + } + + @InjectedBy(DoubleGenerator.class) + private static native boolean doubleEqualsJs(double a, double b); + + private static boolean equalsWithBits(double a, double b) { + return a != a ? b != b : doubleToRawLongBits(a) == doubleToRawLongBits(b); } @Override diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java index a09f8ff73..14c31ebba 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java @@ -85,7 +85,11 @@ public class TFloat extends TNumber implements TComparable { if (this == other) { return true; } - return other instanceof TFloat && floatToIntBits(((TFloat) other).value) == floatToIntBits(value); + return other instanceof TFloat && equals(value, ((TFloat) other).value); + } + + private static boolean equals(float a, float b) { + return a != a ? b != b : floatToRawIntBits(a) == floatToRawIntBits(b); } @Override diff --git a/core/src/main/resources/org/teavm/backend/javascript/runtime.js b/core/src/main/resources/org/teavm/backend/javascript/runtime.js index 8d0ebaa44..8643f4eb2 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/runtime.js +++ b/core/src/main/resources/org/teavm/backend/javascript/runtime.js @@ -656,7 +656,11 @@ function $rt_eraseClinit(target) { return target.$clinit = function() {}; } -var $rt_numberConversionView = new DataView(new ArrayBuffer(8)); +var $rt_numberConversionBuffer = new ArrayBuffer(16); +var $rt_numberConversionView = new DataView($rt_numberConversionBuffer); +var $rt_numberConversionFloatArray = new Float32Array($rt_numberConversionBuffer); +var $rt_numberConversionDoubleArray = new Float64Array($rt_numberConversionBuffer); +var $rt_numberConversionIntArray = new Int32Array($rt_numberConversionBuffer); var $rt_doubleToRawLongBits; var $rt_longBitsToDouble; @@ -670,31 +674,47 @@ if (typeof BigInt !== 'function') { $rt_numberConversionView.setInt32(4, n.hi, true); return $rt_numberConversionView.getFloat64(0, true); } -} else { +} else if (typeof BigInt64Array !== 'function') { $rt_doubleToRawLongBits = function(n) { $rt_numberConversionView.setFloat64(0, n, true); - // For compatibility with Safari var lo = $rt_numberConversionView.getInt32(0, true); var hi = $rt_numberConversionView.getInt32(4, true); return BigInt.asIntN(64, BigInt.asUintN(32, BigInt(lo)) | (BigInt(hi) << BigInt(32))); } $rt_longBitsToDouble = function(n) { - // For compatibility with Safari - var hi = Number(BigInt.asIntN(32, n >> BigInt(32))); - var lo = Number(BigInt.asIntN(32, n & BigInt(0xFFFFFFFF))); - $rt_numberConversionView.setInt32(0, lo, true); - $rt_numberConversionView.setInt32(4, hi, true); - return $rt_numberConversionView.getFloat64(0, true); + $rt_numberConversionView.setFloat64(0, n, true); + var lo = $rt_numberConversionView.getInt32(0, true); + var hi = $rt_numberConversionView.getInt32(4, true); + return BigInt.asIntN(64, BigInt.asUintN(32, BigInt(lo)) | (BigInt(hi) << BigInt(32))); + } +} else { + var $rt_numberConversionLongArray = new BigInt64Array($rt_numberConversionBuffer); + $rt_doubleToRawLongBits = function(n) { + $rt_numberConversionDoubleArray[0] = n; + return $rt_numberConversionLongArray[0]; + } + $rt_longBitsToDouble = function(n) { + $rt_numberConversionLongArray[0] = n; + return $rt_numberConversionDoubleArray[0]; } } function $rt_floatToRawIntBits(n) { - $rt_numberConversionView.setFloat32(0, n); - return $rt_numberConversionView.getInt32(0); + $rt_numberConversionFloatArray[0] = n; + return $rt_numberConversionIntArray[0]; } function $rt_intBitsToFloat(n) { - $rt_numberConversionView.setInt32(0, n); - return $rt_numberConversionView.getFloat32(0); + $rt_numberConversionIntArray[0] = n; + return $rt_numberConversionFloatArray[0]; +} +function $rt_equalDoubles(a, b) { + if (a !== a) { + return b !== b; + } + $rt_numberConversionDoubleArray[0] = a; + $rt_numberConversionDoubleArray[1] = b; + return $rt_numberConversionIntArray[0] === $rt_numberConversionIntArray[2] + && $rt_numberConversionIntArray[1] === $rt_numberConversionIntArray[3]; } var JavaError;