diff --git a/benchmarks/bigint_bitwise_binops.py b/benchmarks/bigint_bitwise_binops.py new file mode 100644 index 0000000000..643ea2dc8c --- /dev/null +++ b/benchmarks/bigint_bitwise_binops.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 + +import argparse +import time + +import numpy as np + +import arkouda as ak + + +def time_ak_bitwise_binops(N_per_locale, trials, max_bits, seed): + print(">>> arkouda bigint bitwise binops") + cfg = ak.get_config() + N = N_per_locale * cfg["numLocales"] + print("numLocales = {}, N = {:,}".format(cfg["numLocales"], N)) + a1 = ak.randint(0, 2**32, N, dtype=ak.uint64, seed=seed) + a2 = ak.randint(0, 2**32, N, dtype=ak.uint64, seed=seed) + a = ak.bigint_from_uint_arrays([a1, a2], max_bits=max_bits) + b1 = ak.randint(0, 2**32, N, dtype=ak.uint64, seed=seed) + b2 = ak.randint(0, 2**32, N, dtype=ak.uint64, seed=seed) + b = ak.bigint_from_uint_arrays([b1, b2], max_bits=max_bits) + + # bytes per bigint array (N * 16) since it's made of 2 uint64 arrays + tot_bytes = N * 8 * 2 + + and_timings = [] + or_timings = [] + shift_timings = [] + for i in range(trials): + start = time.time() + c = a & b + end = time.time() + and_timings.append(end - start) + + start = time.time() + c = a | b + end = time.time() + or_timings.append(end - start) + + start = time.time() + c = a >> 10 + end = time.time() + shift_timings.append(end - start) + + andtavg = sum(and_timings) / trials + ortavg = sum(or_timings) / trials + shifttavg = sum(shift_timings) / trials + + print("Average bigint AND time = {:.4f} sec".format(andtavg)) + bytes_per_sec = (tot_bytes * 2) / andtavg + print("Average bigint AND rate = {:.2f} GiB/sec".format(bytes_per_sec / 2**30)) + print() + + print("Average bigint OR time = {:.4f} sec".format(ortavg)) + bytes_per_sec = (tot_bytes * 2) / ortavg + print("Average bigint OR rate = {:.2f} GiB/sec".format(bytes_per_sec / 2**30)) + print() + + print("Average bigint SHIFT time = {:.4f} sec".format(shifttavg)) + bytes_per_sec = tot_bytes / shifttavg + print("Average bigint SHIFT rate = {:.2f} GiB/sec".format(bytes_per_sec / 2**30)) + + +def check_correctness(max_bits, seed): + N = 10**4 + if seed is not None: + np.random.seed(seed) + np_a, np_b = np.random.randint(0, 2**32, N), np.random.randint(0, 2**32, N) + ak_a = ak.array(np_a, dtype=ak.bigint, max_bits=max_bits) + ak_b = ak.array(np_b, dtype=ak.bigint, max_bits=max_bits) + np_arrays = [np_a & np_b, np_a | np_b, np_a >> 10] + ak_arrays = [ak_a & ak_b, ak_a | ak_b, ak_a >> 10] + + for npc, akc in zip(np_arrays, ak_arrays): + np_ans = (npc % (2**max_bits)).astype(np.uint) if max_bits != -1 else npc + ak_ans = akc.to_ndarray().astype(np.uint) + assert np.all(np_ans == ak_ans) + + +def create_parser(): + parser = argparse.ArgumentParser(description="Run the bigint bitwise binops benchmarks") + parser.add_argument("hostname", help="Hostname of arkouda server") + parser.add_argument("port", type=int, help="Port of arkouda server") + parser.add_argument( + "-n", "--size", type=int, default=10**8, help="Problem size: length of arrays A and B" + ) + parser.add_argument( + "-t", "--trials", type=int, default=6, help="Number of times to run the benchmark" + ) + parser.add_argument( + "--max-bits", + type=int, + default=-1, + help="Maximum number of bits, so values > 2**max_bits will wraparound. -1 is interpreted as no maximum", + ) + parser.add_argument( + "--correctness-only", + default=False, + action="store_true", + help="Only check correctness, not performance.", + ) + parser.add_argument( + "-s", "--seed", default=None, type=int, help="Value to initialize random number generator" + ) + return parser + + +if __name__ == "__main__": + import sys + + parser = create_parser() + args = parser.parse_args() + ak.verbose = False + ak.connect(server=args.hostname, port=args.port) + + if args.correctness_only: + check_correctness(args.max_bits, args.seed) + sys.exit(0) + + print("array size = {:,}".format(args.size)) + print("number of trials = ", args.trials) + time_ak_bitwise_binops(args.size, args.trials, args.max_bits, args.seed) + + sys.exit(0) diff --git a/benchmarks/bigint_conversion.py b/benchmarks/bigint_conversion.py index 5635be940e..9dbc31d958 100644 --- a/benchmarks/bigint_conversion.py +++ b/benchmarks/bigint_conversion.py @@ -43,8 +43,15 @@ def time_bigint_conversion(N_per_locale, trials, seed, max_bits): (2 * a.size * a.itemsize) / 2**30 / avg_conversion ) ) - assert ak.all(a == u_arrays[0]) - assert ak.all(b == u_arrays[1]) + if max_bits == -1 or max_bits > 128: + assert ak.all(a == u_arrays[0]) + assert ak.all(b == u_arrays[1]) + elif max_bits <= 64: + assert ak.all(b % (2**max_bits - 1) == u_arrays[0]) + else: + max_bits -= 64 + assert ak.all(a & (2**max_bits - 1) == u_arrays[0]) + assert ak.all(b == u_arrays[1]) def check_correctness(seed, max_bits): @@ -54,8 +61,15 @@ def check_correctness(seed, max_bits): b = ak.randint(0, N, N, dtype=ak.uint64, seed=seed) u_arrays = ak.bigint_from_uint_arrays([a, b], max_bits=max_bits).bigint_to_uint_arrays() - assert ak.all(a == u_arrays[0]) - assert ak.all(b == u_arrays[1]) + if max_bits == -1 or max_bits > 128: + assert ak.all(a == u_arrays[0]) + assert ak.all(b == u_arrays[1]) + elif max_bits <= 64: + assert ak.all(b % (2**max_bits - 1) == u_arrays[0]) + else: + max_bits -= 64 + assert ak.all(a & (2**max_bits - 1) == u_arrays[0]) + assert ak.all(b == u_arrays[1]) def create_parser(): diff --git a/benchmarks/bigint_stream.py b/benchmarks/bigint_stream.py new file mode 100644 index 0000000000..84043495cb --- /dev/null +++ b/benchmarks/bigint_stream.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +import argparse +import time + +import numpy as np + +import arkouda as ak + + +def time_ak_stream(N_per_locale, trials, alpha, max_bits, random, seed): + print(">>> arkouda bigint stream") + cfg = ak.get_config() + N = N_per_locale * cfg["numLocales"] + print("numLocales = {}, N = {:,}".format(cfg["numLocales"], N)) + # default tot_bytes to ones case + tot_bytes = N * 8 * 3 + if random or seed is not None: + a1 = ak.randint(0, 2**32, N, dtype=ak.uint64, seed=seed) + a2 = ak.randint(0, 2**32, N, dtype=ak.uint64, seed=seed) + a = ak.bigint_from_uint_arrays([a1, a2], max_bits=max_bits) + b1 = ak.randint(0, 2**32, N, dtype=ak.uint64, seed=seed) + b2 = ak.randint(0, 2**32, N, dtype=ak.uint64, seed=seed) + b = ak.bigint_from_uint_arrays([b1, b2], max_bits=max_bits) + # update tot_bytes to account for using 2 uint64 + tot_bytes *= 2 + else: + a = ak.bigint_from_uint_arrays([ak.ones(N, dtype=ak.uint64)], max_bits=max_bits) + b = ak.bigint_from_uint_arrays([ak.ones(N, dtype=ak.uint64)], max_bits=max_bits) + + timings = [] + for i in range(trials): + start = time.time() + c = a + b * alpha + end = time.time() + timings.append(end - start) + tavg = sum(timings) / trials + + print("Average bigint stream time = {:.4f} sec".format(tavg)) + bytes_per_sec = tot_bytes / tavg + print("Average bigint stream rate = {:.2f} GiB/sec".format(bytes_per_sec / 2**30)) + + +def check_correctness(alpha, max_bits, random, seed): + N = 10**4 + if seed is not None: + np.random.seed(seed) + if random or seed is not None: + a = np.random.randint(0, 2**32, N) + b = np.random.randint(0, 2**32, N) + else: + a = np.ones(N, dtype=np.uint) + b = np.ones(N, dtype=np.uint) + npc = a + b * alpha + akc = ( + ak.array(a, dtype=ak.bigint, max_bits=max_bits) + + ak.array(b, dtype=ak.bigint, max_bits=max_bits) * alpha + ) + np_ans = (npc % (2 ** max_bits)).astype(np.uint) if max_bits != -1 else npc + ak_ans = akc.to_ndarray().astype(np.uint) + assert np.all(np_ans == ak_ans) + + +def create_parser(): + parser = argparse.ArgumentParser(description="Run the bigint stream benchmark: C = A + alpha*B") + parser.add_argument("hostname", help="Hostname of arkouda server") + parser.add_argument("port", type=int, help="Port of arkouda server") + parser.add_argument( + "-n", "--size", type=int, default=10**8, help="Problem size: length of arrays A and B" + ) + parser.add_argument( + "-t", "--trials", type=int, default=6, help="Number of times to run the benchmark" + ) + parser.add_argument( + "--max-bits", + type=int, + default=-1, + help="Maximum number of bits, so values > 2**max_bits will wraparound. -1 is interpreted as no maximum", + ) + parser.add_argument( + "-r", + "--randomize", + default=False, + action="store_true", + help="Fill arrays with random values instead of ones", + ) + parser.add_argument("-a", "--alpha", default=1, help="Scalar multiple") + parser.add_argument( + "--correctness-only", + default=False, + action="store_true", + help="Only check correctness, not performance.", + ) + parser.add_argument( + "-s", "--seed", default=None, type=int, help="Value to initialize random number generator" + ) + return parser + + +if __name__ == "__main__": + import sys + + parser = create_parser() + args = parser.parse_args() + args.alpha = int(args.alpha) + ak.verbose = False + ak.connect(server=args.hostname, port=args.port) + + if args.correctness_only: + check_correctness(args.alpha, args.max_bits, args.randomize, args.seed) + sys.exit(0) + + print("array size = {:,}".format(args.size)) + print("number of trials = ", args.trials) + time_ak_stream(args.size, args.trials, args.alpha, args.max_bits, args.randomize, args.seed) + + sys.exit(0) diff --git a/benchmarks/graph_infra/arkouda.graph b/benchmarks/graph_infra/arkouda.graph index 54afb38dea..a8f8e528bf 100644 --- a/benchmarks/graph_infra/arkouda.graph +++ b/benchmarks/graph_infra/arkouda.graph @@ -147,3 +147,15 @@ graphkeys: bigint_from_uint_arrays GiB/s, bigint_to_uint_arrays GiB/s files: bigint_conversion.dat, bigint_conversion.dat graphtitle: Bigint Conversion Performance ylabel: Performance (GiB/s) + +perfkeys: Average bigint stream rate = +graphkeys: bigint stream GiB/s +files: bigint_stream.dat +graphtitle: Bigint Stream Performance +ylabel: Performance (GiB/s) + +perfkeys: Average bigint AND rate =, Average bigint OR rate =, Average bigint SHIFT rate = +graphkeys: bigint AND GiB/s, bigint OR GiB/s, bigint SHIFT GiB/s +files: bigint_bitwise_binops.dat, bigint_bitwise_binops.dat, bigint_bitwise_binops.dat +graphtitle: Bigint Bitwise Binops Performance +ylabel: Performance (GiB/s) diff --git a/benchmarks/graph_infra/bigint_bitwise_binops.perfkeys b/benchmarks/graph_infra/bigint_bitwise_binops.perfkeys new file mode 100644 index 0000000000..ac72fe9c30 --- /dev/null +++ b/benchmarks/graph_infra/bigint_bitwise_binops.perfkeys @@ -0,0 +1,6 @@ +Average bigint AND time = +Average bigint AND rate = +Average bigint OR time = +Average bigint OR rate = +Average bigint SHIFT time = +Average bigint SHIFT rate = diff --git a/benchmarks/graph_infra/bigint_stream.perfkeys b/benchmarks/graph_infra/bigint_stream.perfkeys new file mode 100644 index 0000000000..7e75908c69 --- /dev/null +++ b/benchmarks/graph_infra/bigint_stream.perfkeys @@ -0,0 +1,2 @@ +Average bigint stream time = +Average bigint stream rate = diff --git a/benchmarks/run_benchmarks.py b/benchmarks/run_benchmarks.py index 295f351a90..586e881803 100755 --- a/benchmarks/run_benchmarks.py +++ b/benchmarks/run_benchmarks.py @@ -49,6 +49,8 @@ "dataframe", "encode", "bigint_conversion", + "bigint_stream", + "bigint_bitwise_binops", ] if os.getenv("ARKOUDA_SERVER_PARQUET_SUPPORT"): diff --git a/src/BigIntMsg.chpl b/src/BigIntMsg.chpl index babedb4ae9..9e2fe61402 100644 --- a/src/BigIntMsg.chpl +++ b/src/BigIntMsg.chpl @@ -37,10 +37,11 @@ module BigIntMsg { if max_bits != -1 { // modBy should always be non-zero since we start at 1 and left shift - var modBy = 1:bigint; - modBy <<= max_bits; - forall bA in bigIntArray with (var local_modBy = modBy) { - bA.mod(bA, local_modBy); + var max_size = 1:bigint; + max_size <<= max_bits; + max_size -= 1; + forall bA in bigIntArray with (var local_max_size = max_size) { + bA &= local_max_size; } } diff --git a/src/BinOp.chpl b/src/BinOp.chpl index 602d8dd659..56db0836f9 100644 --- a/src/BinOp.chpl +++ b/src/BinOp.chpl @@ -1099,10 +1099,11 @@ module BinOp var has_max_bits = max_bits != -1; if has_max_bits { max_size <<= max_bits; + max_size -= 1; } ref la = l.a; ref ra = r.a; - var tmp = makeDistArray(la.size, bigint); + var tmp = if l.etype == bigint then la else if l.etype == bool then la:int:bigint else la:bigint; // these cases are not mutually exclusive, // so we have a flag to track if tmp is ever populated var visted = false; @@ -1114,19 +1115,39 @@ module BinOp // both being bigint select op { when "&" { - tmp = la & ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t &= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "|" { - tmp = la | ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t |= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "^" { - tmp = la ^ ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t ^= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "/" { - tmp = la / ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t /= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } } @@ -1138,7 +1159,12 @@ module BinOp // can't shift a bigint by a bigint select op { when "<<" { - tmp = la << ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t <<= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when ">>" { @@ -1148,7 +1174,12 @@ module BinOp var divideBy = makeDistArray(la.size, bigint); divideBy = 1:bigint; divideBy <<= ra; - tmp = la / divideBy; + forall (t, dB) in zip(tmp, divideBy) with (var local_max_size = max_size) { + t /= dB; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "<<<" { @@ -1157,21 +1188,27 @@ module BinOp } // should be as simple as the below, see issue #2006 // return (la << ra) | (la >> (max_bits - ra)); - tmp = la << ra; var botBits = la; if r.etype == int { - var shift_amt = max_bits - ra; // cant just do botBits >>= shift_amt; - var divideBy = makeDistArray(la.size, bigint); - divideBy = 1:bigint; - divideBy <<= shift_amt; - botBits = botBits / divideBy; - tmp += botBits; + forall (t, ri, bot_bits) in zip(tmp, ra, botBits) with (var local_max_size = max_size) { + t <<= ri; + var div_by = 1:bigint; + var shift_amt = max_bits - ri; + div_by <<= shift_amt; + bot_bits /= div_by; + t += bot_bits; + t &= local_max_size; + } } else { - var shift_amt = max_bits:uint - ra; - botBits >>= shift_amt; - tmp += botBits; + forall (t, ri, bot_bits) in zip(tmp, ra, botBits) with (var local_max_size = max_size) { + t <<= ri; + var shift_amt = max_bits:uint - ri; + bot_bits >>= shift_amt; + t += bot_bits; + t &= local_max_size; + } } visted = true; } @@ -1181,23 +1218,16 @@ module BinOp } // should be as simple as the below, see issue #2006 // return (la >> ra) | (la << (max_bits - ra)); - tmp = la; // cant just do tmp >>= ra; - var divideBy = makeDistArray(la.size, bigint); - divideBy = 1:bigint; - divideBy <<= ra; - tmp = tmp / divideBy; - var topBits = la; - if r.etype == int { - var shift_amt = max_bits - ra; - topBits <<= shift_amt; - tmp += topBits; - } - else { - var shift_amt = max_bits:uint - ra; - topBits <<= shift_amt; - tmp += topBits; + forall (t, ri, tB) in zip(tmp, ra, topBits) with (var local_max_size = max_size) { + var div_by = 1:bigint; + div_by <<= ri; + t /= div_by; + var shift_amt = if r.etype == int then max_bits - ri else max_bits:uint - ri; + tB <<= shift_amt; + t += tB; + t &= local_max_size; } visted = true; } @@ -1205,13 +1235,33 @@ module BinOp } select op { when "//" { // floordiv - [(ei,li,ri) in zip(tmp,la,ra)] ei = if ri != 0 then li/ri else 0:bigint; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + if ri != 0 { + t /= ri; + } + else { + t = 0:bigint; + } + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "%" { // modulo // we only do in place mod when ri != 0, tmp will be 0 in other locations // we can't use ei = li % ri because this can result in negatives - [(ei,li,ri) in zip(tmp,la,ra)] if ri != 0 then ei.mod(li, ri); + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + if ri != 0 { + t.mod(t, ri); + } + else { + t = 0:bigint; + } + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "**" { @@ -1219,10 +1269,14 @@ module BinOp throw new Error("Attempt to exponentiate base of type BigInt to negative exponent"); } if has_max_bits { - [(ei,li,ri) in zip(tmp,la,ra)] ei.powMod(li, ri, max_size); + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t.powMod(t, ri, local_max_size + 1); + } } else { - tmp = la ** ra:int; + forall (t, ri) in zip(tmp, ra) { + t **= ri:uint; + } } visted = true; } @@ -1233,15 +1287,30 @@ module BinOp (r.etype == bigint && (l.etype == int || l.etype == uint || l.etype == bool)) { select op { when "+" { - tmp = la + ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t += ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "-" { - tmp = l.a - r.a; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t -= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "*" { - tmp = l.a * r.a; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t *= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } } @@ -1249,13 +1318,7 @@ module BinOp if !visted { throw new Error("Unsupported operation: " + l.etype:string +" "+ op +" "+ r.etype:string); } - else { - if has_max_bits { - // max_size should always be non-zero since we start at 1 and left shift - tmp.mod(tmp, max_size); - } - return (tmp, max_bits); - } + return (tmp, max_bits); } proc doBigIntBinOpvvBoolReturn(l, r, op: string) throws { @@ -1292,9 +1355,10 @@ module BinOp var has_max_bits = max_bits != -1; if has_max_bits { max_size <<= max_bits; + max_size -= 1; } ref la = l.a; - var tmp = makeDistArray(la.size, bigint); + var tmp = if l.etype == bigint then la else if l.etype == bool then la:int:bigint else la:bigint; // these cases are not mutually exclusive, // so we have a flag to track if tmp is ever populated var visted = false; @@ -1306,19 +1370,39 @@ module BinOp // both being bigint select op { when "&" { - tmp = la & val; + forall t in tmp with (var local_val = val, var local_max_size = max_size) { + t &= local_val; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "|" { - tmp = la | val; + forall t in tmp with (var local_val = val, var local_max_size = max_size) { + t |= local_val; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "^" { - tmp = la ^ val; + forall t in tmp with (var local_val = val, var local_max_size = max_size) { + t ^= local_val; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "/" { - tmp = la / val; + forall t in tmp with (var local_val = val, var local_max_size = max_size) { + t /= local_val; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } } @@ -1330,7 +1414,12 @@ module BinOp // can't shift a bigint by a bigint select op { when "<<" { - tmp = la << val; + forall t in tmp with (var local_val = val, var local_max_size = max_size) { + t <<= local_val; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when ">>" { @@ -1340,7 +1429,12 @@ module BinOp var divideBy = makeDistArray(la.size, bigint); divideBy = 1:bigint; divideBy <<= val; - tmp = la / divideBy; + forall (t, dB) in zip(tmp, divideBy) with (var local_max_size = max_size) { + t /= dB; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "<<<" { @@ -1349,21 +1443,27 @@ module BinOp } // should be as simple as the below, see issue #2006 // return (la << val) | (la >> (max_bits - val)); - tmp = la << val; var botBits = la; if val.type == int { var shift_amt = max_bits - val; // cant just do botBits >>= shift_amt; - var divideBy = makeDistArray(la.size, bigint); - divideBy = 1:bigint; - divideBy <<= shift_amt; - botBits = botBits / divideBy; - tmp += botBits; + forall (t, bot_bits) in zip(tmp, botBits) with (var local_val = val, var local_shift_amt = shift_amt, var local_max_size = max_size) { + t <<= local_val; + var div_by = 1:bigint; + div_by <<= local_shift_amt; + bot_bits /= div_by; + t += bot_bits; + t &= local_max_size; + } } else { var shift_amt = max_bits:uint - val; - botBits >>= shift_amt; - tmp += botBits; + forall (t, bot_bits) in zip(tmp, botBits) with (var local_val = val, var local_shift_amt = shift_amt, var local_max_size = max_size) { + t <<= local_val; + bot_bits >>= local_shift_amt; + t += bot_bits; + t &= local_max_size; + } } visted = true; } @@ -1373,23 +1473,16 @@ module BinOp } // should be as simple as the below, see issue #2006 // return (la >> val) | (la << (max_bits - val)); - tmp = la; // cant just do tmp >>= ra; - var divideBy = makeDistArray(la.size, bigint); - divideBy = 1:bigint; - divideBy <<= val; - tmp = tmp / divideBy; - var topBits = la; - if val.type == int { - var shift_amt = max_bits - val; - topBits <<= shift_amt; - tmp += topBits; - } - else { - var shift_amt = max_bits:uint - val; - topBits <<= shift_amt; - tmp += topBits; + var shift_amt = if val.type == int then max_bits - val else max_bits:uint - val; + forall (t, tB) in zip(tmp, topBits) with (var local_val = val, var local_shift_amt = shift_amt, var local_max_size = max_size) { + var div_by = 1:bigint; + div_by <<= local_val; + t /= div_by; + tB <<= local_shift_amt; + t += tB; + t &= local_max_size; } visted = true; } @@ -1397,13 +1490,33 @@ module BinOp } select op { when "//" { // floordiv - [(ei,li) in zip(tmp,la)] ei = if val != 0 then li/val else 0:bigint; + forall t in tmp with (var local_val = val, var local_max_size = max_size) { + if local_val != 0 { + t /= local_val; + } + else { + t = 0:bigint; + } + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "%" { // modulo // we only do in place mod when val != 0, tmp will be 0 in other locations // we can't use ei = li % val because this can result in negatives - [(ei,li) in zip(tmp,la)] if val != 0 then ei.mod(li, val); + forall (t, li) in zip(tmp, la) with (var local_val = val, var local_max_size = max_size) { + if local_val != 0 { + t.mod(t, local_val); + } + else { + t = 0:bigint; + } + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "**" { @@ -1411,10 +1524,14 @@ module BinOp throw new Error("Attempt to exponentiate base of type BigInt to negative exponent"); } if has_max_bits { - [(ei,li) in zip(tmp,la)] ei.powMod(li, val, max_size); + forall t in tmp with (var local_val = val, var local_max_size = max_size) { + t.powMod(t, local_val, local_max_size + 1); + } } else { - tmp = la ** val:int; + forall t in tmp with (var local_val = val) { + t **= local_val:uint; + } } visted = true; } @@ -1425,15 +1542,30 @@ module BinOp (val.type == bigint && (l.etype == int || l.etype == uint || l.etype == bool)) { select op { when "+" { - tmp = la + val; + forall t in tmp with (var local_val = val, var local_max_size = max_size) { + t += local_val; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "-" { - tmp = l.a - val; + forall t in tmp with (var local_val = val, var local_max_size = max_size) { + t -= local_val; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "*" { - tmp = l.a * val; + forall t in tmp with (var local_val = val, var local_max_size = max_size) { + t *= local_val; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } } @@ -1441,34 +1573,42 @@ module BinOp if !visted { throw new Error("Unsupported operation: " + l.etype:string +" "+ op +" "+ val.type:string); } - else { - if has_max_bits { - // max_size should always be non-zero since we start at 1 and left shift - tmp.mod(tmp, max_size); - } - return (tmp, max_bits); - } + return (tmp, max_bits); } proc doBigIntBinOpvsBoolReturn(l, val, op: string) throws { + ref la = l.a; + var tmp = makeDistArray(la.size, bool); select op { when "<" { - return l.a < val; + forall (t, li) in zip(tmp, la) with (var local_val = val) { + t = (li < local_val); + } } when ">" { - return l.a > val; + forall (t, li) in zip(tmp, la) with (var local_val = val) { + t = (li > local_val); + } } when "<=" { - return l.a <= val; + forall (t, li) in zip(tmp, la) with (var local_val = val) { + t = (li <= local_val); + } } when ">=" { - return l.a >= val; + forall (t, li) in zip(tmp, la) with (var local_val = val) { + t = (li >= local_val); + } } when "==" { - return l.a == val; + forall (t, li) in zip(tmp, la) with (var local_val = val) { + t = (li == local_val); + } } when "!=" { - return l.a != val; + forall (t, li) in zip(tmp, la) with (var local_val = val) { + t = (li != local_val); + } } otherwise { // we should never reach this since we only enter this proc @@ -1476,6 +1616,7 @@ module BinOp throw new Error("Unsupported operation: " +" "+ l.etype:string + op +" "+ val.type:string); } } + return tmp; } proc doBigIntBinOpsv(val, r, op: string) throws { @@ -1484,9 +1625,12 @@ module BinOp var has_max_bits = max_bits != -1; if has_max_bits { max_size <<= max_bits; + max_size -= 1; } ref ra = r.a; var tmp = makeDistArray(ra.size, bigint); + // TODO we have to cast to bigint until chape issue #21290 is resolved, see issue #2007 + tmp = if val.type == bool then val:int:bigint else val:bigint; // these cases are not mutually exclusive, // so we have a flag to track if tmp is ever populated var visted = false; @@ -1498,19 +1642,39 @@ module BinOp // both being bigint select op { when "&" { - tmp = val & ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t &= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "|" { - tmp = val | ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t |= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "^" { - tmp = val ^ ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t ^= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "/" { - tmp = val / ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t /= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } } @@ -1522,7 +1686,12 @@ module BinOp // can't shift a bigint by a bigint select op { when "<<" { - tmp = val << ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t <<= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when ">>" { @@ -1532,7 +1701,12 @@ module BinOp var divideBy = makeDistArray(ra.size, bigint); divideBy = 1:bigint; divideBy <<= ra; - tmp = val / divideBy; + forall (t, dB) in zip(tmp, divideBy) with (var local_max_size = max_size) { + t /= dB; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "<<<" { @@ -1540,23 +1714,29 @@ module BinOp throw new Error("Must set max_bits to rotl"); } // should be as simple as the below, see issue #2006 - // return (val << ra) | (val >> (max_bits - ra)); - tmp = val << ra; + // return (la << ra) | (la >> (max_bits - ra)); var botBits = makeDistArray(ra.size, bigint); botBits = val; if r.etype == int { - var shift_amt = max_bits - ra; // cant just do botBits >>= shift_amt; - var divideBy = makeDistArray(ra.size, bigint); - divideBy = 1:bigint; - divideBy <<= shift_amt; - botBits = botBits / divideBy; - tmp += botBits; + forall (t, ri, bot_bits) in zip(tmp, ra, botBits) with (var local_max_size = max_size) { + t <<= ri; + var div_by = 1:bigint; + var shift_amt = max_bits - ri; + div_by <<= shift_amt; + bot_bits /= div_by; + t += bot_bits; + t &= local_max_size; + } } else { - var shift_amt = max_bits:uint - ra; - botBits >>= shift_amt; - tmp += botBits; + forall (t, ri, bot_bits) in zip(tmp, ra, botBits) with (var local_max_size = max_size) { + t <<= ri; + var shift_amt = max_bits:uint - ri; + bot_bits >>= shift_amt; + t += bot_bits; + t &= local_max_size; + } } visted = true; } @@ -1565,25 +1745,18 @@ module BinOp throw new Error("Must set max_bits to rotr"); } // should be as simple as the below, see issue #2006 - // return (val >> ra) | (val << (max_bits - ra)); - tmp = val; + // return (la >> ra) | (la << (max_bits - ra)); // cant just do tmp >>= ra; - var divideBy = makeDistArray(ra.size, bigint); - divideBy = 1:bigint; - divideBy <<= ra; - tmp = tmp / divideBy; - var topBits = makeDistArray(ra.size, bigint); topBits = val; - if r.etype == int { - var shift_amt = max_bits - ra; - topBits <<= shift_amt; - tmp += topBits; - } - else { - var shift_amt = max_bits:uint - ra; - topBits <<= shift_amt; - tmp += topBits; + forall (t, ri, tB) in zip(tmp, ra, topBits) with (var local_max_size = max_size) { + var div_by = 1:bigint; + div_by <<= ri; + t /= div_by; + var shift_amt = if r.etype == int then max_bits - ri else max_bits:uint - ri; + tB <<= shift_amt; + t += tB; + t &= local_max_size; } visted = true; } @@ -1591,24 +1764,46 @@ module BinOp } select op { when "//" { // floordiv - [(ei,ri) in zip(tmp,ra)] ei = if ri != 0 then val/ri else 0:bigint; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + if ri != 0 { + t /= ri; + } + else { + t = 0:bigint; + } + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "%" { // modulo - // we only do in place mod when val != 0, tmp will be 0 in other locations - // we can't use ei = li % val because this can result in negatives - [(ei,ri) in zip(tmp,ra)] if ri != 0 then ei.mod(val, ri); + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + if ri != 0 { + t.mod(t, ri); + } + else { + t = 0:bigint; + } + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "**" { - if || reduce(ra<0) { + if || reduce (ra<0) { throw new Error("Attempt to exponentiate base of type BigInt to negative exponent"); } if has_max_bits { - [(ei,ri) in zip(tmp,ra)] ei.powMod(val, ri, max_size); + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t.powMod(t, ri, local_max_size + 1); + } } else { - tmp = val ** ra:int; + forall (t, ri) in zip(tmp, ra) { + t **= ri:uint; + } } visted = true; } @@ -1617,19 +1812,32 @@ module BinOp if (val.type == bigint && r.etype == bigint) || (val.type == bigint && (r.etype == int || r.etype == uint || r.etype == bool)) || (r.etype == bigint && (val.type == int || val.type == uint || val.type == bool)) { - // TODO we have to cast to bigint until chape issue #21290 is resolved, see issue #2007 - var cast_val = if val.type == bool then val:int:bigint else val:bigint; select op { when "+" { - tmp = cast_val + ra; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t += ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "-" { - tmp = cast_val - r.a; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t -= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } when "*" { - tmp = cast_val * r.a; + forall (t, ri) in zip(tmp, ra) with (var local_max_size = max_size) { + t *= ri; + if has_max_bits { + t &= local_max_size; + } + } visted = true; } } @@ -1637,34 +1845,42 @@ module BinOp if !visted { throw new Error("Unsupported operation: " + val.type:string +" "+ op +" "+ r.etype:string); } - else { - if has_max_bits { - // max_size should always be non-zero since we start at 1 and left shift - tmp.mod(tmp, max_size); - } - return (tmp, max_bits); - } + return (tmp, max_bits); } proc doBigIntBinOpsvBoolReturn(val, r, op: string) throws { + ref ra = r.a; + var tmp = makeDistArray(ra.size, bool); select op { when "<" { - return val < r.a; + forall (t, ri) in zip(tmp, ra) with (var local_val = val) { + t = (local_val < ri); + } } when ">" { - return val > r.a; + forall (t, ri) in zip(tmp, ra) with (var local_val = val) { + t = (local_val > ri); + } } when "<=" { - return val <= r.a; + forall (t, ri) in zip(tmp, ra) with (var local_val = val) { + t = (local_val <= ri); + } } when ">=" { - return val >= r.a; + forall (t, ri) in zip(tmp, ra) with (var local_val = val) { + t = (local_val >= ri); + } } when "==" { - return val == r.a; + forall (t, ri) in zip(tmp, ra) with (var local_val = val) { + t = (local_val == ri); + } } when "!=" { - return val != r.a; + forall (t, ri) in zip(tmp, ra) with (var local_val = val) { + t = (local_val != ri); + } } otherwise { // we should never reach this since we only enter this proc @@ -1672,5 +1888,6 @@ module BinOp throw new Error("Unsupported operation: " + val.type:string +" "+ op +" "+ r.etype:string); } } + return tmp; } }