From 5dd4d34d7527ce980e9088a0e1238584b14e8cb6 Mon Sep 17 00:00:00 2001 From: Stiopa Koltsov Date: Sat, 5 Dec 2020 23:59:50 +0000 Subject: [PATCH] Starlark: optimize StarlarkInt.Big comparison to StarlarkInt.Int{32,64} Perform comparison without conversion of smaller integers to `BigInteger`. `StarlarkInt.compareTo` does not allocate now. For this benchmark: ``` def test(): x = 17 << 77 for i in range(10): print(i) for j in range(10000000): x > 1 test() ``` ``` A: n=27 mean=4.262 std=0.203 se=0.039 min=4.036 med=4.193 B: n=27 mean=4.113 std=0.172 se=0.033 min=3.859 med=4.049 B/A: 0.965 0.941..0.990 (95% conf) ``` Speed up is about 7% when comparing to an integer outside of `BigInteger` cached range (-16..16). Finally, `StarlarkInt.Big` to `StarlarkInt.Big` comparison performance seems to stay the same (within 95% confidence interval after 100 test iterations). --- .../net/starlark/java/eval/StarlarkInt.java | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/starlark/java/eval/StarlarkInt.java b/src/main/java/net/starlark/java/eval/StarlarkInt.java index d63186ba92cf29..f595aff4064f9e 100644 --- a/src/main/java/net/starlark/java/eval/StarlarkInt.java +++ b/src/main/java/net/starlark/java/eval/StarlarkInt.java @@ -436,17 +436,32 @@ public int compareTo(StarlarkInt x) { /** Returns a value whose signum is equal to x - y. */ public static int compare(StarlarkInt x, StarlarkInt y) { - if (x instanceof Int32 && y instanceof Int32) { - return Integer.compare(((Int32) x).v, ((Int32) y).v); + long xl = 0, yl = 0; + boolean xbig = false, ybig = false; + try { + xl = x.toLongFast(); + } catch (Overflow unused) { + xbig = true; } - try { - return Long.compare(x.toLongFast(), y.toLongFast()); + yl = y.toLongFast(); } catch (Overflow unused) { - /* fall through */ + ybig = true; + } + + // If both arguments are big integers, we invoke BigInteger.compare. + // If neither argument is big integer, we compare longs. + // If only one argument is big integer, it is bigger than other if positive + // and smaller otherwise. + if (xbig && ybig) { + return ((Big) x).v.compareTo(((Big) y).v); + } else if (xbig) { + return ((Big) x).v.signum(); + } else if (ybig) { + return -((Big) y).v.signum(); + } else { + return Long.compare(xl, yl); } - - return x.toBigInteger().compareTo(y.toBigInteger()); } /** Returns x + y. */