diff --git a/bench/groupby_shapes/grouping_scaling.py b/bench/groupby_shapes/grouping_scaling.py new file mode 100644 index 000000000..abd354547 --- /dev/null +++ b/bench/groupby_shapes/grouping_scaling.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +"""Portable grouping-family scaling matrix, with typed baseline comparisons.""" +import argparse +import csv +import hashlib +import json +import os +from pathlib import Path +import re +import shutil +import statistics +import subprocess +import tempfile + +from scaling_common import compare_csv, profile_phases + +# (preparation, select fields, keys, optional selection) +CASES = {} +for kind in ("BOOL", "U8", "I16", "I32", "I64", "F32", "F64", "TIME"): + source = "(> v 0)" if kind == "BOOL" else "(as 'U8 (+ v 50))" if kind == "U8" else f"(as '{kind} v)" + # F32 conversion is admitted by the expression materializer. + prep = f"(set t (select {{from:t i:i k:k j:j v:{source} f:f w:w}}))" + CASES[f"sum-{kind.lower()}"] = (prep, "s:(sum v)", "k", "") + CASES[f"extrema-{kind.lower()}"] = (prep, "s:(min v) hi:(max v)", "k", "") +CASES.update({ + "count": ("", "s:(count v)", "k", ""), + "statistics": ("", "s:(avg f) v:(var f) sd:(stddev f)", "k", ""), + "binary": ("", "s:(wsum f w) a:(wavg f w) p:(pearson_corr f w) c:(cov f w)", "k", ""), + "truth": ("", "s:(all v) a:(any v)", "k", ""), + "product": ("(set t (update {from:t v:(+ (% i 2) -1)}))", "s:(prod v)", "k", ""), + "median": ("", "s:(med v)", "k", ""), + "quantile": ("", "s:(quantile v 0.25)", "k", ""), + "mode": ("", "s:(mode v)", "k", ""), + "top-bottom": ("", "s:(top v 3) b:(bot v 3)", "k", ""), + "first-last": ("", "s:(first v) z:(last v)", "k", ""), + "mixed": ("", "s:(sum v) m:(med v) a:(avg f)", "k", ""), + "distinct": ("", "s:(count (distinct v))", "k", ""), + "composite": ("", "s:(sum v) m:(min v)", "[k j]", ""), + "selected": ("", "s:(sum v) m:(min v)", "k", "where:(> v 25)"), + "selected-indexed": ("", "s:(sum v) m:(med v)", "[k j]", "where:(> v 25)"), + "sparse": ("(set t (select {from:t i:i k:(* (as 'I64 k) 1000000007) j:j v:v f:f w:w}))", "s:(sum v)", "k", ""), + "clustered": ("(set t (update {from:t k:(as 'I32 (/ i 8))}))", "s:(sum v)", "k", ""), + "skew": ("(set t (update {from:t k:(as 'I32 0N) where:(< (% i 10) 4)}))", "s:(sum v)", "k", ""), + "skew-indexed": ("(set t (update {from:t k:(as 'I32 0N) where:(< (% i 10) 4)}))", "s:(sum v) m:(med v)", "k", ""), + "float-key": ("(set t (select {from:t i:i k:(as 'F64 k) j:j v:v f:f w:w}))", "s:(sum v)", "k", ""), + "float-indexed": ("(set t (select {from:t i:i k:(as 'F64 k) j:j v:v f:f w:w}))", "s:(sum v) m:(med v)", "k", ""), + "symbol-key": ("(set t (table [i k j v f w] (list i (at ['a 'b 'c 'd] (% i 4)) (at t 'j) (at t 'v) (at t 'f) (at t 'w))))", "s:(sum v)", "k", ""), + "string-key": ('(set t (table [i k j v f w] (list i (at ["pooled long alpha value" "beta" "gamma" "delta"] (% i 4)) (at t \'j) (at t \'v) (at t \'f) (at t \'w))))', "s:(sum v)", "k", ""), + "string-indexed": ('(set t (table [i k j v f w] (list i (at ["pooled long alpha value" "beta" "gamma" "delta"] (% i 4)) (at t \'j) (at t \'v) (at t \'f) (at t \'w))))', "s:(sum v) m:(med v)", "k", ""), + "wide-extrema": ('(set t (table [i k j v f w text] (list i (at t \'k) (at t \'j) (at t \'v) (at t \'f) (at t \'w) (at ["pooled long alpha value" "beta" "gamma" "delta"] (% i 4)))))', "s:(min text) z:(max text)", "k", ""), +}) +for kind in ("DATE", "TIMESTAMP"): + CASES[f"extrema-{kind.lower()}"] = ( + f"(set t (select {{from:t i:i k:k j:j v:(as '{kind} v) f:f w:w}}))", + "s:(min v) hi:(max v)", "k", "") +for name, fields in (("wide-mode", "s:(mode text)"), + ("wide-top-bottom", "s:(top text 3) b:(bot text 3)")): + CASES[name] = (CASES["wide-extrema"][0], fields, "k", "") +for name, value in (("extrema-ascending", "i"), ("extrema-descending", "(- 0 i)")): + CASES[name] = (f"(set t (select {{from:t i:i k:k j:j v:(as 'I32 {value}) f:f w:w}}))", + "s:(min v) hi:(max v)", "k", "") + +CASES["few-groups-indexed"] = ( + "(set t (update {from:t k:(as 'I32 (% i 7))}))", + "n:(count v) f:(first v) l:(last v)", "k", "") + +CASES["hot-top-ascending"] = ( + "(set t (select {from:t i:i k:(as 'I32 (* i 0)) j:j v:i f:f w:w}))", + "s:(top v 3) b:(bot v 3)", "k", "") + +CASES["guid-key"] = ( + "(set g (as 'GUID (list \"00000000-0000-0000-0000-000000000001\" \"00000000-0000-0000-0000-000000000002\" \"00000000-0000-0000-0000-000000000003\" \"\")))\n" + "(set t (table [i k j v f w] (list i (at g (% i 4)) (at t 'j) (at t 'v) (at t 'f) (at t 'w))))", + "s:(sum v) m:(med v)", "k", "") +CASES["list-key"] = ( + "(set g (list [1 2] [3 4] ['a 'b] [\"pooled list string\" \"x\"]))\n" + "(set t (table [i k j v f w] (list i (at g (% i 4)) (at t 'j) (at t 'v) (at t 'f) (at t 'w))))", + "s:(sum v) m:(med v)", "k", "") +CASES["unique-keys"] = ("(set t (update {from:t k:(as 'I32 i)}))", "s:(sum v)", "k", "") +CASES["nonnull"] = ("(set t (update {from:t v:(- (% i 101) 50)}))", "s:(sum v) a:(avg v)", "k", "") + +for direction, value in (("ascending", "i"), ("descending", "(- 0 i)")): + CASES[f"hot-min-{direction}"] = ( + f"(set t (select {{from:t i:i k:k j:j v:(as 'I32 {value}) f:f w:w}}))\n" + "(set t (update {from:t k:(as 'I32 0) where:(== (% i 2) 0)}))", + "s:(min v)", "k", "") +CASES["all-null-key"] = ("(set t (update {from:t k:(as 'I32 0N)}))", "s:(sum v) a:(avg v)", "k", "") + +CASES["distinct-skew"] = ( + "(set t (update {from:t k:(as 'I32 0N) v:0 where:(< (% i 10) 4)}))", + "s:(count (distinct v))", "k", "") +CASES["distinct-correlated"] = ("", "s:(count (distinct k))", "k", "") +CASES["symbol-extrema"] = (CASES["wide-extrema"][0].replace('["pooled long alpha value" "beta" "gamma" "delta"]', "['alpha 'beta 'gamma 'delta]"), "s:(min text) z:(max text)", "k", "") +CASES["symbol-top-bottom"] = (CASES["symbol-extrema"][0], "s:(top text 3) z:(bot text 3)", "k", "") +CASES["distinct-string-values"] = (CASES["wide-extrema"][0], "s:(count (distinct text))", "k", "") +for kind in ("string", "guid", "list"): + CASES[f"distinct-{kind}-keys"] = (CASES[f"{kind}-key"][0], "s:(count (distinct v))", "k", "") + CASES[f"mixed-distinct-{kind}-keys"] = ( + CASES[f"{kind}-key"][0], "s:(count (distinct v)) total:(sum v)", "k", "") +CASES["mixed-distinct-string-values"] = ( + CASES["wide-extrema"][0], "s:(count (distinct text)) total:(sum v)", "k", "") + +# A single large group must share work across workers too. +for family in ("first-last", "symbol-extrema", "wide-extrema", "mode", + "wide-mode", "top-bottom", "wide-top-bottom", "symbol-top-bottom"): + prep, fields, keys, selection = CASES[family] + prep += "\n(set t (update {from:t k:(as 'I32 0)}))" + if family == "first-last": + prep += "\n(set t (update {from:t v:0N}))" + CASES[f"hot-{family}"] = (prep, fields, keys, selection) + +CASES["hot-max-k"] = ("(set t (update {from:t k:(as 'I32 0)}))", + "s:(top v 1024) b:(bot v 1024)", "k", "") + + +def main(): + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("--binary", type=Path, required=True) + ap.add_argument("--baseline", type=Path) + ap.add_argument("--baseline-skip-cases", default="", + help="cases with a recorded baseline failure; current results still checked across workers") + ap.add_argument("--workers", default="1,2,4,8,16,default") + ap.add_argument("--rounds", type=int, default=3) + ap.add_argument("--rows", type=int, default=1000000) + ap.add_argument("--cases", default=",".join(CASES)) + ap.add_argument("--profile", action="store_true") + ap.add_argument("--stop-file", type=Path, help="stop between processes when this file exists") + ap.add_argument("--output", type=Path, required=True) + args = ap.parse_args() + cases, workers = args.cases.split(","), args.workers.split(",") + baseline_skip = set(filter(None, args.baseline_skip_cases.split(","))) + if any(c not in CASES for c in cases) or args.rounds < 1 or args.rows < 1: + ap.error("invalid cases, rounds or rows") + if any(w != "default" and (not w.isdecimal() or int(w) < 1) for w in workers): + ap.error("worker counts must be positive or default") + binaries = [("current", args.binary.resolve())] + if args.baseline: + binaries.insert(0, ("baseline", args.baseline.resolve())) + hashes = {name: hashlib.sha256(binary.read_bytes()).hexdigest() for name, binary in binaries} + env = os.environ.copy() + env.pop("RAYFORCE_CORES", None) + log_dir = args.output.with_suffix(".runs") + log_dir.mkdir(parents=True, exist_ok=True) + if baseline_skip - CASES.keys(): + ap.error("unknown baseline skip case") + matrix = [(case, worker, name, binary) for case in cases for worker in workers for name, binary in binaries + if not (name == "baseline" and case in baseline_skip)] + records = [] + with tempfile.TemporaryDirectory(prefix="rayforce-grouping-") as temp: + directory = Path(temp) + script, result, schema, rss, input_schema = (directory / f for f in ("query.rfl", "result.csv", "schema.csv", "rss", "input-schema.csv")) + warm_result, warm_schema = directory / "warm-result.csv", directory / "warm-schema.csv" + references = set() + for round_number in range(1, args.rounds + 1): + for case, worker, name, binary in matrix if round_number % 2 else reversed(matrix): + if args.stop_file and args.stop_file.exists(): + print(f"Stopped between processes; {len(records)} verified runs retained in {args.output}") + return + prep, fields, keys, selection = CASES[case] + key_list = "['k 'j]" if keys == "[k j]" else "['k]" + query = f"(select {{from:t by:{keys} {fields} {selection}}})" + setup = f"""(set i (til {args.rows})) +(set v (- (% i 101) 50)) +(set t (table [i k j v f w] (list i (as 'I32 (% (* i 17) 65536)) (as 'I32 (% i 7)) v (as 'F64 v) (as 'F64 (+ (% i 31) 1))))) +(set t (update {{from:t v:0N where:(== (% i 97) 0)}})) +{prep} +""" + text = setup + f"(println (count t))\n(println (timeit (set r {query})))\n(println (count r))\n" + text += (f"(println (timeit {query}))\n" * 5) + text += f"(set ordered (xasc r {key_list}))\n(.csv.write ordered {json.dumps(str(result))})\n" + # Untimed re-execution checks that warming does not change results. + text += f"(set warm_ordered (xasc {query} {key_list}))\n(.csv.write warm_ordered {json.dumps(str(warm_result))})\n" + text += "(.csv.write (table [column kind] (list (key warm_ordered) (map (fn [c] (at (meta (at warm_ordered c)) 'type)) (key warm_ordered)))) " + json.dumps(str(warm_schema)) + ")\n" + text += "(.csv.write (table [column kind] (list (key ordered) (map (fn [c] (at (meta (at ordered c)) 'type)) (key ordered)))) " + json.dumps(str(schema)) + ")\n" + text += "(.csv.write (table [column kind] (list (key t) (map (fn [c] (at (meta (at t c)) 'type)) (key t)))) " + json.dumps(str(input_schema)) + ")\n" + script.write_text(text) + for p in (result, schema, warm_result, warm_schema, input_schema, rss): + p.unlink(missing_ok=True) + command = ["/usr/bin/time", "-f", "%M", "-o", str(rss), str(binary), str(script)] + if worker != "default": + command += ["-c", worker] + if args.profile: + command += ["-t", "1"] + run = subprocess.run(command, env=env, capture_output=True, text=True) + (log_dir / f"{case}-{worker}-{name}-{round_number}.log").write_text(run.stdout + run.stderr) + if run.returncode: + raise RuntimeError(f"{case} failed: {run.stdout}\n{run.stderr}") + values = [float(line) for line in run.stdout.splitlines() if re.fullmatch(r"[0-9]+(?:\.[0-9]+)?", line.strip())] + if len(values) < 8 or not all(p.is_file() for p in (result, schema, warm_result, warm_schema, input_schema)): + raise RuntimeError(f"Incomplete {case}: {run.stdout}\n{run.stderr}") + reference, ref_schema = directory / f"{case}.csv", directory / f"{case}-schema.csv" + if case not in references: + shutil.copyfile(result, reference) + shutil.copyfile(schema, ref_schema) + references.add(case) + if schema.read_bytes() != warm_schema.read_bytes(): + raise RuntimeError(f"Cold/warm result types differ for {case}") + compare_csv(result, warm_result, schema, key_names=("k", "j")) + if schema.read_bytes() != ref_schema.read_bytes(): + raise RuntimeError(f"Result types differ for {case}") + try: + compare_csv(reference, result, schema, key_names=("k", "j")) + except RuntimeError: + shutil.copyfile(reference, log_dir / "mismatch-reference.csv") + shutil.copyfile(result, log_dir / "mismatch-current.csv") + shutil.copyfile(schema, log_dir / "mismatch-schema.csv") + raise + with input_schema.open(newline="") as stream: + input_types = {r["column"]: r["kind"] for r in csv.DictReader(stream)} + if case.startswith("string-") and input_types["k"] != "STR": + raise RuntimeError("String fixture did not produce STR keys") + if case == "symbol-key" and input_types["k"] != "SYM": + raise RuntimeError("Symbol fixture did not produce SYM keys") + if case in ("guid-key", "list-key") and input_types["k"] != case.split("-")[0].upper(): + raise RuntimeError("Wide key fixture has wrong input type") + if case in ("symbol-extrema", "symbol-top-bottom") and input_types["text"] != "SYM": + raise RuntimeError("Symbol fixture did not produce SYM values") + if case.startswith("wide-") and input_types["text"] != "STR": + raise RuntimeError("Wide fixture did not produce STR values") + records.append(dict(input_types=input_types, phases=profile_phases(run.stdout + run.stderr), + case=case, workers=worker, binary=name, path=str(binary), sha256=hashes[name], round=round_number, + rows=int(values[0]), groups=int(values[2]), cold_ms=values[1], warm_ms=values[3:8], + peak_rss_kib=int(rss.read_text()), result_sha256=hashlib.sha256(result.read_bytes()).hexdigest())) + args.output.write_text(json.dumps(records, indent=2) + "\n") + print(f"{case} {name} workers={worker} round={round_number}: cold={values[1]:.3f}, warm={statistics.median(values[3:8]):.3f} ms", flush=True) + print(f"Verified typed results for all {len(records)} runs.") + + +if __name__ == "__main__": + main() diff --git a/bench/groupby_shapes/nullable_time.rfl b/bench/groupby_shapes/nullable_time.rfl new file mode 100644 index 000000000..21c4d7ea7 --- /dev/null +++ b/bench/groupby_shapes/nullable_time.rfl @@ -0,0 +1,8 @@ +;; Synthetic nullable I32 keys with TIME values. +(set n 16000000) +(set group_count 1000000) +(set i (til n)) +(set g (% (* i 17) group_count)) +(set t (table [k tm] (list (as 'I32 (+ (* g 2) (div g 4))) (as 'TIME (% (* i 13) 86400000))))) +(set t (update {from:t k:(as 'I32 0N) where:(== k 0)})) +(map (fn [_] (println (timeit (select {from:t by:k s:(min tm)})))) (til 8)) diff --git a/bench/groupby_shapes/results/2026-09-15/baseline-failures.txt b/bench/groupby_shapes/results/2026-09-15/baseline-failures.txt new file mode 100644 index 000000000..e28d562d7 --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-15/baseline-failures.txt @@ -0,0 +1,24 @@ +Baseline failures reproduced on bb81e621 + +Use the baseline binary identified in environment.json: +python3 bench/groupby_shapes/grouping_scaling.py --binary /path/to/baseline --workers 1 --rounds 1 --cases CASE --output /tmp/CASE.json + +distinct-string-values: +=== rayforce fatal SIGSEGV at fault addr 0x0000000000000008 === + +mixed-distinct-string-values: +=== rayforce fatal SIGSEGV at fault addr 0x0000000000000000 === + +distinct-list-keys: +error: type: vec_new: type must be a positive concrete vector type, got LIST + +mixed-distinct-list-keys: +error: type: vec_new: type must be a positive concrete vector type, got LIST + +Direct indexed top/bottom-K kernel, nullable I64, K at group size: +Build topk_consumer.c against the baseline static library, then run: +RAYFORCE_CORES=1 /path/to/baseline-topk-consumer 10000 10000 +Result: top/bottom K differs from histogram oracle +The native serial heap was not heapified when nulls left fewer than K values. +No baseline speedup is reported for this failing shape; candidate worker runs +are checked against the histogram oracle. diff --git a/bench/groupby_shapes/results/2026-09-15/environment.json b/bench/groupby_shapes/results/2026-09-15/environment.json new file mode 100644 index 000000000..8117d7cdd --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-15/environment.json @@ -0,0 +1,77 @@ +{ + "kernel": "Linux-6.17.9-76061709-generic-x86_64-with-glibc2.35", + "cpu": { + "Architecture": "x86_64", + "CPU(s)": "28", + "On-line CPU(s) list": "0-27", + "Model name": "Intel(R) Core(TM) i7-14700", + "Thread(s) per core": "2", + "Core(s) per socket": "20", + "Socket(s)": "1", + "L2 cache": "28 MiB (11 instances)", + "L3 cache": "33 MiB (1 instance)" + }, + "compiler": "cc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0", + "workers": [ + "1", + "2", + "4", + "8", + "16", + "default" + ], + "fresh_processes_per_case": 3, + "warm_executions_per_process": 5, + "baseline_revision": "bb81e6215321a7cf2cdd83d7223d62909804dee8", + "governor": "powersave", + "scaling_driver": "intel_pstate", + "candidate_revision": "f61a4eda", + "baseline_sha256": "f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e", + "candidate_sha256": "1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353", + "build_flags": "-O3 -march=native -funroll-loops -fomit-frame-pointer -fno-math-errno -falign-functions=64 -fassociative-math -ffp-contract=fast -fno-signed-zeros -fno-trapping-math", + "physical_memory_kib": 65633828, + "allowed_cpus": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27 + ], + "kernel_baseline_sha256": "e174e083c2a474b6da59888401b742d784df685db28d8a0c63bf9e4ad424fe73", + "kernel_candidate_sha256": "96944ec28f56bdc442fbfc86b9578e9d553d97122976d3a2db6512fac1447825", + "acceptance_status": "complete", + "sanitizer_tests": "3828/3828", + "thread_sanitizer_tests": "24/24", + "kernel_oracle_runs": 90, + "thread_sanitizer_command": "RAYFORCE_CORES=3 setarch x86_64 -R ./rayforce.test.tsan -f agg_contract", + "synthetic_acceptance_status": "complete", + "synthetic_query_runs": 2700, + "synthetic_cases": 77, + "profile_runs": 68, + "integration_base_revision": "cb6b0371", + "kernel_candidate_revision": "f61a4eda", + "regression_repeat_runs": 130 +} diff --git a/bench/groupby_shapes/results/2026-09-15/grouping.csv b/bench/groupby_shapes/results/2026-09-15/grouping.csv new file mode 100644 index 000000000..f6882ab1f --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-15/grouping.csv @@ -0,0 +1,901 @@ +case,workers,binary,processes,cold_median_ms,cold_min_ms,cold_max_ms,warm_median_ms,warm_min_ms,warm_max_ms,peak_rss_kib +sum-bool,1,baseline,3,2.85115,2.84029,2.85819,2.70623,2.69803,2.71166,71188 +sum-bool,1,current,3,1.86115,1.83958,1.92035,1.6701,1.66011,1.67891,71228 +sum-bool,2,baseline,3,2.02529,1.98643,2.19218,1.81046,1.72391,1.85568,95924 +sum-bool,2,current,3,1.22503,1.22261,1.24141,1.11588,1.11582,1.12262,81812 +sum-bool,4,baseline,3,1.45689,1.41542,2.11658,1.44454,1.44421,1.83434,126948 +sum-bool,4,current,3,1.56088,0.82576,1.63984,0.753534,0.734525,1.45734,102124 +sum-bool,8,baseline,3,3.1498,2.78327,3.40196,3.0453,2.76534,3.10493,200392 +sum-bool,8,current,3,1.31776,1.10695,1.78182,1.39947,0.654634,1.53973,142920 +sum-bool,16,baseline,3,5.14442,4.9388,5.23555,5.74788,5.20144,5.77796,360336 +sum-bool,16,current,3,0.996806,0.909349,1.03402,0.936126,0.918078,0.991297,225260 +sum-bool,default,baseline,3,3.91682,3.83916,3.92407,5.23176,5.20232,5.28817,753984 +sum-bool,default,current,3,0.831388,0.751294,0.839015,0.672871,0.643422,0.727257,348196 +extrema-bool,1,baseline,3,3.13015,3.1141,3.19557,2.97816,2.9712,3.02039,71036 +extrema-bool,1,current,3,2.88251,2.84382,3.2982,2.62198,2.62084,2.63876,71284 +extrema-bool,2,baseline,3,2.4467,2.35578,2.68893,2.28751,2.23056,2.35797,104100 +extrema-bool,2,current,3,1.8987,1.8371,2.52555,1.73467,1.73093,1.74829,81824 +extrema-bool,4,baseline,3,2.00109,1.95792,3.65964,2.05007,2.04509,2.96493,169416 +extrema-bool,4,current,3,1.28104,1.27627,2.06788,1.1646,1.15341,1.17544,102112 +extrema-bool,8,baseline,3,3.35131,3.2465,3.64507,3.27256,3.20235,3.60044,300896 +extrema-bool,8,current,3,1.81964,1.60531,2.28211,1.72616,1.55584,1.85889,143196 +extrema-bool,16,baseline,3,3.79115,3.67069,3.85732,4.63651,4.36614,4.95944,519964 +extrema-bool,16,current,3,1.15571,1.13552,1.17772,1.11937,1.09655,1.83127,225240 +extrema-bool,default,baseline,3,3.93852,3.80797,4.13591,5.60263,5.25158,6.73474,809292 +extrema-bool,default,current,3,0.988766,0.945417,1.09948,0.814595,0.80697,0.816067,348356 +sum-u8,1,baseline,3,2.88014,2.87442,2.89749,2.69868,2.69243,2.70509,71292 +sum-u8,1,current,3,1.82349,1.8083,1.83119,1.67085,1.65574,1.67102,71536 +sum-u8,2,baseline,3,2.01882,1.96725,2.03569,1.76568,1.7313,1.82254,95920 +sum-u8,2,current,3,1.23519,1.23258,1.45635,1.11762,1.11675,1.1986,81744 +sum-u8,4,baseline,3,2.06253,2.06063,2.10796,1.91371,1.55983,2.14737,126748 +sum-u8,4,current,3,1.61996,1.5066,1.62609,1.43282,0.736886,1.45201,102116 +sum-u8,8,baseline,3,2.64215,2.47299,3.27418,2.7597,2.20252,3.1271,200724 +sum-u8,8,current,3,1.62583,1.50413,1.822,1.37373,1.32182,1.68416,143360 +sum-u8,16,baseline,3,5.11795,5.09348,5.1956,5.52286,5.15294,5.76142,348280 +sum-u8,16,current,3,0.927987,0.874794,1.04345,0.884019,0.83367,0.970967,225228 +sum-u8,default,baseline,3,4.04075,3.94519,4.2537,4.8532,4.85066,5.29948,756084 +sum-u8,default,current,3,0.797461,0.75569,0.825563,0.679529,0.665353,1.36317,348396 +extrema-u8,1,baseline,3,3.34432,3.29172,3.4381,3.0048,2.97454,3.01021,71540 +extrema-u8,1,current,3,3.04867,2.93953,3.08847,2.73847,2.73332,2.80163,71296 +extrema-u8,2,baseline,3,2.57568,2.48205,2.76971,2.44941,2.2824,2.63531,104300 +extrema-u8,2,current,3,2.00317,1.99968,2.15706,1.84769,1.84599,1.87726,81724 +extrema-u8,4,baseline,3,2.89413,2.12289,5.39392,2.0939,2.06043,3.20168,169988 +extrema-u8,4,current,3,1.6939,1.6361,2.40759,1.28068,1.25231,1.2945,102360 +extrema-u8,8,baseline,3,3.1983,2.84083,3.48981,2.81285,2.48752,3.1674,300856 +extrema-u8,8,current,3,1.95566,1.80796,2.02895,1.80402,1.77538,2.00885,143428 +extrema-u8,16,baseline,3,3.86416,3.71626,5.00778,4.82435,4.22231,6.50457,525836 +extrema-u8,16,current,3,1.21197,1.10064,1.32113,1.15755,1.15331,1.26912,225308 +extrema-u8,default,baseline,3,4.11715,4.1046,4.80561,6.30159,5.70256,6.7347,790572 +extrema-u8,default,current,3,1.05219,1.04023,1.0892,0.855764,0.843661,0.899765,348556 +sum-i16,1,baseline,3,2.91036,2.89775,2.96202,2.76208,2.74681,2.77812,71188 +sum-i16,1,current,3,1.82042,1.80692,1.82626,1.66239,1.66186,1.67148,71272 +sum-i16,2,baseline,3,2.12622,2.07411,2.13511,1.80138,1.73741,1.86035,96160 +sum-i16,2,current,3,1.24207,1.22018,1.24962,1.11906,1.11574,1.1199,81820 +sum-i16,4,baseline,3,2.17748,1.69613,3.20243,1.55175,1.38873,2.99741,126728 +sum-i16,4,current,3,0.860116,0.846091,0.903795,0.736604,0.733846,0.740804,102248 +sum-i16,8,baseline,3,3.09957,1.511,3.52005,2.70254,1.64152,2.9961,200588 +sum-i16,8,current,3,1.68157,1.40602,1.92831,1.38569,1.24192,1.81483,161968 +sum-i16,16,baseline,3,5.42644,4.61316,5.4798,5.46538,5.0847,5.56419,348296 +sum-i16,16,current,3,0.986881,0.984697,1.05453,0.872707,0.847536,0.911208,225364 +sum-i16,default,baseline,3,4.03637,3.90248,4.2152,5.31133,5.22668,6.39098,755784 +sum-i16,default,current,3,0.803319,0.79728,0.869246,0.768048,0.662563,1.29041,348440 +extrema-i16,1,baseline,3,3.80808,3.79153,3.91731,3.67714,3.63449,3.68698,71284 +extrema-i16,1,current,3,2.90114,2.89722,3.03902,2.71614,2.70568,2.76785,71272 +extrema-i16,2,baseline,3,2.82853,2.71676,3.39343,2.79853,2.77708,2.92125,104064 +extrema-i16,2,current,3,1.97652,1.9552,1.98565,1.82914,1.82799,1.85644,81804 +extrema-i16,4,baseline,3,2.29811,2.29553,3.2415,2.31935,2.28046,2.40672,169720 +extrema-i16,4,current,3,1.42785,1.36892,3.10754,1.25062,1.23821,1.30673,102304 +extrema-i16,8,baseline,3,3.11486,2.57199,3.64175,3.51284,2.57657,4.10445,301116 +extrema-i16,8,current,3,2.40155,2.086,2.55702,2.13506,1.90926,2.33073,143264 +extrema-i16,16,baseline,3,4.00246,3.97622,5.0393,4.74269,4.68394,6.08605,521792 +extrema-i16,16,current,3,1.34213,1.17425,1.42123,1.41208,1.19786,3.16544,225296 +extrema-i16,default,baseline,3,4.41063,4.40969,4.602,6.76652,6.44464,7.14008,796684 +extrema-i16,default,current,3,1.17709,1.09236,1.23365,0.900764,0.898898,0.91756,348320 +sum-i32,1,baseline,3,2.88167,2.86588,2.91517,2.74592,2.73827,2.76378,71232 +sum-i32,1,current,3,1.86791,1.83443,1.86823,1.68166,1.67011,1.69419,71280 +sum-i32,2,baseline,3,2.10598,2.02557,2.11253,1.84076,1.79,1.94725,95912 +sum-i32,2,current,3,1.26256,1.24788,1.27422,1.12289,1.12093,1.16606,81484 +sum-i32,4,baseline,3,2.14475,1.94087,2.14902,1.54273,1.47079,1.82137,126700 +sum-i32,4,current,3,1.52026,1.50876,1.64107,1.42597,1.41931,1.43105,102084 +sum-i32,8,baseline,3,2.79077,2.67614,3.17427,2.99941,2.63331,3.55899,200676 +sum-i32,8,current,3,2.60714,2.31471,2.61757,1.38774,1.1077,1.39892,161556 +sum-i32,16,baseline,3,4.95902,4.8718,5.10541,5.68594,5.45707,5.69508,348116 +sum-i32,16,current,3,1.0332,0.964804,1.07971,0.927815,0.877543,0.936073,225332 +sum-i32,default,baseline,3,4.21954,3.95551,4.47484,5.48193,5.05723,5.56176,751916 +sum-i32,default,current,3,0.842703,0.773389,0.900228,0.70729,0.633517,0.715552,348668 +extrema-i32,1,baseline,3,4.12055,3.83955,4.75309,3.70808,3.66742,3.71278,71292 +extrema-i32,1,current,3,2.9813,2.92399,3.02533,2.75414,2.74461,2.76837,71212 +extrema-i32,2,baseline,3,2.76321,2.72761,2.88071,2.72257,2.69386,2.78975,104104 +extrema-i32,2,current,3,1.98594,1.96566,2.01147,1.83918,1.83091,1.85071,81816 +extrema-i32,4,baseline,3,2.30307,2.23503,2.33044,2.33657,2.30941,2.36885,169712 +extrema-i32,4,current,3,2.47832,1.39832,2.55795,1.26274,1.2373,2.49741,101908 +extrema-i32,8,baseline,3,3.42816,3.05416,3.62409,3.65816,3.44323,3.74659,301020 +extrema-i32,8,current,3,2.03938,1.76638,2.1818,1.96222,1.71234,2.1539,143120 +extrema-i32,16,baseline,3,3.98587,3.98146,4.02253,4.99137,4.71344,5.29829,532536 +extrema-i32,16,current,3,1.35976,1.29021,1.47876,1.19915,1.16844,1.22097,225132 +extrema-i32,default,baseline,3,4.78081,4.19531,4.8832,6.97303,6.34178,7.41861,798716 +extrema-i32,default,current,3,1.13122,1.12598,1.17444,0.919257,0.898904,0.966434,348652 +sum-i64,1,baseline,3,2.92031,2.87089,2.94982,2.75781,2.72217,2.77038,71284 +sum-i64,1,current,3,1.82678,1.81832,1.85004,1.69734,1.67541,1.69983,71516 +sum-i64,2,baseline,3,2.07136,2.06857,2.41509,1.77488,1.77134,1.80985,96052 +sum-i64,2,current,3,1.24817,1.22561,1.28382,1.12931,1.1268,1.13033,81600 +sum-i64,4,baseline,3,2.03405,1.38438,2.62602,1.48698,1.42855,2.47581,126520 +sum-i64,4,current,3,0.973966,0.849439,2.01383,0.757089,0.744637,1.50995,102384 +sum-i64,8,baseline,3,2.59449,2.57494,2.84855,2.48184,1.55585,2.8754,200924 +sum-i64,8,current,3,2.23437,2.15011,2.44172,1.10074,1.08148,1.37183,161588 +sum-i64,16,baseline,3,5.08889,5.07437,5.13122,5.27388,4.9912,5.38384,348508 +sum-i64,16,current,3,2.56997,2.56608,2.65954,0.984199,0.924442,0.993263,247708 +sum-i64,default,baseline,3,5.05133,5.00262,5.12699,6.0133,5.73348,6.60908,763972 +sum-i64,default,current,3,3.10859,2.83692,3.25708,0.724293,0.702752,0.741624,370948 +extrema-i64,1,baseline,3,5.3738,5.35731,5.82831,5.09015,5.08735,5.1923,71268 +extrema-i64,1,current,3,3.30825,3.18178,3.32903,3.01952,3.01545,3.07238,71276 +extrema-i64,2,baseline,3,3.70684,3.43894,5.18446,3.51387,3.49743,4.7806,104088 +extrema-i64,2,current,3,3.24823,3.23686,3.28004,2.20637,2.17723,2.24491,94168 +extrema-i64,4,baseline,3,3.57333,2.78335,3.79783,2.79897,2.76945,2.86292,169956 +extrema-i64,4,current,3,2.57464,2.56828,3.06618,1.72057,1.29109,2.3306,124940 +extrema-i64,8,baseline,3,3.8067,3.7868,4.34814,3.82336,3.66589,4.82427,301104 +extrema-i64,8,current,3,3.06972,2.79173,3.66651,2.03334,1.93343,2.15448,165976 +extrema-i64,16,baseline,3,9.0657,8.78043,9.07953,8.95139,8.91074,9.09246,579640 +extrema-i64,16,current,3,3.05314,2.96924,3.68417,1.28618,1.2499,2.0779,247784 +extrema-i64,default,baseline,3,6.83421,6.55167,9.11157,8.56589,7.77039,9.12967,803120 +extrema-i64,default,current,3,3.38029,3.16176,4.35511,0.971834,0.954928,2.84546,371016 +sum-f32,1,baseline,3,3.01292,2.937,3.0168,2.80677,2.77775,3.00875,71392 +sum-f32,1,current,3,1.8637,1.84458,2.14713,1.68725,1.6798,1.69225,71284 +sum-f32,2,baseline,3,2.18041,2.09803,2.36505,1.87767,1.85474,1.93356,95908 +sum-f32,2,current,3,1.2839,1.27782,1.29512,1.14327,1.1417,1.1516,81816 +sum-f32,4,baseline,3,1.59253,1.48593,2.06562,1.47256,1.44818,1.52216,126636 +sum-f32,4,current,3,1.57417,1.09538,1.62792,0.808428,0.764382,0.813678,102340 +sum-f32,8,baseline,3,3.1108,2.79292,4.42114,3.0653,2.87896,3.92003,212180 +sum-f32,8,current,3,2.58673,2.5593,2.5972,1.41262,1.08441,1.41729,161692 +sum-f32,16,baseline,3,5.54834,4.61041,5.60359,5.81083,5.42308,6.09278,348304 +sum-f32,16,current,3,0.989356,0.937978,1.068,0.943219,0.936274,0.999886,225116 +sum-f32,default,baseline,3,4.09372,3.96294,4.63756,6.15999,5.03726,6.33537,749432 +sum-f32,default,current,3,0.862503,0.859856,0.888018,0.761881,0.691866,0.769746,348132 +extrema-f32,1,baseline,3,5.45893,5.29616,5.67338,5.2173,5.17719,5.37186,71276 +extrema-f32,1,current,3,3.52121,3.18339,3.79765,3.1462,3.01361,3.41404,71448 +extrema-f32,2,baseline,3,3.70369,3.53513,3.79498,3.50803,3.47998,3.5222,104064 +extrema-f32,2,current,3,2.24371,2.1615,2.44227,1.94802,1.93629,2.05811,81632 +extrema-f32,4,baseline,3,2.65165,2.64062,2.68409,2.72763,2.68963,2.75935,169864 +extrema-f32,4,current,3,4.52351,2.57331,4.56584,2.1719,1.26843,2.28263,124904 +extrema-f32,8,baseline,3,3.49114,3.47151,3.82842,3.93878,3.75369,4.04377,300720 +extrema-f32,8,current,3,2.71138,2.6789,2.79752,2.04205,1.72914,2.25974,155492 +extrema-f32,16,baseline,3,5.39288,4.39953,7.85691,5.8185,5.78419,6.04386,530520 +extrema-f32,16,current,3,2.19102,2.08833,2.25065,1.36876,1.29961,1.36898,237220 +extrema-f32,default,baseline,3,4.97652,4.87895,5.65287,6.89382,6.09747,7.79704,801356 +extrema-f32,default,current,3,1.96186,1.92756,1.98871,0.88938,0.87116,0.897368,360744 +sum-f64,1,baseline,3,3.06451,2.93022,3.12794,2.82134,2.79175,2.88778,71268 +sum-f64,1,current,3,1.8702,1.82955,1.88394,1.70614,1.68252,1.71467,71292 +sum-f64,2,baseline,3,2.11169,2.09335,2.19726,1.87308,1.86262,2.2634,96144 +sum-f64,2,current,3,1.30281,1.27817,1.65832,1.15695,1.15491,1.22185,81572 +sum-f64,4,baseline,3,1.45179,1.43863,1.46419,1.47447,1.46925,1.56379,126656 +sum-f64,4,current,3,0.852956,0.846179,0.93962,0.760498,0.752888,0.809736,102120 +sum-f64,8,baseline,3,1.85443,1.77639,2.87027,2.36943,1.61604,2.83838,200432 +sum-f64,8,current,3,2.52395,2.43998,2.55583,1.15863,1.04212,1.44452,161744 +sum-f64,16,baseline,3,5.53291,5.34072,5.6563,5.25893,4.87357,6.18024,348532 +sum-f64,16,current,3,2.723,2.61249,3.12924,1.1375,0.941254,1.15357,247656 +sum-f64,default,baseline,3,5.16974,4.94291,5.26616,5.81845,5.56726,6.55662,766280 +sum-f64,default,current,3,3.03359,2.9894,3.14149,0.730299,0.724414,0.754219,370964 +extrema-f64,1,baseline,3,5.31407,5.31395,5.74927,5.27082,5.15537,5.46037,71172 +extrema-f64,1,current,3,3.27336,3.25216,3.34132,3.10238,3.08759,3.12152,71384 +extrema-f64,2,baseline,3,3.58139,3.54316,3.59552,3.53716,3.49547,3.55687,104304 +extrema-f64,2,current,3,2.92952,2.88438,3.08568,1.99237,1.98619,1.996,93932 +extrema-f64,4,baseline,3,3.81263,2.60766,4.0839,2.9827,2.67298,4.26624,169612 +extrema-f64,4,current,3,2.54536,2.5319,3.85165,1.36961,1.28155,2.1762,124748 +extrema-f64,8,baseline,3,3.65861,3.49804,3.95384,3.84827,3.59231,4.18378,300912 +extrema-f64,8,current,3,3.67616,3.18382,4.82309,2.2691,2.20456,2.45302,166144 +extrema-f64,16,baseline,3,8.93854,8.85579,9.27134,8.75644,8.69423,8.76005,579892 +extrema-f64,16,current,3,3.29208,3.2529,3.29695,1.40104,1.38904,1.79788,248112 +extrema-f64,default,baseline,3,6.93316,6.93209,8.25926,8.48831,7.93522,8.9053,799296 +extrema-f64,default,current,3,3.57679,3.4704,3.5954,0.935988,0.923717,0.951923,370968 +sum-time,1,baseline,3,3.03342,2.99617,3.0503,2.75243,2.73568,2.787,103848 +sum-time,1,current,3,1.90925,1.87622,1.92033,1.6833,1.67089,1.69125,104056 +sum-time,2,baseline,3,2.12834,2.10483,2.15667,1.87344,1.86956,2.01221,128852 +sum-time,2,current,3,1.30562,1.30305,1.34512,1.12442,1.1196,1.13,114384 +sum-time,4,baseline,3,1.44973,1.42619,1.51846,1.40421,1.37783,1.40797,159748 +sum-time,4,current,3,1.55576,0.92859,1.70774,1.42086,0.797314,1.46261,135008 +sum-time,8,baseline,3,2.80151,2.60146,2.94654,2.99744,2.79129,3.07464,239204 +sum-time,8,current,3,2.20855,2.20847,2.51064,1.09803,0.690969,1.18101,194372 +sum-time,16,baseline,3,4.76982,4.419,5.44694,5.62165,4.99328,5.80926,381000 +sum-time,16,current,3,1.11726,1.06292,1.21446,0.946718,0.840266,0.957766,258120 +sum-time,default,baseline,3,3.98859,3.89256,4.4625,5.22843,5.19373,5.25617,784216 +sum-time,default,current,3,0.924017,0.916089,1.00208,0.677662,0.647245,0.684125,381096 +extrema-time,1,baseline,3,4.25594,4.17433,4.32439,3.82105,3.7007,4.15443,103912 +extrema-time,1,current,3,3.12522,3.03932,3.16901,2.75617,2.74267,2.7832,103772 +extrema-time,2,baseline,3,2.95558,2.92891,3.04479,2.66462,2.62207,2.67632,136888 +extrema-time,2,current,3,2.05112,2.04083,3.82062,1.83252,1.82292,2.30933,114404 +extrema-time,4,baseline,3,2.35032,2.3029,2.37772,2.3535,2.26375,2.44584,202516 +extrema-time,4,current,3,2.27393,1.42629,2.57987,1.86655,1.23548,2.2203,135012 +extrema-time,8,baseline,3,3.38634,3.17287,3.63579,3.3869,3.34807,3.43526,333536 +extrema-time,8,current,3,2.12102,2.01632,2.23773,1.81567,1.4454,2.07508,176408 +extrema-time,16,baseline,3,4.15189,4.12448,4.46824,4.88719,4.58504,5.00774,559208 +extrema-time,16,current,3,1.44419,1.3524,1.51864,1.26968,1.21062,1.35331,258044 +extrema-time,default,baseline,3,4.29501,4.27285,4.71958,6.37881,6.0584,6.47432,821784 +extrema-time,default,current,3,1.26016,1.2009,1.26234,0.957532,0.938134,0.975341,381420 +count,1,baseline,3,2.8556,2.83967,2.97521,2.70349,2.68539,2.70961,71536 +count,1,current,3,1.82407,1.8133,2.00243,1.66212,1.66156,1.67589,71328 +count,2,baseline,3,2.08392,2.05857,2.1081,1.7293,1.69495,1.79808,96136 +count,2,current,3,1.26085,1.25438,1.28119,1.11621,1.11279,1.14216,81564 +count,4,baseline,3,1.59104,1.49805,2.17445,1.45128,1.42286,2.15762,126732 +count,4,current,3,0.922323,0.890332,1.08845,0.753063,0.731622,0.814687,102364 +count,8,baseline,3,2.64736,1.46607,2.89826,2.661,1.5962,2.67004,200656 +count,8,current,3,1.41452,1.27541,1.49971,1.2486,1.06093,1.37565,143464 +count,16,baseline,3,4.97783,4.49197,6.54667,5.34109,5.03302,5.71665,348504 +count,16,current,3,0.874575,0.862873,0.895732,0.917731,0.794509,1.07863,225356 +count,default,baseline,3,3.47542,3.40525,3.55967,3.35035,3.12832,3.75944,518660 +count,default,current,3,0.797195,0.691022,0.812567,0.571386,0.568314,0.640817,348440 +statistics,1,baseline,3,10.3892,10.07,10.4227,9.12161,8.96031,9.28278,87684 +statistics,1,current,3,6.01174,5.92599,6.48597,4.77758,4.65543,5.03334,85628 +statistics,2,baseline,3,6.50684,6.39504,6.68112,5.47555,5.37813,5.60452,112316 +statistics,2,current,3,4.40888,4.40174,4.70717,2.95429,2.93826,3.02334,104020 +statistics,4,baseline,3,4.87336,4.87303,6.72496,3.62727,3.58863,3.6614,165744 +statistics,4,current,3,4.02174,3.87071,4.39292,2.43227,2.33279,2.49363,129032 +statistics,8,baseline,3,7.07728,7.02099,7.95221,4.9641,4.79875,5.7504,257988 +statistics,8,current,3,4.1802,3.79353,4.51943,2.31985,2.00451,2.94112,170136 +statistics,16,baseline,3,11.4514,11.2918,12.2717,11.5605,11.2437,12.3497,581676 +statistics,16,current,3,3.70056,3.52003,5.20474,1.74492,1.67178,3.0429,251988 +statistics,default,baseline,3,8.83014,8.66844,11.7429,10.1526,9.82609,14.849,919348 +statistics,default,current,3,4.41113,4.34917,4.44043,1.31332,1.31323,1.34393,375216 +binary,1,baseline,3,22.1877,22.1565,22.59,19.4017,19.0557,19.8211,120208 +binary,1,current,3,15.7881,14.9429,16.2711,12.9851,12.5945,13.3326,116388 +binary,2,baseline,3,15.3747,14.9226,18.0474,13.3416,13.0788,14.7891,138912 +binary,2,current,3,12.4055,12.0254,17.6502,9.0343,8.93177,9.13601,126148 +binary,4,baseline,3,74.285,73.4528,74.5284,69.4054,69.2803,69.7511,333832 +binary,4,current,3,7.07972,6.98811,7.5051,4.66812,4.48696,5.21096,146292 +binary,8,baseline,3,38.7347,37.6699,39.1694,37.2518,36.6846,37.5576,384948 +binary,8,current,3,6.01083,5.40418,6.14853,4.20339,3.34235,4.71506,186784 +binary,16,baseline,3,23.2496,22.8368,23.305,22.0336,21.4506,23.735,590248 +binary,16,current,3,4.95318,4.71538,5.1355,3.06828,3.02919,3.20203,267984 +binary,default,baseline,3,17.5195,17.4139,19.6249,17.8475,17.818,19.6924,975432 +binary,default,current,3,5.56608,5.53566,5.70244,2.41457,2.34808,2.60549,392256 +truth,1,baseline,3,5.95297,5.82654,6.48339,5.46096,5.39496,6.10125,71216 +truth,1,current,3,3.99828,3.91542,4.10344,3.74443,3.66675,3.82288,71276 +truth,2,baseline,3,3.69897,3.69746,4.0247,3.56343,3.56076,3.61075,104116 +truth,2,current,3,4.27608,3.75654,6.46303,2.40171,2.26301,2.62518,93924 +truth,4,baseline,3,4.31672,2.60405,4.76764,2.97258,2.86427,4.34945,169864 +truth,4,current,3,3.04326,2.66291,4.0922,1.38068,1.37816,1.72791,124932 +truth,8,baseline,3,3.85719,3.73312,4.35845,4.26723,3.76155,4.34187,300772 +truth,8,current,3,3.44802,2.73755,3.65623,1.80245,1.3451,1.97501,165888 +truth,16,baseline,3,8.75881,8.68395,8.77798,8.54061,8.4582,8.67441,575528 +truth,16,current,3,3.14872,3.10732,3.19255,1.36169,1.32519,1.37689,247668 +truth,default,baseline,3,6.99748,6.59733,9.02567,8.33102,7.09272,9.71802,786488 +truth,default,current,3,3.61002,3.49982,3.72339,1.00395,1.00154,1.15224,370940 +product,1,baseline,3,2.94287,2.89712,3.40243,2.67908,2.65909,6.12203,71204 +product,1,current,3,2.06025,2.05807,2.07336,1.76825,1.75696,1.80061,71448 +product,2,baseline,3,2.33631,2.11724,3.63577,1.87708,1.86414,3.16219,97892 +product,2,current,3,1.29706,1.29667,2.5407,1.12213,1.11419,2.31084,81560 +product,4,baseline,3,2.67184,1.64008,2.81324,2.22481,1.41243,3.01334,151276 +product,4,current,3,1.08554,0.900089,1.4532,0.75735,0.744063,0.83067,102292 +product,8,baseline,3,2.34846,2.26525,3.08916,2.53155,2.43203,3.32658,257964 +product,8,current,3,2.61451,2.53157,2.84591,1.22137,1.01477,1.40885,165736 +product,16,baseline,3,5.45542,5.39751,8.62584,6.4375,6.26258,15.6653,491284 +product,16,current,3,2.73211,2.65615,2.90349,0.959924,0.955742,1.01769,247596 +product,default,baseline,3,4.9685,4.69608,5.39619,5.78913,5.69577,7.97594,751920 +product,default,current,3,3.25206,3.00283,4.61432,0.737456,0.730065,2.19956,370964 +median,1,baseline,3,12.3794,10.3721,14.6937,8.65121,8.54634,9.87829,97900 +median,1,current,3,10.6128,10.5626,13.0019,8.85014,8.72377,8.97765,98164 +median,2,baseline,3,8.39062,8.22119,10.5414,6.94624,6.77647,8.62141,108464 +median,2,current,3,8.47708,8.18159,9.04538,6.87061,6.83814,7.49081,108684 +median,4,baseline,3,7.96551,7.11485,8.95415,6.30041,5.72896,7.19842,128984 +median,4,current,3,5.9813,5.97627,6.69455,4.82545,4.79373,4.83751,128984 +median,8,baseline,3,7.31426,6.94981,7.40288,5.91534,5.87511,6.14112,170180 +median,8,current,3,4.93218,3.95813,5.00116,3.24723,2.96109,3.477,169992 +median,16,baseline,3,7.45873,6.71281,9.66563,6.1368,6.0568,8.09029,252164 +median,16,current,3,4.28629,4.20977,4.28963,2.62713,2.46443,2.70634,252236 +median,default,baseline,3,6.69945,6.63415,7.50485,6.20873,6.0952,7.67243,374784 +median,default,current,3,4.9519,4.81222,5.3917,2.02125,1.86112,2.15328,375064 +quantile,1,baseline,3,10.6932,10.3724,10.9949,9.02098,8.96879,9.06721,98148 +quantile,1,current,3,11.3865,10.7099,11.4102,9.40562,9.38608,9.5986,98384 +quantile,2,baseline,3,8.77408,8.39108,10.7101,7.29517,7.0466,8.59232,108716 +quantile,2,current,3,8.68974,8.56898,9.9228,7.26262,7.25403,7.29838,108604 +quantile,4,baseline,3,7.21473,7.17542,7.26203,5.88758,5.78884,6.12741,129144 +quantile,4,current,3,6.77405,6.76244,6.80635,4.86182,4.82602,4.95457,129256 +quantile,8,baseline,3,7.71253,7.37716,7.83826,6.23011,6.08637,6.29498,169584 +quantile,8,current,3,4.73815,4.13021,5.86419,3.32266,3.05778,3.68453,170104 +quantile,16,baseline,3,7.62052,7.21631,7.65878,6.06864,6.01546,6.36227,251752 +quantile,16,current,3,4.35571,4.32685,4.5659,2.6464,2.54399,2.72634,251840 +quantile,default,baseline,3,7.44068,7.26129,7.87623,6.11016,6.07439,6.31817,375124 +quantile,default,current,3,4.93497,4.92083,5.07145,2.02239,1.91313,2.02671,375288 +mode,1,baseline,3,15.4251,14.9062,17.6407,14.3276,14.1143,16.5603,92032 +mode,1,current,3,14.309,13.5079,14.5042,13.2323,12.3546,13.6757,91860 +mode,2,baseline,3,10.7935,10.7309,11.057,9.75906,9.45276,9.9706,102580 +mode,2,current,3,10.8316,10.5736,11.7647,9.36956,9.27414,9.38486,102580 +mode,4,baseline,3,8.78904,8.44344,10.0945,7.33475,7.28926,7.47742,122836 +mode,4,current,3,7.63022,7.14074,8.58006,6.04566,5.91223,6.32469,129128 +mode,8,baseline,3,8.05897,7.99972,8.62604,6.93492,6.74749,6.95263,164160 +mode,8,current,3,6.25398,5.33434,6.47839,4.34563,3.6591,4.64923,170096 +mode,16,baseline,3,8.26857,7.42677,8.51642,7.0938,6.93412,7.34901,246040 +mode,16,current,3,4.78742,4.60352,5.00277,3.0729,2.99592,3.10162,251608 +mode,default,baseline,3,7.86541,7.60379,8.01364,6.68187,6.66136,6.94528,368872 +mode,default,current,3,5.32972,5.29127,5.71451,2.2643,2.23944,2.56245,374992 +top-bottom,1,baseline,3,16.2045,16.155,17.1171,15.044,14.8611,16.4288,104336 +top-bottom,1,current,3,16.0986,15.5452,16.2429,15.2901,14.4145,15.6211,104320 +top-bottom,2,baseline,3,12.4368,12.0317,14.6924,11.9175,10.987,12.2218,114564 +top-bottom,2,current,3,12.1724,12.0289,13.2831,11.4129,11.2584,11.4695,114552 +top-bottom,4,baseline,3,11.0059,10.7378,12.1946,9.44971,9.24068,10.0223,135380 +top-bottom,4,current,3,9.31282,9.02819,10.4877,8.36305,7.91628,8.52705,135400 +top-bottom,8,baseline,3,10.3323,10.1034,10.3731,8.51969,8.51149,8.72913,176196 +top-bottom,8,current,3,7.41507,7.19992,8.09414,5.65515,5.48736,6.24956,176264 +top-bottom,16,baseline,3,10.2422,10.1675,10.2523,8.92543,8.77014,9.35391,257916 +top-bottom,16,current,3,7.10462,6.87216,8.99083,5.08641,5.07571,5.2836,258184 +top-bottom,default,baseline,3,10.1203,10.0901,10.3461,9.02756,8.87715,9.2609,381408 +top-bottom,default,current,3,7.31707,7.31308,7.65863,4.37288,4.32188,4.43491,381320 +first-last,1,baseline,3,10.7744,10.6774,11.0288,9.73365,9.49286,9.85053,92020 +first-last,1,current,3,6.60903,6.58853,8.75653,5.21215,5.09187,7.14955,92128 +first-last,2,baseline,3,11.2227,10.9182,11.2701,9.57088,9.5651,10.174,102316 +first-last,2,current,3,6.15343,6.13035,8.70963,4.93795,4.80917,5.00115,102424 +first-last,4,baseline,3,10.8256,10.7985,11.0356,9.73837,9.67351,9.87538,122596 +first-last,4,current,3,5.01999,5.00619,5.25234,3.81232,3.77614,3.89115,128984 +first-last,8,baseline,3,11.0627,10.7753,11.1008,9.85779,9.40357,9.89955,163672 +first-last,8,current,3,4.00039,3.95179,4.44369,2.80826,2.47088,2.95415,169780 +first-last,16,baseline,3,11.5219,10.7393,11.9225,9.90291,9.7397,10.2544,245820 +first-last,16,current,3,4.2632,3.71548,4.31883,2.38619,2.29957,2.50754,252024 +first-last,default,baseline,3,10.8919,10.7784,10.9762,9.59397,9.44323,9.79223,368868 +first-last,default,current,3,4.93734,4.84119,5.69349,1.73831,1.66892,3.05581,375028 +mixed,1,baseline,3,13.0948,12.8873,13.2937,11.4894,11.4571,11.7087,98208 +mixed,1,current,3,11.9352,11.7995,12.2544,10.4236,10.0429,10.4561,97996 +mixed,2,baseline,3,11.1109,11.1009,12.464,9.72997,9.49329,9.90548,108464 +mixed,2,current,3,10.249,9.79907,10.8467,8.55293,8.48696,8.63315,108460 +mixed,4,baseline,3,10.0216,10.013,10.3919,8.70454,8.5155,8.82884,128984 +mixed,4,current,3,6.68952,6.62543,6.75193,5.54403,5.51961,5.59224,129224 +mixed,8,baseline,3,10.3634,9.89705,10.3794,8.72915,8.16653,8.75482,170088 +mixed,8,current,3,6.06707,4.86456,6.56724,3.85682,3.54915,4.19372,169880 +mixed,16,baseline,3,10.0829,10.0824,10.6374,9.53252,8.88077,9.59381,251696 +mixed,16,current,3,5.10905,5.06253,5.81738,3.1964,3.18613,3.20344,251740 +mixed,default,baseline,3,10.1497,9.91131,10.6373,8.76293,8.4749,8.98491,375032 +mixed,default,current,3,5.51339,5.38745,5.62206,2.39691,2.36638,2.51578,375328 +distinct,1,baseline,3,60.1221,58.817,66.2591,66.0718,65.0266,67.8513,202120 +distinct,1,current,3,50.4498,50.252,51.8757,43.8485,43.667,46.6439,202404 +distinct,2,baseline,3,23.7967,23.3274,25.3826,21.8639,20.3189,22.0173,201604 +distinct,2,current,3,20.641,20.2438,20.7799,16.8146,16.3903,18.0085,151468 +distinct,4,baseline,3,17.0743,16.4544,18.2618,13.9913,13.9905,14.1693,230160 +distinct,4,current,3,11.9849,11.0521,12.3263,8.63684,8.58601,9.05558,200632 +distinct,8,baseline,3,14.5647,14.4633,14.5872,11.2294,10.983,11.3878,318328 +distinct,8,current,3,7.02913,6.94185,7.39156,6.10714,5.89978,6.6388,327144 +distinct,16,baseline,3,11.8774,11.8043,12.0528,11.8296,11.4286,14.1603,576552 +distinct,16,current,3,4.86657,4.86062,5.10982,4.68746,4.59953,4.79417,419696 +distinct,default,baseline,3,11.9068,11.1932,21.5251,12.7345,12.7154,13.3885,871496 +distinct,default,current,3,4.54016,4.48183,4.54223,4.11154,4.04345,4.51697,541232 +composite,1,baseline,3,28.4447,28.1179,28.6778,24.7237,24.5532,26.5636,130444 +composite,1,current,3,15.4139,15.2645,15.8307,11.9506,11.4023,12.3737,137112 +composite,2,baseline,3,20.7051,19.9874,21.0376,17.8262,17.5843,17.9414,153488 +composite,2,current,3,12.1405,12.0937,12.6886,9.76793,9.67129,9.94601,141500 +composite,4,baseline,3,17.084,16.1869,18.9278,13.4477,13.1602,14.4514,198704 +composite,4,current,3,10.2996,8.23805,10.3638,6.02458,5.99584,7.25574,161792 +composite,8,baseline,3,15.795,15.1309,18.3378,12.1516,11.6286,12.7104,288708 +composite,8,current,3,6.7431,6.5492,7.43645,3.88642,3.59671,4.45521,202696 +composite,16,baseline,3,14.438,13.8946,15.663,13.3232,11.4113,13.7275,432340 +composite,16,current,3,5.92705,5.83202,6.19201,2.75768,2.66625,2.8223,284968 +composite,default,baseline,3,15.8827,15.85,16.9261,13.9814,13.4597,14.4627,641528 +composite,default,current,3,8.09947,7.91615,8.32868,2.11917,1.98797,2.23277,408184 +selected,1,baseline,3,5.45002,5.30714,5.60358,5.01581,4.98582,5.21121,71280 +selected,1,current,3,3.83971,3.80032,3.97622,3.4662,3.42853,3.61561,71552 +selected,2,baseline,3,3.60234,3.44321,3.7569,3.39645,3.36483,3.46257,102320 +selected,2,current,3,2.68798,2.60098,3.41594,2.37131,2.27664,2.99503,91892 +selected,4,baseline,3,2.32106,2.26837,3.14204,2.30887,2.2421,2.65103,151552 +selected,4,current,3,1.81404,1.78483,2.09059,1.48698,1.46632,1.72623,112864 +selected,8,baseline,3,2.5428,2.49937,2.64946,2.58257,2.24095,2.84631,227244 +selected,8,current,3,1.47255,1.4187,1.56863,1.19063,1.16448,1.21686,153984 +selected,16,baseline,3,2.56568,2.25417,2.65258,2.64517,2.51866,2.83638,303024 +selected,16,current,3,1.31024,1.29443,1.32136,0.992912,0.90686,1.05103,235888 +selected,default,baseline,3,2.40701,2.31749,2.49479,2.52463,2.43385,2.57681,401824 +selected,default,current,3,1.28978,1.23576,1.34957,0.967615,0.890845,0.969571,358900 +selected-indexed,1,baseline,3,10.516,10.2694,10.7281,9.04923,8.78676,9.45375,102292 +selected-indexed,1,current,3,8.43038,8.318,8.47689,7.17894,6.79691,7.70592,102288 +selected-indexed,2,baseline,3,8.99262,8.95689,9.14508,7.85309,7.78292,8.13637,112560 +selected-indexed,2,current,3,8.04807,8.04544,8.28135,6.81745,6.73172,6.94996,113080 +selected-indexed,4,baseline,3,8.52385,8.51019,8.61267,7.33744,7.2953,7.42797,133128 +selected-indexed,4,current,3,6.27559,6.25852,6.30604,5.13332,4.95782,5.69107,133128 +selected-indexed,8,baseline,3,8.30556,8.23644,8.67422,7.0829,6.99259,7.52727,174180 +selected-indexed,8,current,3,5.24638,5.0736,5.93567,4.24202,4.14747,4.24345,174352 +selected-indexed,16,baseline,3,8.06611,7.98095,8.07299,6.84929,6.84877,6.90073,255660 +selected-indexed,16,current,3,4.74086,4.65389,5.10821,3.58029,3.46413,4.1507,256116 +selected-indexed,default,baseline,3,8.09682,7.97204,8.11669,6.86623,6.78016,6.98613,379100 +selected-indexed,default,current,3,4.72004,4.5514,5.84318,3.39838,3.3876,3.98713,379104 +sparse,1,baseline,3,52.1926,51.4365,52.427,49.0826,48.7955,49.4262,128572 +sparse,1,current,3,50.838,50.7418,50.9332,48.1919,48.1325,48.9286,128928 +sparse,2,baseline,3,27.2192,27.1138,27.2804,25.8446,25.6334,25.875,136792 +sparse,2,current,3,26.2557,26.1094,26.3035,24.9,24.6654,25.0285,137424 +sparse,4,baseline,3,14.6574,14.2391,15.1147,14.0987,14.0137,14.2319,188148 +sparse,4,current,3,14.5727,14.3852,14.6249,13.4242,13.3995,13.4894,188404 +sparse,8,baseline,3,9.2167,9.06025,9.62928,8.41367,8.27334,9.1387,319664 +sparse,8,current,3,8.51001,8.44511,9.17125,7.92178,7.86008,7.96491,319560 +sparse,16,baseline,3,6.30127,6.16413,6.59412,5.85785,5.83669,6.67176,475100 +sparse,16,current,3,5.80016,5.71968,5.92215,5.32882,5.25149,5.64545,477096 +sparse,default,baseline,3,5.67306,5.5858,5.80998,5.97472,5.94257,5.99421,770492 +sparse,default,current,3,5.31451,5.28202,6.59978,7.01408,5.52047,7.07964,766916 +clustered,1,baseline,3,4.12992,4.04253,4.14846,4.03779,3.90657,4.06086,71296 +clustered,1,current,3,1.81919,1.81658,1.90996,1.63635,1.62605,1.6895,71212 +clustered,2,baseline,3,2.73228,2.68523,3.79227,2.50677,2.4402,2.71856,95900 +clustered,2,current,3,1.57733,1.49092,1.60285,1.37457,1.36906,1.37731,81852 +clustered,4,baseline,3,1.69999,1.68065,1.93285,1.77048,1.74988,1.77118,126684 +clustered,4,current,3,2.5876,2.42888,2.61763,1.35913,0.793945,1.47583,118484 +clustered,8,baseline,3,2.14469,1.42258,2.15661,1.95101,1.56107,2.24196,200656 +clustered,8,current,3,2.5964,2.37559,2.60764,1.15424,1.01171,1.33147,163624 +clustered,16,baseline,3,3.07933,3.02794,3.39181,3.71156,3.66341,4.02383,348288 +clustered,16,current,3,2.51699,2.49779,2.5471,0.766491,0.730307,0.775206,245668 +clustered,default,baseline,3,3.8535,3.77912,4.06496,4.94647,4.70193,5.23627,541192 +clustered,default,current,3,2.76275,2.66175,2.90502,0.651319,0.647425,0.676644,369164 +skew,1,baseline,3,4.2176,4.20997,4.25534,4.18905,4.18159,4.26432,71560 +skew,1,current,3,1.77168,1.7264,1.77172,1.51251,1.49999,1.51581,71556 +skew,2,baseline,3,2.9129,2.89871,3.08191,2.7627,2.76077,3.03605,95908 +skew,2,current,3,1.49203,1.20375,2.13281,1.03589,1.03178,1.05004,81572 +skew,4,baseline,3,1.95135,1.90793,3.4545,2.15646,1.9792,3.5717,126640 +skew,4,current,3,1.8328,1.74497,2.70235,0.777134,0.701036,1.23609,116480 +skew,8,baseline,3,3.02396,1.94521,3.30196,3.34912,1.99173,3.43376,200744 +skew,8,current,3,2.28635,2.11518,2.29984,1.18396,1.00107,1.21501,161892 +skew,16,baseline,3,4.73483,4.67595,4.83814,4.99499,4.87804,5.23339,348280 +skew,16,current,3,2.75485,2.66443,3.12934,0.952163,0.905748,1.03914,245836 +skew,default,baseline,3,16.3664,15.9606,16.3931,15.9715,15.0289,16.4291,870980 +skew,default,current,3,3.08262,2.82994,3.31695,0.671146,0.66766,0.725358,368700 +skew-indexed,1,baseline,3,11.4574,11.2657,11.4869,9.91648,9.79442,10.0365,96124 +skew-indexed,1,current,3,11.1708,11.1047,11.2461,9.56156,9.42775,9.78617,96148 +skew-indexed,2,baseline,3,9.48278,9.44488,9.75891,8.05682,7.98767,8.43291,106360 +skew-indexed,2,current,3,9.48749,8.71258,10.1543,7.50223,7.36349,7.67812,106628 +skew-indexed,4,baseline,3,9.45465,9.29586,9.46837,8.08383,7.98298,8.1159,127092 +skew-indexed,4,current,3,7.49421,6.72001,8.53092,5.24752,5.20295,5.25443,129080 +skew-indexed,8,baseline,3,9.55001,9.44513,9.73882,8.07484,8.01248,8.53584,167736 +skew-indexed,8,current,3,5.78151,5.40529,5.99453,3.91945,3.38648,4.54027,170100 +skew-indexed,16,baseline,3,9.69318,9.64263,9.9695,8.23569,8.12543,8.49557,249892 +skew-indexed,16,current,3,5.62606,5.51971,6.49192,3.04306,3.00079,3.21754,251916 +skew-indexed,default,baseline,3,9.58608,9.57011,9.59724,8.19677,8.01948,8.34596,372876 +skew-indexed,default,current,3,7.63693,7.31127,10.6083,2.87342,2.43173,4.71337,374860 +float-key,1,baseline,3,17.2641,17.2546,17.4989,17.1283,16.984,17.5623,83848 +float-key,1,current,3,13.6853,13.6356,14.4515,11.8817,11.8105,12.2315,92048 +float-key,2,baseline,3,12.6809,12.677,13.0299,10.1879,9.6721,10.5752,196416 +float-key,2,current,3,12.9391,12.6111,13.5714,9.81445,9.53161,9.9374,194624 +float-key,4,baseline,3,8.23468,7.58575,9.96683,5.77648,5.59638,6.06818,247656 +float-key,4,current,3,8.78086,7.53899,10.022,5.55434,5.49428,5.85521,233212 +float-key,8,baseline,3,6.24717,6.00338,6.56727,4.47039,4.33426,4.57498,325612 +float-key,8,current,3,5.89967,5.53505,6.08028,4.22017,3.91171,4.44235,319432 +float-key,16,baseline,3,5.11303,4.79531,5.42804,5.28237,4.81118,5.59802,579668 +float-key,16,current,3,4.55272,4.49635,4.78268,3.00571,2.96884,3.35546,251748 +float-key,default,baseline,3,3.841,3.68635,3.86105,6.35678,5.88659,6.36616,821908 +float-key,default,current,3,4.99421,4.99373,5.18318,2.20774,2.18478,2.21732,375236 +float-indexed,1,baseline,3,19.8795,19.7565,20.8468,17.4703,17.4506,17.8861,97928 +float-indexed,1,current,3,18.3168,18.0583,19.0044,16.1153,16.1113,17.5674,98192 +float-indexed,2,baseline,3,17.6882,17.56,17.8942,15.9897,15.6153,16.0519,108464 +float-indexed,2,current,3,14.4314,14.0416,14.4721,12.8489,12.7068,13.1091,108704 +float-indexed,4,baseline,3,17.3184,16.4394,17.5295,15.2205,14.763,15.4097,128820 +float-indexed,4,current,3,9.48208,9.42671,9.68062,7.8102,7.71641,8.43862,129024 +float-indexed,8,baseline,3,16.9393,16.5424,17.4629,14.4495,14.1548,15.0049,170032 +float-indexed,8,current,3,6.07107,5.79562,6.43461,4.9976,4.97336,5.08105,170052 +float-indexed,16,baseline,3,17.2911,17.0123,17.6986,15.2495,15.1762,15.2665,251992 +float-indexed,16,current,3,5.42875,5.16432,6.40859,3.75214,3.50342,3.90144,252044 +float-indexed,default,baseline,3,16.8389,16.7687,16.9807,15.0729,14.8192,15.3946,375288 +float-indexed,default,current,3,5.51458,5.50285,5.71736,2.79968,2.71596,2.87693,375288 +symbol-key,1,baseline,3,1.28793,1.20587,1.32903,1.24551,1.20192,1.28408,87576 +symbol-key,1,current,3,1.32932,1.26836,1.48091,1.11563,1.06603,1.57754,87740 +symbol-key,2,baseline,3,0.646787,0.641313,0.658803,0.527431,0.509225,0.535175,97944 +symbol-key,2,current,3,0.885494,0.708576,1.06701,0.762758,0.543715,0.85474,97928 +symbol-key,4,baseline,3,0.609116,0.578837,0.673842,0.45528,0.26361,0.520877,118440 +symbol-key,4,current,3,0.631533,0.405305,0.680858,0.447363,0.290411,0.518259,118720 +symbol-key,8,baseline,3,0.493773,0.468192,0.498166,0.364847,0.332787,0.366862,159464 +symbol-key,8,current,3,0.472912,0.452578,0.60245,0.284705,0.269699,0.306208,159544 +symbol-key,16,baseline,3,0.528814,0.522826,0.553113,0.253916,0.247904,0.270062,241480 +symbol-key,16,current,3,0.487714,0.463049,0.523397,0.203067,0.194295,0.241693,241644 +symbol-key,default,baseline,3,0.637399,0.590841,0.642414,0.267455,0.228584,0.28981,364520 +symbol-key,default,current,3,0.602288,0.600484,0.628995,0.244925,0.228613,0.264675,364796 +string-key,1,baseline,3,24.5845,24.1086,25.2604,25.1998,24.8582,25.4506,102268 +string-key,1,current,3,13.6654,13.6583,13.6668,12.4759,12.4629,12.6624,122420 +string-key,2,baseline,3,18.5117,18.0921,18.6747,16.5369,16.1519,17.0944,204948 +string-key,2,current,3,9.32846,9.304,9.61895,7.97788,7.89615,8.04631,139172 +string-key,4,baseline,3,12.9118,10.9501,13.1077,8.85097,8.38998,9.23148,220708 +string-key,4,current,3,5.33978,5.33728,5.44554,4.01938,3.99779,4.05695,159704 +string-key,8,baseline,3,7.28974,6.92244,10.3892,6.84057,6.05845,7.34923,331988 +string-key,8,current,3,4.2364,4.11222,4.27529,2.62839,2.37275,2.68372,200760 +string-key,16,baseline,3,6.29167,6.21834,6.75785,7.34633,7.19752,9.08758,583600 +string-key,16,current,3,3.8401,3.69076,3.98798,1.85294,1.76062,1.85498,282908 +string-key,default,baseline,3,7.62725,5.90381,7.79372,8.49716,6.78953,9.44994,622992 +string-key,default,current,3,5.28317,5.13908,7.10256,1.65204,1.46184,2.9287,405652 +string-indexed,1,baseline,3,17.9138,17.3024,18.4697,16.1418,16.0069,16.7862,122500 +string-indexed,1,current,3,18.2363,17.8832,18.3007,16.2289,16.1742,17.1929,122496 +string-indexed,2,baseline,3,18.0939,17.9247,18.3036,15.8768,15.8557,16.8161,132832 +string-indexed,2,current,3,11.6696,11.2604,13.3246,10.2885,10.2204,10.3696,139188 +string-indexed,4,baseline,3,17.5898,17.4659,17.6045,16.3727,16.1685,16.922,153544 +string-indexed,4,current,3,7.41355,7.29482,7.43158,5.66372,5.64048,5.84084,159988 +string-indexed,8,baseline,3,17.5647,17.2795,17.5648,16.1895,15.9005,16.322,194508 +string-indexed,8,current,3,5.6511,5.63034,5.73485,4.43644,4.08082,4.47716,200472 +string-indexed,16,baseline,3,17.6196,17.4448,17.6526,16.3873,15.934,16.4249,276348 +string-indexed,16,current,3,5.41444,5.28202,5.51269,3.50546,3.26589,3.55165,282572 +string-indexed,default,baseline,3,17.3931,17.2369,17.6209,16.2267,15.5833,16.3992,399836 +string-indexed,default,current,3,6.92726,6.67514,18.2273,3.06918,2.93071,13.0554,405756 +wide-extrema,1,baseline,3,26.7717,26.3884,26.8064,25.8824,25.8716,26.1743,126864 +wide-extrema,1,current,3,25.1943,24.5555,27.3666,23.9332,23.3977,24.2249,127104 +wide-extrema,2,baseline,3,27.4303,26.1071,28.3535,25.5539,25.466,26.9989,136908 +wide-extrema,2,current,3,16.4515,16.2268,20.1947,15.481,15.0702,15.6897,137156 +wide-extrema,4,baseline,3,26.2311,25.947,26.5416,25.5615,25.3692,26.2113,157508 +wide-extrema,4,current,3,11.3327,10.5362,11.4452,9.65175,9.62012,9.72351,151564 +wide-extrema,8,baseline,3,26.126,25.9871,26.4974,25.2846,24.8693,25.4932,198308 +wide-extrema,8,current,3,8.23361,7.69959,9.23312,6.49302,6.37823,6.8224,192632 +wide-extrema,16,baseline,3,26.9108,26.1856,28.5858,26.2252,25.3439,27.1455,280680 +wide-extrema,16,current,3,6.91491,6.87333,7.01715,5.38196,5.25593,5.47628,274860 +wide-extrema,default,baseline,3,26.4643,26.0338,26.465,25.152,24.833,25.2752,403432 +wide-extrema,default,current,3,6.60295,6.57926,6.60388,4.64733,4.60456,4.65022,397836 +extrema-date,1,baseline,3,4.15692,4.13681,4.36179,3.81809,3.79154,3.84679,103636 +extrema-date,1,current,3,3.05794,3.05031,3.13843,2.74614,2.72577,2.75919,104056 +extrema-date,2,baseline,3,3.08403,2.95273,3.12221,2.80293,2.67211,2.82735,137136 +extrema-date,2,current,3,2.06032,2.02887,3.93943,1.8392,1.83369,1.95314,114340 +extrema-date,4,baseline,3,2.44538,2.28491,2.78463,2.37253,2.32814,2.68444,202724 +extrema-date,4,current,3,1.44761,1.44183,1.57452,1.26555,1.24206,1.74661,134876 +extrema-date,8,baseline,3,3.1173,3.03726,3.51182,3.26633,3.2172,3.5386,333508 +extrema-date,8,current,3,2.08427,2.08133,2.18208,2.016,1.75714,2.04679,175952 +extrema-date,16,baseline,3,4.15215,4.12062,4.23502,4.71376,4.59321,5.07625,554828 +extrema-date,16,current,3,1.48626,1.3852,1.54416,1.28514,1.28202,1.30598,258160 +extrema-date,default,baseline,3,4.53009,4.42393,4.59071,6.60827,6.07856,6.61732,825648 +extrema-date,default,current,3,1.23771,1.11965,1.41779,0.971952,0.917081,1.08279,381660 +extrema-timestamp,1,baseline,3,4.36463,4.33774,4.46084,3.82812,3.79317,4.24469,118656 +extrema-timestamp,1,current,3,3.54841,3.50259,3.65425,3.01585,2.99636,3.22706,118644 +extrema-timestamp,2,baseline,3,3.437,2.99709,4.69658,2.90708,2.85235,3.07061,151452 +extrema-timestamp,2,current,3,2.32123,2.31653,2.44476,1.96964,1.95401,1.99382,128860 +extrema-timestamp,4,baseline,3,3.57648,3.27036,3.60233,2.37622,2.32631,2.52327,217104 +extrema-timestamp,4,current,3,2.35415,2.13563,3.10717,1.43267,1.34149,1.44989,159492 +extrema-timestamp,8,baseline,3,3.56856,3.53791,3.82235,3.93447,3.47598,4.1629,348260 +extrema-timestamp,8,current,3,3.29106,2.99039,9.62104,2.36969,1.99491,3.88882,200596 +extrema-timestamp,16,baseline,3,7.82457,7.70814,7.96382,8.19396,8.13165,8.25012,610856 +extrema-timestamp,16,current,3,2.35516,2.30066,2.44112,1.36934,1.306,1.38569,282748 +extrema-timestamp,default,baseline,3,6.6222,6.10537,7.64854,6.69323,6.64289,7.52515,836152 +extrema-timestamp,default,current,3,2.69622,2.64016,2.94833,0.969572,0.965193,1.00629,406020 +wide-mode,1,baseline,3,16.9147,16.2128,17.1317,15.3161,14.9849,15.4688,118664 +wide-mode,1,current,3,17.0749,16.1086,17.642,15.393,14.6737,16.505,118824 +wide-mode,2,baseline,3,16.344,16.261,17.104,15.2818,15.0474,16.1532,128736 +wide-mode,2,current,3,11.5964,11.212,12.5719,10.887,10.8142,12.2629,128944 +wide-mode,4,baseline,3,16.7449,16.4067,17.3684,14.8806,14.7019,15.0342,149428 +wide-mode,4,current,3,8.28158,7.82692,9.06012,7.16337,7.02921,7.57069,151548 +wide-mode,8,baseline,3,15.4623,15.2646,15.7403,15.0585,14.9897,15.1289,190444 +wide-mode,8,current,3,6.12025,5.74942,6.43746,4.70674,4.51779,5.05731,192740 +wide-mode,16,baseline,3,16.4999,15.2564,16.8686,15.1603,14.2123,15.9337,272460 +wide-mode,16,current,3,5.42177,5.42144,5.71673,3.92695,3.78208,4.08904,274688 +wide-mode,default,baseline,3,16.902,16.5646,16.9505,14.9622,14.7953,15.7037,395516 +wide-mode,default,current,3,5.22961,5.12808,8.24282,3.37304,3.1722,5.62216,397580 +wide-top-bottom,1,baseline,3,34.6592,34.2616,35.7342,31.5003,31.3843,32.3679,186132 +wide-top-bottom,1,current,3,35.197,34.4335,37.4449,31.4075,31.1848,32.2265,186088 +wide-top-bottom,2,baseline,3,35.0762,34.1592,35.2802,31.5954,31.5953,32.6754,196528 +wide-top-bottom,2,current,3,23.0019,20.1318,25.7063,20.7082,19.7517,21.4238,212896 +wide-top-bottom,4,baseline,3,34.542,34.2345,36.95,32.2646,32.0714,32.4707,217032 +wide-top-bottom,4,current,3,13.5741,12.0801,14.1295,11.9241,11.6107,12.0473,225448 +wide-top-bottom,8,baseline,3,34.3376,34.1313,35.6135,31.383,31.1896,32.9149,258136 +wide-top-bottom,8,current,3,8.05092,7.61747,11.0561,6.85939,6.79814,8.86685,342164 +wide-top-bottom,16,baseline,3,35.1798,34.063,37.8777,32.5018,31.5415,33.286,339756 +wide-top-bottom,16,current,3,6.8097,6.55738,6.99991,5.43522,5.29651,5.66479,421916 +wide-top-bottom,default,baseline,3,34.3055,34.0273,34.8732,31.4531,31.1335,32.4085,462892 +wide-top-bottom,default,current,3,5.97946,5.73091,6.03558,4.11873,4.00581,4.36842,516700 +extrema-ascending,1,baseline,3,3.72952,3.36856,4.06803,3.39566,3.24853,3.53015,71276 +extrema-ascending,1,current,3,2.84876,2.81039,2.86688,2.6506,2.63505,2.68225,71344 +extrema-ascending,2,baseline,3,2.40784,2.37752,2.52752,2.35063,2.34938,2.43922,104344 +extrema-ascending,2,current,3,2.02395,1.85794,2.95237,1.7298,1.72425,2.03362,81536 +extrema-ascending,4,baseline,3,2.6842,2.21643,3.05204,2.82645,2.20538,2.89582,170012 +extrema-ascending,4,current,3,1.30307,1.29832,1.44156,1.16382,1.14128,1.18048,102168 +extrema-ascending,8,baseline,3,3.27675,2.82407,3.39622,3.21948,2.47483,3.29902,301032 +extrema-ascending,8,current,3,1.93777,1.59597,2.0532,1.73392,1.47052,1.93112,143208 +extrema-ascending,16,baseline,3,3.96972,3.84158,4.01411,4.50175,4.33142,4.62157,528156 +extrema-ascending,16,current,3,1.26869,1.16658,1.27784,1.14602,1.12224,1.16881,225320 +extrema-ascending,default,baseline,3,4.34774,4.12422,4.37853,6.50641,6.30394,6.62015,803140 +extrema-ascending,default,current,3,1.00859,0.979915,1.13806,0.915286,0.848123,0.991834,348364 +extrema-descending,1,baseline,3,3.46788,3.4257,3.46807,3.22514,3.21664,3.24878,71276 +extrema-descending,1,current,3,2.97365,2.88994,3.31361,2.65437,2.64595,2.70111,71296 +extrema-descending,2,baseline,3,2.55894,2.47662,3.04569,2.44734,2.42602,2.48889,104120 +extrema-descending,2,current,3,1.91099,1.82731,1.93711,1.72385,1.72005,1.74822,81808 +extrema-descending,4,baseline,3,2.76106,2.51378,2.84996,2.74306,2.1981,2.92969,169544 +extrema-descending,4,current,3,2.33886,2.18941,2.42297,1.17514,1.16895,2.27383,102136 +extrema-descending,8,baseline,3,3.0775,2.06577,3.20526,3.13362,2.58927,3.49574,300900 +extrema-descending,8,current,3,2.04927,1.58464,2.27525,1.90041,1.51183,2.19676,143084 +extrema-descending,16,baseline,3,3.81476,3.73004,3.93306,4.56768,4.42162,4.77262,530468 +extrema-descending,16,current,3,1.2827,1.25821,1.33349,1.20927,1.19614,1.34158,225384 +extrema-descending,default,baseline,3,4.76794,4.46966,4.76804,6.37629,5.77425,6.6306,795160 +extrema-descending,default,current,3,1.13766,1.00278,1.14186,0.833244,0.816589,0.878052,348376 +few-groups-indexed,1,baseline,3,5.98107,5.94388,6.84367,4.58422,4.57103,5.41288,89740 +few-groups-indexed,1,current,3,3.34211,3.27256,3.39713,2.0637,2.06259,2.0718,90180 +few-groups-indexed,2,baseline,3,6.00968,5.94978,6.06117,4.44369,4.36927,4.47055,100008 +few-groups-indexed,2,current,3,3.0404,2.99341,3.29885,1.87717,1.84696,1.9267,100252 +few-groups-indexed,4,baseline,3,5.96232,5.94414,6.04504,4.55567,4.42246,4.56055,120788 +few-groups-indexed,4,current,3,2.87696,2.21448,2.99803,1.23304,1.1877,1.40505,118996 +few-groups-indexed,8,baseline,3,6.01299,5.93984,6.01417,4.53674,4.44186,4.56469,161644 +few-groups-indexed,8,current,3,1.85696,1.72231,1.94939,0.856779,0.711055,0.900485,159852 +few-groups-indexed,16,baseline,3,6.18019,6.01618,6.76127,4.40912,4.34745,5.22532,243788 +few-groups-indexed,16,current,3,1.96048,1.88924,1.96191,0.568243,0.559137,0.585207,241760 +few-groups-indexed,default,baseline,3,5.98013,5.97938,6.01457,4.47183,4.43963,4.56004,367080 +few-groups-indexed,default,current,3,2.39552,2.35035,2.64826,0.524136,0.520776,0.552251,365028 +hot-top-ascending,1,baseline,3,6.48262,6.43096,6.50462,5.43292,5.38656,5.51606,87672 +hot-top-ascending,1,current,3,6.4159,6.39105,6.60793,5.38995,5.30374,5.41042,87836 +hot-top-ascending,2,baseline,3,6.50204,6.41472,6.58289,5.40962,5.32106,5.57461,98188 +hot-top-ascending,2,current,3,4.43768,4.39617,6.2911,3.36497,3.34263,4.11724,98224 +hot-top-ascending,4,baseline,3,6.45316,6.43816,6.65191,5.45601,5.36024,5.54086,118680 +hot-top-ascending,4,current,3,1.98517,1.95997,2.14163,1.89589,1.87674,2.05569,102336 +hot-top-ascending,8,baseline,3,6.49846,6.48206,6.53261,5.37697,5.34689,5.47062,159432 +hot-top-ascending,8,current,3,1.60253,1.44801,2.14297,1.34047,1.27961,1.54634,143212 +hot-top-ascending,16,baseline,3,6.50102,6.48847,6.60741,5.3548,5.30873,5.46717,241788 +hot-top-ascending,16,current,3,0.996319,0.98001,1.12856,0.770642,0.763049,0.872596,225348 +hot-top-ascending,default,baseline,3,6.52277,6.49138,6.57208,5.37463,5.36753,5.38156,364972 +hot-top-ascending,default,current,3,1.00641,0.983387,1.02154,0.709564,0.704886,0.726718,348556 +guid-key,1,baseline,3,12.1912,12.1201,12.2227,10.5166,10.4167,10.7139,122752 +guid-key,1,current,3,11.9861,11.9214,11.9924,10.3959,10.1571,10.6609,122708 +guid-key,2,baseline,3,12.1628,12.1282,12.2146,10.51,10.4106,10.6529,132752 +guid-key,2,current,3,7.86898,7.78616,7.92789,6.37708,6.37446,6.41516,133040 +guid-key,4,baseline,3,12.6018,12.2024,12.7186,10.568,10.4277,10.9615,153792 +guid-key,4,current,3,5.26008,5.23701,7.34279,4.00091,3.88525,5.7121,153588 +guid-key,8,baseline,3,12.2829,12.2225,12.3764,10.477,10.4225,10.5133,194664 +guid-key,8,current,3,4.2104,4.11737,4.43581,2.94823,2.87786,3.1572,194372 +guid-key,16,baseline,3,12.11,12.0775,12.3149,10.4534,10.4313,10.6907,276668 +guid-key,16,current,3,4.36779,4.31168,4.59557,2.49245,2.464,2.50603,276568 +guid-key,default,baseline,3,12.238,12.2171,12.3073,10.5296,10.4232,10.6126,399596 +guid-key,default,current,3,6.41462,6.29287,6.5182,2.51814,2.4193,2.52894,399880 +list-key,1,baseline,3,28.6646,28.4436,30.8402,27.6491,27.5626,27.8837,102312 +list-key,1,current,3,17.515,17.4859,18.0228,16.4494,16.2965,16.4535,102264 +list-key,2,baseline,3,28.6511,28.637,28.701,27.6334,27.6266,27.7233,112268 +list-key,2,current,3,9.87613,9.8057,9.87848,9.37827,9.35463,9.42016,106288 +list-key,4,baseline,3,28.883,28.6632,28.9412,27.8146,27.8007,28.0665,132848 +list-key,4,current,3,6.29784,6.12192,6.63537,5.41346,5.41276,5.4174,126904 +list-key,8,baseline,3,28.5722,28.5161,28.5767,27.6002,27.5487,27.7738,174168 +list-key,8,current,3,5.15358,4.31066,5.21227,3.76403,3.63171,3.84177,168040 +list-key,16,baseline,3,28.5558,28.4995,28.6869,27.6892,27.6487,27.7824,256012 +list-key,16,current,3,3.5098,3.49848,4.81875,2.99824,2.97953,3.4438,249928 +list-key,default,baseline,3,28.82,28.6409,29.7033,27.7497,27.6754,28.8316,379116 +list-key,default,current,3,3.60166,3.55654,4.44572,2.76222,2.73959,3.44517,373204 +unique-keys,1,baseline,3,29.6957,29.5997,31.7378,27.0494,26.227,28.3488,136764 +unique-keys,1,current,3,10.2715,10.109,10.3205,6.89431,6.76729,6.91218,136864 +unique-keys,2,baseline,3,44.2299,43.9082,45.1406,40.4074,40.1672,40.6758,202432 +unique-keys,2,current,3,9.45652,9.34964,9.9795,6.81446,6.80232,6.98945,147380 +unique-keys,4,baseline,3,24.7197,24.0402,26.107,21.9424,21.7845,22.2002,218916 +unique-keys,4,current,3,7.11016,6.63805,7.35812,4.05321,3.98592,4.23984,168096 +unique-keys,8,baseline,3,15.6404,14.7041,15.7253,13.2508,13.0868,13.7647,351944 +unique-keys,8,current,3,5.92091,5.82791,6.43324,3.00798,2.73198,3.1277,209024 +unique-keys,16,baseline,3,11.7624,11.6182,11.8857,10.4696,10.1077,10.6303,596160 +unique-keys,16,current,3,6.3016,5.99508,6.56769,1.92042,1.88143,1.94838,290728 +unique-keys,default,baseline,3,12.7273,12.4491,14.9883,11.0086,10.2449,15.7681,962800 +unique-keys,default,current,3,11.3444,11.3381,11.4403,1.6321,1.60204,1.73209,413996 +nonnull,1,baseline,3,4.76972,4.75811,5.00797,4.54254,4.52293,4.68292,71196 +nonnull,1,current,3,3.23495,3.22125,3.51679,3.06331,3.01337,3.19578,71256 +nonnull,2,baseline,3,3.12705,3.06495,3.39655,2.89208,2.85727,3.06122,97948 +nonnull,2,current,3,2.49878,2.05788,2.84759,1.97992,1.89007,2.21053,96000 +nonnull,4,baseline,3,3.43359,2.49027,3.43717,2.47772,2.1281,3.20365,151264 +nonnull,4,current,3,3.39501,2.29305,3.43143,1.3081,1.21337,2.20135,120756 +nonnull,8,baseline,3,3.06224,2.823,3.06895,3.32978,2.35221,3.35847,258224 +nonnull,8,current,3,3.41113,3.13808,3.74519,2.23125,2.04219,2.49842,165724 +nonnull,16,baseline,3,8.52701,8.30559,8.54358,8.41041,8.33067,8.56328,575748 +nonnull,16,current,3,2.75882,2.69504,2.85727,1.25631,1.25077,1.26607,247948 +nonnull,default,baseline,3,6.40073,6.26275,6.65332,7.88474,7.58751,8.19573,753720 +nonnull,default,current,3,3.18873,3.14658,3.34422,0.879086,0.874103,0.882432,371136 +hot-min-ascending,1,baseline,3,2.04301,2.0162,2.06486,1.89923,1.89488,1.90073,71280 +hot-min-ascending,1,current,3,1.86054,1.85838,1.93873,1.78773,1.78354,1.79139,71288 +hot-min-ascending,2,baseline,3,1.50684,1.50535,2.39975,1.40995,1.36765,1.64892,98204 +hot-min-ascending,2,current,3,1.03564,1.03238,1.10308,1.03248,0.994686,1.14636,81780 +hot-min-ascending,4,baseline,3,1.30177,1.22366,1.32057,1.05602,1.05559,1.06474,151572 +hot-min-ascending,4,current,3,0.795058,0.639223,0.820738,0.599162,0.595508,0.791202,102340 +hot-min-ascending,8,baseline,3,1.53913,1.10758,1.58081,1.80286,1.76152,1.88695,257752 +hot-min-ascending,8,current,3,0.629959,0.549404,0.787264,0.633649,0.492703,0.637016,143344 +hot-min-ascending,16,baseline,3,7.73735,7.7327,9.07751,9.14992,8.66542,9.68189,546900 +hot-min-ascending,16,current,3,0.734111,0.659868,1.11554,0.766571,0.668133,0.791314,225400 +hot-min-ascending,default,baseline,3,8.61974,8.16247,9.37795,10.5928,9.95009,12.8767,890748 +hot-min-ascending,default,current,3,1.0172,0.977415,1.10179,0.856921,0.838542,0.872466,348632 +hot-min-descending,1,baseline,3,2.02704,1.98724,2.10358,1.89756,1.89569,1.89781,71224 +hot-min-descending,1,current,3,1.29068,1.28819,1.313,1.18551,1.17425,1.19278,71476 +hot-min-descending,2,baseline,3,2.1551,1.52897,2.43726,1.43775,1.39337,2.30058,97848 +hot-min-descending,2,current,3,0.896815,0.84501,0.938629,0.771648,0.771376,0.783632,81816 +hot-min-descending,4,baseline,3,1.34671,1.33665,1.92079,1.08599,1.03842,1.78914,151556 +hot-min-descending,4,current,3,1.05258,0.606774,1.23835,1.04771,0.531226,1.06404,102368 +hot-min-descending,8,baseline,3,1.44626,1.36059,1.47922,1.76865,1.25509,1.7947,258032 +hot-min-descending,8,current,3,2.04729,1.96361,2.27623,0.912322,0.811252,1.09401,161680 +hot-min-descending,16,baseline,3,7.91664,7.83943,12.7164,8.47318,8.22166,8.61445,545024 +hot-min-descending,16,current,3,1.22691,1.1947,1.26469,1.13767,1.11674,1.94421,225336 +hot-min-descending,default,baseline,3,8.22635,8.00452,8.32896,10.3967,10.0381,10.5774,877080 +hot-min-descending,default,current,3,0.902368,0.896045,0.920424,0.783659,0.776415,1.64818,348436 +all-null-key,1,baseline,3,4.66494,4.61663,4.6828,4.55403,4.51564,4.55556,71276 +all-null-key,1,current,3,3.33676,3.31146,3.34507,3.26528,3.25577,3.26973,71284 +all-null-key,2,baseline,3,2.53785,2.51677,2.64689,2.47353,2.46123,2.49919,81548 +all-null-key,2,current,3,1.71983,1.7187,2.28923,1.64614,1.6408,2.19605,81816 +all-null-key,4,baseline,3,1.50898,1.49219,1.64516,1.44573,1.4333,1.50859,102084 +all-null-key,4,current,3,1.0084,0.90048,1.18321,0.840595,0.838543,0.840608,102144 +all-null-key,8,baseline,3,1.52247,1.4302,1.58456,1.44759,1.28914,1.4634,143224 +all-null-key,8,current,3,0.735873,0.627092,0.767926,0.616547,0.447994,0.656247,142988 +all-null-key,16,baseline,3,1.15113,1.12723,1.17061,1.01137,1.0036,1.02393,224864 +all-null-key,16,current,3,0.488689,0.460429,0.502272,0.409256,0.389389,0.423322,225108 +all-null-key,default,baseline,3,1.07188,1.06472,1.10126,0.86281,0.853855,0.887437,348380 +all-null-key,default,current,3,0.47369,0.457733,0.475572,0.305104,0.301478,0.313103,348652 +distinct-skew,1,baseline,3,46.7975,43.3451,47.3734,43.1594,42.816,45.77,200340 +distinct-skew,1,current,3,36.3665,36.2055,36.5383,32.3638,32.3478,32.3919,200616 +distinct-skew,2,baseline,3,22.3314,22.0884,23.5966,17.2965,16.8935,17.4656,289956 +distinct-skew,2,current,3,13.3343,13.2628,13.5379,11.0965,10.6997,11.2641,149684 +distinct-skew,4,baseline,3,17.8565,17.2376,18.1881,14.2565,13.4373,14.7782,351548 +distinct-skew,4,current,3,7.26155,7.20331,9.25518,6.03605,5.9741,7.28115,213316 +distinct-skew,8,baseline,3,14.4501,14.3777,15.5717,12.7995,12.7106,13.3417,447672 +distinct-skew,8,current,3,5.07841,4.99766,6.49705,3.99008,3.98529,4.53461,281152 +distinct-skew,16,baseline,3,15.1141,14.6078,15.4628,13.7512,13.4016,14.5776,712140 +distinct-skew,16,current,3,3.49275,3.47312,3.68146,3.14434,2.98794,3.18768,327920 +distinct-skew,default,baseline,3,14.9346,14.8426,17.4594,18.8867,13.4073,20.2078,989260 +distinct-skew,default,current,3,3.48556,3.46355,3.50959,2.77614,2.7546,2.8987,447572 +distinct-correlated,1,baseline,3,19.1869,19.0813,19.4039,16.6562,16.4069,16.8874,122196 +distinct-correlated,1,current,3,17.6032,17.5764,17.7174,14.7803,14.7172,14.8791,122772 +distinct-correlated,2,baseline,3,14.0671,14.0448,14.3082,11.92,10.9893,12.9441,189280 +distinct-correlated,2,current,3,10.5848,10.4343,11.8739,7.00951,6.95891,9.97651,121052 +distinct-correlated,4,baseline,3,11.3484,11.1748,11.7951,9.29031,9.06464,9.50877,195520 +distinct-correlated,4,current,3,5.93708,5.87688,9.25464,3.91018,3.88561,3.97343,139420 +distinct-correlated,8,baseline,3,10.8823,10.3634,11.2042,8.55283,8.54343,8.59391,318312 +distinct-correlated,8,current,3,4.31437,3.57566,5.02472,2.91264,2.32199,3.11199,179904 +distinct-correlated,16,baseline,3,10.145,9.85951,10.4594,9.91688,9.78042,10.3006,576700 +distinct-correlated,16,current,3,3.22627,2.98137,3.82435,2.17917,2.16498,2.32191,254196 +distinct-correlated,default,baseline,3,9.28634,9.07438,9.68557,10.7676,10.1387,11.5966,736772 +distinct-correlated,default,current,3,3.45239,3.29535,3.90345,2.12879,2.07931,2.99444,379160 +symbol-extrema,1,baseline,3,40.6461,39.7466,40.8204,39.1318,38.8601,40.3588,98104 +symbol-extrema,1,current,3,34.4135,34.3054,34.452,33.0126,32.7625,33.3166,98316 +symbol-extrema,2,baseline,3,39.756,39.429,42.5426,39.6291,39.1072,39.7428,108440 +symbol-extrema,2,current,3,20.1982,19.6253,23.6714,19.0356,18.8869,19.1948,108456 +symbol-extrema,4,baseline,3,39.8108,39.7666,40.0012,39.5203,39.1821,40.0674,128948 +symbol-extrema,4,current,3,11.282,11.2045,12.4997,10.8976,10.8944,10.9637,128972 +symbol-extrema,8,baseline,3,41.3553,39.606,42.7238,39.6642,39.462,40.2329,169776 +symbol-extrema,8,current,3,8.15245,8.10291,8.7357,6.82171,6.55083,6.87281,170224 +symbol-extrema,16,baseline,3,40.1852,39.9599,40.9141,39.876,39.3889,40.1824,251780 +symbol-extrema,16,current,3,5.57566,5.31708,5.95017,4.60402,4.486,4.69179,251752 +symbol-extrema,default,baseline,3,39.7437,39.699,40.1495,39.5485,39.5253,39.7111,375036 +symbol-extrema,default,current,3,5.68657,5.52136,5.85419,3.46445,3.39421,3.5543,375016 +symbol-top-bottom,1,baseline,3,44.6678,44.633,44.8164,44.311,43.6789,44.3386,116824 +symbol-top-bottom,1,current,3,40.6969,40.6463,41.0897,39.8809,39.7605,40.1995,116604 +symbol-top-bottom,2,baseline,3,45.1952,44.802,45.4442,43.9217,43.7246,44.6391,126876 +symbol-top-bottom,2,current,3,26.0174,23.9991,26.1569,25.2664,23.3436,25.411,137104 +symbol-top-bottom,4,baseline,3,45.2068,44.7031,45.4125,43.6947,43.6642,44.9294,147436 +symbol-top-bottom,4,current,3,14.3901,13.9146,16.8873,13.5237,13.5229,13.9429,157704 +symbol-top-bottom,8,baseline,3,44.9387,44.5224,45.7011,43.7981,43.2701,44.8224,188516 +symbol-top-bottom,8,current,3,9.08567,9.04237,10.0626,7.38323,7.37157,8.26782,184388 +symbol-top-bottom,16,baseline,3,46.0483,44.7708,46.9351,43.8166,43.6629,44.2343,270216 +symbol-top-bottom,16,current,3,6.3659,5.85105,6.41526,5.35388,5.30037,5.56268,264140 +symbol-top-bottom,default,baseline,3,44.7094,44.5826,45.2067,43.9132,43.7177,44.0844,393432 +symbol-top-bottom,default,current,3,6.13454,5.88677,6.32198,4.04844,4.04649,4.2073,375100 +distinct-string-values,1,current,3,25.536,23.5843,30.8713,22.7224,22.3809,22.7364,122516 +distinct-string-values,2,current,3,16.873,16.8285,17.0474,15.6609,15.4492,16.1663,139436 +distinct-string-values,4,current,3,11.3954,11.2418,12.2797,9.57085,9.43517,9.61408,159784 +distinct-string-values,8,current,3,7.07099,6.60752,9.17245,5.66938,5.47442,5.91698,200812 +distinct-string-values,16,current,3,6.30552,5.85773,6.82709,4.65505,4.42819,4.75964,282756 +distinct-string-values,default,current,3,6.38633,5.77448,6.62121,3.85778,3.81936,5.57619,405800 +distinct-string-keys,1,baseline,3,42.376,42.2942,42.7106,40.8188,40.7126,40.9586,118644 +distinct-string-keys,1,current,3,17.8817,17.853,18.376,16.8017,16.7405,16.8907,122496 +distinct-string-keys,2,baseline,3,24.8749,24.819,25.6576,22.965,22.1579,23.0613,202688 +distinct-string-keys,2,current,3,10.5246,10.4952,10.8763,9.15362,9.14033,9.27864,139104 +distinct-string-keys,4,baseline,3,13.5964,13.5332,14.8144,12.0014,11.979,12.2844,220756 +distinct-string-keys,4,current,3,6.00461,5.98426,6.85973,4.78189,4.70674,4.79168,159680 +distinct-string-keys,8,baseline,3,10.7706,10.3224,11.8566,8.06058,7.7147,8.9782,354020 +distinct-string-keys,8,current,3,4.18628,3.97352,4.31926,2.85194,2.76323,2.88225,200428 +distinct-string-keys,16,baseline,3,8.60144,8.33302,8.74936,8.73943,8.04493,8.77697,606472 +distinct-string-keys,16,current,3,3.79577,3.62774,3.86207,1.99306,1.94697,2.08663,282532 +distinct-string-keys,default,baseline,3,8.69082,8.60506,9.71789,7.44161,7.36271,7.95611,700812 +distinct-string-keys,default,current,3,4.91473,4.81321,5.43854,2.11694,2.10601,2.68161,405764 +mixed-distinct-string-keys,1,baseline,3,45.1501,45.1348,46.2738,44.8765,44.5976,45.1732,118136 +mixed-distinct-string-keys,1,current,3,34.3736,34.2599,34.4893,32.8194,32.7586,33.3654,128876 +mixed-distinct-string-keys,2,baseline,3,27.8705,27.7638,28.3756,25.6296,25.3066,26.4145,208828 +mixed-distinct-string-keys,2,current,3,18.2224,17.9577,18.31,16.5676,16.4944,16.5728,143536 +mixed-distinct-string-keys,4,baseline,3,15.8224,15.4494,19.1732,13.6008,13.5995,13.8791,225508 +mixed-distinct-string-keys,4,current,3,10.6502,10.6432,11.2706,9.11103,9.04792,9.11298,172240 +mixed-distinct-string-keys,8,baseline,3,14.7082,13.3222,16.5023,11.4747,9.31757,12.0948,354512 +mixed-distinct-string-keys,8,current,3,7.47664,7.38497,8.77116,5.58209,5.52492,6.5898,229416 +mixed-distinct-string-keys,16,baseline,3,10.7515,9.737,11.2509,10.0707,9.73404,10.7199,579688 +mixed-distinct-string-keys,16,current,3,6.4119,6.38486,6.50908,4.39218,4.19809,4.52784,336036 +mixed-distinct-string-keys,default,baseline,3,10.2163,9.66644,10.232,9.63127,9.32093,10.1374,682616 +mixed-distinct-string-keys,default,current,3,7.36882,7.26533,7.84305,3.99634,3.95088,4.06717,487468 +distinct-guid-keys,1,baseline,3,33.5498,33.4932,33.8955,32.1282,31.9255,32.5114,122460 +distinct-guid-keys,1,current,3,10.0406,9.67227,10.3539,9.02441,8.59811,9.05553,116360 +distinct-guid-keys,2,baseline,3,29.9497,29.6837,29.961,28.5032,28.387,28.5798,136596 +distinct-guid-keys,2,current,3,6.88077,6.78034,8.69829,5.486,5.40282,5.56471,132784 +distinct-guid-keys,4,baseline,3,29.6497,29.5131,30.347,28.1615,27.8987,28.4392,165340 +distinct-guid-keys,4,current,3,4.72891,4.04613,4.73443,2.91397,2.85656,2.94545,153356 +distinct-guid-keys,8,baseline,3,29.4884,29.4701,29.8262,28.0167,27.9686,28.2079,214808 +distinct-guid-keys,8,current,3,3.10369,3.00404,3.41054,1.82024,1.74199,2.13048,194328 +distinct-guid-keys,16,baseline,3,29.7032,29.6535,29.7524,28.5312,28.1036,28.6772,325180 +distinct-guid-keys,16,current,3,2.69334,2.6449,2.82222,1.308,1.30574,1.33679,276520 +distinct-guid-keys,default,baseline,3,30.1909,30.0605,33.1633,28.4582,28.4461,28.5222,476644 +distinct-guid-keys,default,current,3,4.75,3.8932,4.78535,1.58194,1.47043,1.58621,399424 +mixed-distinct-guid-keys,1,baseline,3,38.3147,37.9974,38.987,36.755,36.7185,36.8238,122752 +mixed-distinct-guid-keys,1,current,3,38.6208,38.1202,38.6767,36.9051,36.8961,37.1415,122740 +mixed-distinct-guid-keys,2,baseline,3,33.6914,33.6579,34.5185,32.2959,32.2766,32.3997,136764 +mixed-distinct-guid-keys,2,current,3,34.0543,34.0138,34.2888,32.3837,32.3134,32.4765,137132 +mixed-distinct-guid-keys,4,baseline,3,32.9669,32.7646,33.628,31.3845,31.3566,31.6305,165384 +mixed-distinct-guid-keys,4,current,3,8.37359,8.22456,9.55148,7.25415,6.97931,7.6089,196800 +mixed-distinct-guid-keys,8,baseline,3,33.8264,33.0636,34.6114,31.3789,31.3054,31.6049,215016 +mixed-distinct-guid-keys,8,current,3,7.38673,7.37446,7.50016,6.17918,6.0928,6.25889,247816 +mixed-distinct-guid-keys,16,baseline,3,33.0239,32.8723,33.3007,31.4391,31.1977,31.5628,320808 +mixed-distinct-guid-keys,16,current,3,7.60494,7.60041,8.05643,6.51012,6.38474,6.5276,352480 +mixed-distinct-guid-keys,default,baseline,3,33.3119,32.9644,33.428,31.385,31.2683,31.6063,468968 +mixed-distinct-guid-keys,default,current,3,8.24364,8.15376,8.53604,6.3326,6.31025,6.37937,495848 +distinct-list-keys,1,current,3,14.9309,14.9217,15.0125,14.6101,14.3766,14.9916,96120 +distinct-list-keys,2,current,3,8.54603,8.5087,8.61914,8.26397,8.21029,8.3329,106304 +distinct-list-keys,4,current,3,4.54288,4.53703,4.54397,4.32202,4.2516,4.33223,126708 +distinct-list-keys,8,current,3,2.96067,2.88339,3.00852,2.50874,2.27311,2.91732,167936 +distinct-list-keys,16,current,3,2.02472,2.02061,2.18033,1.73499,1.72752,1.87999,249884 +distinct-list-keys,default,current,3,2.51101,2.22404,3.50549,1.99379,1.74418,3.54334,372968 +mixed-distinct-list-keys,1,current,3,39.1396,39.1373,45.312,38.7604,38.6764,38.9243,98156 +mixed-distinct-list-keys,2,current,3,34.5949,34.5208,34.7059,34.0187,33.6618,34.2652,110740 +mixed-distinct-list-keys,4,current,3,9.8477,8.71936,11.3894,8.50203,8.43694,9.19103,169988 +mixed-distinct-list-keys,8,current,3,7.83904,7.41903,9.15951,7.00859,6.99476,7.81901,221220 +mixed-distinct-list-keys,16,current,3,7.41969,7.37227,7.58198,7.17242,7.14127,7.2114,329572 +mixed-distinct-list-keys,default,current,3,6.92429,6.75216,7.56513,6.78299,6.67453,6.85346,462320 +mixed-distinct-string-values,1,current,3,27.11,26.8965,27.8119,25.8613,25.8163,26.7063,118696 +mixed-distinct-string-values,2,current,3,66.0719,65.0125,68.2011,43.215,41.074,51.4016,256976 +mixed-distinct-string-values,4,current,3,60.1565,59.6659,60.4176,35.8191,35.7075,44.9527,275516 +mixed-distinct-string-values,8,current,3,57.1645,56.7355,58.3363,32.5605,32.3774,34.1591,324408 +mixed-distinct-string-values,16,current,3,57.979,57.1036,58.0669,32.1899,31.851,33.8689,408780 +mixed-distinct-string-values,default,current,3,58.3992,57.3275,59.2207,31.1633,30.7777,31.2679,531676 +hot-first-last,1,baseline,3,7.09583,7.07106,7.10089,6.40329,6.39774,6.42729,97912 +hot-first-last,1,current,3,4.89523,4.69748,4.93863,4.10858,3.87909,4.247,98124 +hot-first-last,2,baseline,3,7.08728,7.07491,7.33966,6.39604,6.38506,6.42094,108436 +hot-first-last,2,current,3,3.45462,3.39263,3.4625,2.61303,2.58811,2.65671,108468 +hot-first-last,4,baseline,3,7.10706,7.08247,7.18214,6.39944,6.38603,6.42485,128492 +hot-first-last,4,current,3,1.92987,1.86146,1.99187,1.55441,1.49241,1.59884,120624 +hot-first-last,8,baseline,3,7.13169,7.05969,7.1335,6.42354,6.36792,6.46108,169768 +hot-first-last,8,current,3,1.37468,1.35087,1.46065,0.952553,0.943296,0.988419,161504 +hot-first-last,16,baseline,3,7.11044,7.02647,7.17832,6.39826,6.38843,6.44796,251300 +hot-first-last,16,current,3,1.46635,1.3866,1.51605,0.688426,0.622997,0.939317,243676 +hot-first-last,default,baseline,3,7.09356,7.04062,7.10527,6.39356,6.3785,6.42971,374732 +hot-first-last,default,current,3,1.9784,1.86109,1.98415,0.534353,0.524855,0.571058,366812 +hot-symbol-extrema,1,baseline,3,34.9612,34.4095,35.51,35.9632,35.0616,36.8017,97840 +hot-symbol-extrema,1,current,3,34.5945,32.784,34.6073,33.4078,32.9806,34.846,98164 +hot-symbol-extrema,2,baseline,3,36.2219,35.6489,36.6823,35.3792,34.9845,36.5819,108452 +hot-symbol-extrema,2,current,3,18.3777,18.1992,18.4682,17.969,17.7944,18.1369,108276 +hot-symbol-extrema,4,baseline,3,37.0128,36.5557,37.3713,36.7929,36.5553,36.9002,128692 +hot-symbol-extrema,4,current,3,9.32877,9.02556,10.5134,9.21701,9.09093,9.64222,120708 +hot-symbol-extrema,8,baseline,3,37.0574,36.5754,37.7959,37.6232,35.2788,37.7483,169828 +hot-symbol-extrema,8,current,3,6.48382,6.44304,6.7901,5.66045,4.83967,5.76115,161452 +hot-symbol-extrema,16,baseline,3,36.6978,35.4158,36.7731,36.9171,35.9338,37.6562,251704 +hot-symbol-extrema,16,current,3,3.38306,3.37399,3.3892,3.21096,3.1623,3.24176,243824 +hot-symbol-extrema,default,baseline,3,35.8858,35.2741,36.2922,36.0372,35.3531,36.4221,374872 +hot-symbol-extrema,default,current,3,2.61013,2.60561,3.439,2.39432,2.12837,2.41344,366844 +hot-wide-extrema,1,baseline,3,20.694,20.6581,20.8993,19.7952,19.6449,20.1218,118348 +hot-wide-extrema,1,current,3,21.5911,20.914,23.7262,19.9433,19.7562,20.2448,118412 +hot-wide-extrema,2,baseline,3,20.6997,20.6201,20.7728,19.8962,19.5103,19.9932,128680 +hot-wide-extrema,2,current,3,12.2725,12.1016,15.2988,11.3624,11.3624,11.5005,128980 +hot-wide-extrema,4,baseline,3,21.0214,20.6815,21.314,19.8821,19.7847,20.4596,149192 +hot-wide-extrema,4,current,3,5.80419,5.79021,6.54052,5.85769,5.83435,5.86328,133052 +hot-wide-extrema,8,baseline,3,20.7487,20.6322,20.7873,19.7788,19.6198,19.9389,190096 +hot-wide-extrema,8,current,3,4.04567,3.86621,5.12004,3.53612,3.2098,3.62027,173916 +hot-wide-extrema,16,baseline,3,20.7716,20.4983,20.7774,20.1121,19.8867,20.1513,272468 +hot-wide-extrema,16,current,3,2.54247,2.37127,2.92579,2.27206,2.19736,3.47676,255772 +hot-wide-extrema,default,baseline,3,20.6251,20.6146,20.9251,19.8463,19.2109,19.9948,396004 +hot-wide-extrema,default,current,3,2.07719,2.0748,2.09843,1.67952,1.62771,1.70235,379092 +hot-mode,1,baseline,3,29.3913,29.3602,29.6403,11.2878,11.2549,11.4402,171484 +hot-mode,1,current,3,29.4346,29.2165,30.0294,11.5144,11.4595,11.534,171508 +hot-mode,2,baseline,3,29.2914,29.1384,29.5235,11.3437,11.2956,11.4704,182024 +hot-mode,2,current,3,6.85102,6.3393,6.94512,5.21663,5.1869,5.34376,100240 +hot-mode,4,baseline,3,29.0294,28.9399,29.5769,11.5566,11.4252,11.5597,202576 +hot-mode,4,current,3,4.20375,3.96442,5.30753,2.80936,2.80806,2.93627,118744 +hot-mode,8,baseline,3,29.6096,29.5426,29.6643,11.2912,11.2642,11.2924,243648 +hot-mode,8,current,3,3.30586,2.95134,3.32202,2.00511,1.8088,2.04185,159784 +hot-mode,16,baseline,3,29.6737,29.3684,29.7256,11.3577,11.2777,11.8338,325580 +hot-mode,16,current,3,2.90348,2.86293,2.90569,1.12556,1.12254,1.14461,241720 +hot-mode,default,baseline,3,29.6428,29.4175,29.7357,11.279,11.2567,11.3158,448592 +hot-mode,default,current,3,3.89075,3.57523,4.09177,1.07636,1.05377,1.07932,364804 +hot-wide-mode,1,baseline,3,32.4109,32.3858,32.7029,14.4016,14.1858,14.4321,200160 +hot-wide-mode,1,current,3,32.5927,32.4883,32.667,14.4969,14.4751,14.5299,200208 +hot-wide-mode,2,baseline,3,32.2729,32.2614,32.7914,14.4035,14.3988,14.4565,210700 +hot-wide-mode,2,current,3,8.77427,8.55513,8.90378,7.57849,7.46829,7.5789,128744 +hot-wide-mode,4,baseline,3,32.4597,32.4351,32.6595,14.4628,14.4571,14.4663,230748 +hot-wide-mode,4,current,3,4.0445,4.00538,5.56985,3.9633,3.94496,5.0221,133016 +hot-wide-mode,8,baseline,3,32.4765,32.1898,32.489,14.4314,14.4306,14.4956,271816 +hot-wide-mode,8,current,3,2.99208,2.89496,3.00188,2.81793,2.12409,2.84235,174000 +hot-wide-mode,16,baseline,3,32.4126,32.41,32.627,14.4524,14.3246,14.4634,353804 +hot-wide-mode,16,current,3,1.82127,1.7985,1.85115,1.58666,1.54677,3.26008,255844 +hot-wide-mode,default,baseline,3,32.4831,32.4526,33.004,14.4772,14.4102,14.533,477232 +hot-wide-mode,default,current,3,1.69013,1.59876,1.80628,1.271,1.26917,1.27355,379048 +hot-top-bottom,1,baseline,3,6.99542,6.96637,7.08298,5.85437,5.77201,5.88321,89744 +hot-top-bottom,1,current,3,7.10101,7.00944,7.20019,5.87199,5.84672,5.90186,89804 +hot-top-bottom,2,baseline,3,7.0241,6.96516,7.02983,5.78022,5.7627,5.83424,100276 +hot-top-bottom,2,current,3,4.30821,4.27335,4.96085,3.10691,3.0307,4.10061,100256 +hot-top-bottom,4,baseline,3,6.96259,6.95758,7.00075,5.83136,5.80959,5.83603,120716 +hot-top-bottom,4,current,3,3.39609,2.85163,3.87523,1.74382,1.70922,1.88272,118784 +hot-top-bottom,8,baseline,3,7.00591,6.99707,7.02172,5.87601,5.77123,5.88358,161372 +hot-top-bottom,8,current,3,2.37434,2.23978,3.01941,1.11068,1.08995,1.28442,159820 +hot-top-bottom,16,baseline,3,6.99347,6.98184,7.04974,5.84733,5.7892,5.86683,243740 +hot-top-bottom,16,current,3,2.52383,2.516,2.74825,0.742781,0.737863,0.745631,241552 +hot-top-bottom,default,baseline,3,7.11187,6.97963,13.6961,5.82342,5.82058,5.84257,366644 +hot-top-bottom,default,current,3,3.75484,3.5996,3.8492,0.613136,0.60594,1.2043,364796 +hot-wide-top-bottom,1,baseline,3,20.3768,19.9263,20.7506,19.4183,18.8677,19.8942,118412 +hot-wide-top-bottom,1,current,3,19.7617,19.5688,20.0408,18.7577,18.6464,19.0285,118480 +hot-wide-top-bottom,2,baseline,3,20.5804,20.1195,21.1308,19.4103,19.2686,19.4594,128692 +hot-wide-top-bottom,2,current,3,13.485,13.3409,14.4274,12.6374,12.527,12.6936,128908 +hot-wide-top-bottom,4,baseline,3,20.3871,19.6826,21.1044,18.9803,18.9387,19.8146,149428 +hot-wide-top-bottom,4,current,3,6.7387,6.50707,7.51752,6.61764,6.57188,6.78126,133096 +hot-wide-top-bottom,8,baseline,3,20.4273,20.3459,20.688,19.1342,19.0817,19.5835,190512 +hot-wide-top-bottom,8,current,3,4.38395,3.66835,4.50249,4.06755,4.05583,4.20675,173916 +hot-wide-top-bottom,16,baseline,3,20.6313,20.22,20.9398,19.5523,18.8556,19.8103,272500 +hot-wide-top-bottom,16,current,3,2.83021,2.56538,2.90661,2.42207,2.23039,2.47724,256092 +hot-wide-top-bottom,default,baseline,3,20.1625,19.6909,20.1953,19.0481,18.9574,19.1067,395544 +hot-wide-top-bottom,default,current,3,2.21912,2.08679,2.43237,1.95261,1.90556,1.95512,379356 +hot-symbol-top-bottom,1,baseline,3,35.5297,35.2429,35.7998,35.4424,34.6725,35.769,97488 +hot-symbol-top-bottom,1,current,3,34.1941,34.0463,36.6947,33.9562,33.8775,36.2639,97980 +hot-symbol-top-bottom,2,baseline,3,36.1349,35.5935,36.6034,34.9645,34.9613,35.7782,108208 +hot-symbol-top-bottom,2,current,3,21.5157,21.0883,21.6777,20.9576,20.5566,21.075,108440 +hot-symbol-top-bottom,4,baseline,3,35.9773,35.2368,36.6876,35.0072,34.3895,35.7326,129008 +hot-symbol-top-bottom,4,current,3,10.7044,10.5359,13.3514,10.8506,10.695,10.8565,120836 +hot-symbol-top-bottom,8,baseline,3,36.2894,35.9618,36.5853,35.6767,34.8364,36.3268,170056 +hot-symbol-top-bottom,8,current,3,6.48601,6.46817,6.89955,6.29204,6.07112,6.31368,161748 +hot-symbol-top-bottom,16,baseline,3,35.1957,34.7294,35.6344,34.6904,34.6876,34.9381,251692 +hot-symbol-top-bottom,16,current,3,3.70493,3.67376,3.73629,3.48573,3.2646,3.51375,243864 +hot-symbol-top-bottom,default,baseline,3,35.3818,35.0857,36.5757,34.8644,34.8419,35.2068,375272 +hot-symbol-top-bottom,default,current,3,2.78521,2.71892,3.09585,2.4075,2.33323,2.48572,367064 +hot-max-k,1,baseline,3,7.35581,7.33929,7.40069,6.20819,6.14517,6.22762,89476 +hot-max-k,1,current,3,7.44675,7.42152,7.44743,6.20461,6.17833,6.26316,89712 +hot-max-k,2,baseline,3,7.41444,7.38344,7.59539,6.26401,6.19853,6.26789,100512 +hot-max-k,2,current,3,4.69163,4.63966,5.74057,3.39216,3.37885,3.42091,100020 +hot-max-k,4,baseline,3,7.37509,7.36134,7.42052,6.14683,6.13649,6.21471,120824 +hot-max-k,4,current,3,3.45289,3.32557,4.14981,2.11833,2.09552,2.60527,118900 +hot-max-k,8,baseline,3,7.40997,7.40579,7.43164,6.17699,6.13231,6.24115,161632 +hot-max-k,8,current,3,2.95818,2.72658,2.98685,1.67287,1.63454,1.69518,159440 +hot-max-k,16,baseline,3,7.3535,7.3466,7.54174,6.1588,6.15276,6.22784,243832 +hot-max-k,16,current,3,2.99184,2.95809,3.03695,1.28785,1.22919,1.3723,241620 +hot-max-k,default,baseline,3,7.66154,7.34957,8.74676,6.1794,6.16771,6.1954,367056 +hot-max-k,default,current,3,4.38078,3.93952,4.85503,1.28793,1.25868,1.30467,364676 diff --git a/bench/groupby_shapes/results/2026-09-15/phase-profiles.csv b/bench/groupby_shapes/results/2026-09-15/phase-profiles.csv new file mode 100644 index 000000000..f2c0a1598 --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-15/phase-profiles.csv @@ -0,0 +1,181 @@ +case,workers,binary,phase,processes,cold_median_ms,cold_min_ms,cold_max_ms,warm_median_ms,warm_min_ms,warm_max_ms +extrema-f32,4,baseline,group: v2 dense,2,0.277,0.266,0.288,0.22599999999999998,0.214,0.238 +extrema-f32,4,baseline,dense: initialized slabs,2,0.5525,0.438,0.667,0.3625,0.36,0.365 +extrema-f32,4,baseline,dense: accumulated rows,2,1.1284999999999998,1.069,1.188,1.111,1.091,1.131 +extrema-f32,4,baseline,dense: merged slabs,2,0.34199999999999997,0.34,0.344,0.34,0.338,0.342 +extrema-f32,4,baseline,dense: freed slabs and counted groups,2,0.012,0.012,0.012,0.011,0.011,0.011 +extrema-f32,4,current,group: v2 dense,2,0.107,0.103,0.111,0.0605,0.058,0.063 +extrema-f32,4,current,dense: allocated slabs,2,0.642,0.633,0.651,0.001,0.001,0.001 +extrema-f32,4,current,dense: accumulated rows,2,1.2205,1.217,1.224,0.8109999999999999,0.801,0.821 +extrema-f32,4,current,dense: merged slabs,2,0.3565,0.349,0.364,0.273,0.273,0.273 +extrema-f32,4,current,dense: freed slabs and counted groups,2,0.0135,0.013,0.014,0.009,0.009,0.009 +clustered,4,baseline,group: v2 dense,2,0.202,0.195,0.209,0.1815,0.181,0.182 +clustered,4,baseline,dense: initialized slabs,2,0.23149999999999998,0.168,0.295,0.4205,0.313,0.528 +clustered,4,baseline,dense: accumulated rows,2,0.515,0.35,0.68,0.4615,0.32,0.603 +clustered,4,baseline,dense: merged slabs,2,0.35050000000000003,0.342,0.359,0.351,0.343,0.359 +clustered,4,baseline,dense: freed slabs and counted groups,2,0.0225,0.022,0.023,0.0215,0.021,0.022 +clustered,4,current,group: v2 dense,2,0.2515,0.249,0.254,0.1275,0.055,0.2 +clustered,4,current,dense: allocated slabs,2,0.624,0.62,0.628,0.001,0.001,0.001 +clustered,4,current,dense: accumulated rows,2,1.141,1.134,1.148,0.5840000000000001,0.401,0.767 +clustered,4,current,dense: merged slabs,2,0.1655,0.161,0.17,0.1585,0.157,0.16 +clustered,4,current,dense: freed slabs and counted groups,2,0.02,0.02,0.02,0.019,0.019,0.019 +symbol-key,2,baseline,group: v2 dense,2,0.013999999999999999,0.013,0.015,0.005,0.004,0.006 +symbol-key,2,baseline,dense: initialized slabs,2,0.003,0.003,0.003,0.0025,0.002,0.003 +symbol-key,2,baseline,dense: accumulated rows,2,0.762,0.625,0.899,0.7025,0.547,0.858 +symbol-key,2,baseline,dense: merged slabs,2,0.003,0.003,0.003,0.002,0.002,0.002 +symbol-key,2,baseline,dense: freed slabs and counted groups,2,0.0005,0.0,0.001,0.0005,0.0,0.001 +symbol-key,2,current,group: v2 dense,2,0.0155,0.014,0.017,0.005,0.005,0.005 +symbol-key,2,current,dense: allocated slabs,2,0.0025,0.002,0.003,0.002,0.002,0.002 +symbol-key,2,current,dense: accumulated rows,2,0.7255,0.645,0.806,0.5694999999999999,0.568,0.571 +symbol-key,2,current,dense: merged slabs,2,0.002,0.002,0.002,0.0015,0.001,0.002 +symbol-key,2,current,dense: freed slabs and counted groups,2,0.001,0.001,0.001,0.0,0.0,0.0 +hot-min-descending,8,baseline,group: v2 dense,2,0.2495,0.238,0.261,0.2265,0.203,0.25 +hot-min-descending,8,baseline,dense: initialized slabs,2,0.2,0.19,0.21,0.4115,0.403,0.42 +hot-min-descending,8,baseline,dense: accumulated rows,2,0.36,0.343,0.377,0.3685,0.353,0.384 +hot-min-descending,8,baseline,dense: merged slabs,2,0.21300000000000002,0.203,0.223,0.21000000000000002,0.167,0.253 +hot-min-descending,8,baseline,dense: freed slabs and counted groups,2,0.015,0.014,0.016,0.013999999999999999,0.013,0.015 +hot-min-descending,8,current,group: v2 dense,2,0.1355,0.135,0.136,0.1335,0.129,0.138 +hot-min-descending,8,current,dense: allocated slabs,2,0.6595,0.643,0.676,0.0,0.0,0.0 +hot-min-descending,8,current,dense: accumulated rows,2,0.996,0.99,1.002,0.5740000000000001,0.56,0.588 +hot-min-descending,8,current,dense: merged slabs,2,0.19,0.172,0.208,0.14,0.137,0.143 +hot-min-descending,8,current,dense: freed slabs and counted groups,2,0.013,0.013,0.013,0.0115,0.011,0.012 +sum-f64,8,baseline,group: v2 dense,2,0.324,0.272,0.376,0.21800000000000003,0.203,0.233 +sum-f64,8,baseline,dense: initialized slabs,2,0.2275,0.221,0.234,0.44599999999999995,0.329,0.563 +sum-f64,8,baseline,dense: accumulated rows,2,0.885,0.712,1.058,0.716,0.397,1.035 +sum-f64,8,baseline,dense: merged slabs,2,1.3785,1.366,1.391,1.147,0.931,1.363 +sum-f64,8,baseline,dense: freed slabs and counted groups,2,0.013999999999999999,0.013,0.015,0.012,0.012,0.012 +sum-f64,8,current,group: v2 dense,2,0.1285,0.114,0.143,0.112,0.111,0.113 +sum-f64,8,current,dense: allocated slabs,2,0.6355,0.633,0.638,0.0,0.0,0.0 +sum-f64,8,current,dense: accumulated rows,2,1.525,1.323,1.727,1.1185,0.933,1.304 +sum-f64,8,current,dense: merged slabs,2,0.296,0.29,0.302,0.2425,0.241,0.244 +sum-f64,8,current,dense: freed slabs and counted groups,2,0.012,0.012,0.012,0.011,0.011,0.011 +float-key,default,baseline,group: parallel wide-key strategy,2,0.0075,0.007,0.008,0.0025,0.002,0.003 +float-key,default,baseline,group: legacy,2,0.001,0.001,0.001,0.0,0.0,0.0 +float-key,default,current,group: v2 indexed,2,0.008,0.007,0.009,0.002,0.002,0.002 +float-key,default,current,directory: allocated,2,0.677,0.639,0.715,0.0,0.0,0.0 +float-key,default,current,directory: initialized,2,0.374,0.331,0.417,0.094,0.086,0.102 +float-key,default,current,directory: inserted keys,2,1.545,1.399,1.691,0.7795,0.705,0.854 +float-key,default,current,directory: counted groups,2,0.153,0.152,0.154,0.107,0.086,0.128 +float-key,default,current,directory: compacted groups,2,0.124,0.116,0.132,0.14150000000000001,0.118,0.165 +float-key,default,current,directory: remapped rows,2,0.1275,0.104,0.151,0.119,0.096,0.142 +float-key,default,current,indexed: grouped keys,2,0.066,0.056,0.076,0.043,0.043,0.043 +float-key,default,current,indexed slices: histogram,2,0.093,0.062,0.124,0.07300000000000001,0.062,0.084 +float-key,default,current,indexed slices: packed,2,0.138,0.093,0.183,0.1115,0.096,0.127 +float-key,default,current,indexed slices: counted groups,2,0.1645,0.128,0.201,0.119,0.109,0.129 +float-key,default,current,indexed slices: filled,2,1.4855,1.16,1.811,0.5885,0.507,0.67 +float-key,default,current,indexed: stable row slices,2,0.001,0.001,0.001,0.0,0.0,0.0 +float-key,default,current,indexed: emitted keys,2,0.08399999999999999,0.065,0.103,0.0565,0.05,0.063 +float-key,default,current,indexed: emitted aggregate,2,0.4235,0.378,0.469,0.2395,0.204,0.275 +clustered,8,baseline,group: v2 dense,2,0.208,0.205,0.211,0.1885,0.186,0.191 +clustered,8,baseline,dense: initialized slabs,2,0.3695,0.352,0.387,0.617,0.478,0.756 +clustered,8,baseline,dense: accumulated rows,2,0.4385,0.382,0.495,0.35150000000000003,0.343,0.36 +clustered,8,baseline,dense: merged slabs,2,0.40549999999999997,0.352,0.459,0.324,0.319,0.329 +clustered,8,baseline,dense: freed slabs and counted groups,2,0.027999999999999997,0.026,0.03,0.024,0.024,0.024 +clustered,8,current,group: v2 dense,2,0.088,0.059,0.117,0.10350000000000001,0.101,0.106 +clustered,8,current,dense partition: histogram,2,0.819,0.817,0.821,0.179,0.178,0.18 +clustered,8,current,dense partition: scattered rows,2,1.0070000000000001,0.93,1.084,0.308,0.297,0.319 +clustered,8,current,dense partition: reduced groups,2,0.2175,0.205,0.23,0.2095,0.208,0.211 +clustered,8,current,dense partition: merged split partitions,2,0.0055,0.005,0.006,0.0055,0.005,0.006 +clustered,8,current,dense: freed slabs and counted groups,2,0.0215,0.021,0.022,0.02,0.02,0.02 +sparse,default,baseline,group: v2 radix,2,0.3655,0.352,0.379,0.40249999999999997,0.395,0.41 +sparse,default,current,group: v2 radix,2,0.07150000000000001,0.07,0.073,0.1305,0.126,0.135 +sparse,default,current,radix: scattered rows,2,1.4155,1.381,1.45,2.0185,1.932,2.105 +sparse,default,current,radix: reduced partitions,2,1.3475000000000001,1.252,1.443,1.6475,1.623,1.672 +sparse,default,current,radix: ordered groups,2,1.6724999999999999,1.656,1.689,0.9005000000000001,0.883,0.918 +truth,2,baseline,group: v2 dense,2,0.263,0.26,0.266,0.223,0.213,0.233 +truth,2,baseline,dense: initialized slabs,2,0.3105,0.307,0.314,0.403,0.398,0.408 +truth,2,baseline,dense: accumulated rows,2,1.913,1.883,1.943,2.1065,1.89,2.323 +truth,2,baseline,dense: merged slabs,2,0.3035,0.301,0.306,0.3105,0.306,0.315 +truth,2,baseline,dense: freed slabs and counted groups,2,0.017,0.017,0.017,0.0135,0.012,0.015 +truth,2,current,group: v2 dense,2,0.1625,0.161,0.164,0.093,0.093,0.093 +truth,2,current,dense: allocated slabs,2,0.001,0.001,0.001,0.0,0.0,0.0 +truth,2,current,dense: accumulated rows,2,1.809,1.807,1.811,1.7515,1.747,1.756 +truth,2,current,dense: merged slabs,2,1.0205,1.011,1.03,0.247,0.247,0.247 +truth,2,current,dense: freed slabs and counted groups,2,0.013000000000000001,0.012,0.014,0.008,0.008,0.008 +nonnull,8,baseline,group: v2 dense,2,0.275,0.274,0.276,0.2455,0.243,0.248 +nonnull,8,baseline,dense: initialized slabs,2,0.39149999999999996,0.37,0.413,0.755,0.707,0.803 +nonnull,8,baseline,dense: accumulated rows,2,0.8140000000000001,0.793,0.835,0.857,0.855,0.859 +nonnull,8,baseline,dense: merged slabs,2,0.515,0.511,0.519,0.45999999999999996,0.434,0.486 +nonnull,8,baseline,dense: freed slabs and counted groups,2,0.015,0.015,0.015,0.014499999999999999,0.014,0.015 +nonnull,8,current,group: v2 dense,2,0.166,0.161,0.171,0.10300000000000001,0.101,0.105 +nonnull,8,current,dense partition: histogram,2,1.1825,1.073,1.292,0.5549999999999999,0.478,0.632 +nonnull,8,current,dense partition: scattered rows,2,0.9944999999999999,0.886,1.103,0.6305000000000001,0.615,0.646 +nonnull,8,current,dense partition: reduced groups,2,0.7070000000000001,0.648,0.766,0.702,0.636,0.768 +nonnull,8,current,dense partition: merged split partitions,2,0.0035,0.003,0.004,0.004,0.004,0.004 +nonnull,8,current,dense: freed slabs and counted groups,2,0.012,0.012,0.012,0.011,0.011,0.011 +product,8,baseline,group: v2 dense,2,0.2755,0.274,0.277,0.235,0.232,0.238 +product,8,baseline,dense: initialized slabs,2,0.2955,0.273,0.318,0.6265000000000001,0.585,0.668 +product,8,baseline,dense: accumulated rows,2,0.8185,0.528,1.109,0.8280000000000001,0.555,1.101 +product,8,baseline,dense: merged slabs,2,0.606,0.576,0.636,0.604,0.572,0.636 +product,8,baseline,dense: freed slabs and counted groups,2,0.0135,0.013,0.014,0.014,0.014,0.014 +product,8,current,group: v2 dense,2,0.16999999999999998,0.141,0.199,0.113,0.113,0.113 +product,8,current,dense: allocated slabs,2,0.636,0.631,0.641,0.0,0.0,0.0 +product,8,current,dense: accumulated rows,2,1.7905,1.436,2.145,1.361,0.993,1.729 +product,8,current,dense: merged slabs,2,0.30100000000000005,0.271,0.331,0.2385,0.224,0.253 +product,8,current,dense: freed slabs and counted groups,2,0.0125,0.012,0.013,0.0115,0.011,0.012 +extrema-i32,4,baseline,group: v2 dense,2,0.266,0.262,0.27,0.2145,0.207,0.222 +extrema-i32,4,baseline,dense: initialized slabs,2,0.522,0.422,0.622,0.5175,0.463,0.572 +extrema-i32,4,baseline,dense: accumulated rows,2,1.3185,0.995,1.642,1.244,1.053,1.435 +extrema-i32,4,baseline,dense: merged slabs,2,0.4455,0.325,0.566,0.4165,0.333,0.5 +extrema-i32,4,baseline,dense: freed slabs and counted groups,2,0.011,0.011,0.011,0.0115,0.011,0.012 +extrema-i32,4,current,group: v2 dense,2,0.224,0.187,0.261,0.1575,0.085,0.23 +extrema-i32,4,current,dense: allocated slabs,2,0.0015,0.001,0.002,0.001,0.001,0.001 +extrema-i32,4,current,dense: accumulated rows,2,1.565,1.562,1.568,1.101,0.764,1.438 +extrema-i32,4,current,dense: merged slabs,2,0.498,0.466,0.53,0.3725,0.287,0.458 +extrema-i32,4,current,dense: freed slabs and counted groups,2,0.0215,0.011,0.032,0.010499999999999999,0.01,0.011 +sum-time,4,baseline,group: v2 dense,2,0.2665,0.264,0.269,0.1965,0.193,0.2 +sum-time,4,baseline,dense: initialized slabs,2,0.20950000000000002,0.199,0.22,0.42000000000000004,0.389,0.451 +sum-time,4,baseline,dense: accumulated rows,2,1.097,0.946,1.248,0.766,0.749,0.783 +sum-time,4,baseline,dense: merged slabs,2,0.489,0.489,0.489,0.476,0.471,0.481 +sum-time,4,baseline,dense: freed slabs and counted groups,2,0.0155,0.014,0.017,0.0115,0.011,0.012 +sum-time,4,current,group: v2 dense,2,0.22449999999999998,0.164,0.285,0.1295,0.059,0.2 +sum-time,4,current,dense: allocated slabs,2,0.0015,0.001,0.002,0.001,0.001,0.001 +sum-time,4,current,dense: accumulated rows,2,1.0025,0.99,1.015,0.6655,0.447,0.884 +sum-time,4,current,dense: merged slabs,2,0.2415,0.239,0.244,0.1925,0.15,0.235 +sum-time,4,current,dense: freed slabs and counted groups,2,0.01,0.01,0.01,0.01,0.01,0.01 +sum-i64,8,baseline,group: v2 dense,2,0.36550000000000005,0.275,0.456,0.22699999999999998,0.212,0.242 +sum-i64,8,baseline,dense: initialized slabs,2,0.242,0.242,0.242,0.5545,0.532,0.577 +sum-i64,8,baseline,dense: accumulated rows,2,0.877,0.696,1.058,0.8815,0.731,1.032 +sum-i64,8,baseline,dense: merged slabs,2,1.2375,1.183,1.292,1.2565,1.244,1.269 +sum-i64,8,baseline,dense: freed slabs and counted groups,2,0.0125,0.012,0.013,0.012,0.012,0.012 +sum-i64,8,current,group: v2 dense,2,0.183,0.173,0.193,0.11499999999999999,0.112,0.118 +sum-i64,8,current,dense: allocated slabs,2,0.6255,0.622,0.629,0.0,0.0,0.0 +sum-i64,8,current,dense: accumulated rows,2,1.3519999999999999,1.03,1.674,0.94,0.898,0.982 +sum-i64,8,current,dense: merged slabs,2,0.26049999999999995,0.236,0.285,0.251,0.225,0.277 +sum-i64,8,current,dense: freed slabs and counted groups,2,0.012,0.011,0.013,0.0115,0.011,0.012 +sum-i64,default,baseline,group: dense worker budget exceeded,2,0.4585,0.358,0.559,0.3425,0.333,0.352 +sum-i64,default,baseline,group: v2 radix,2,0.0,0.0,0.0,0.0,0.0,0.0 +sum-i64,default,current,group: v2 dense,2,0.08199999999999999,0.072,0.092,0.0475,0.045,0.05 +sum-i64,default,current,dense partition: histogram,2,0.8315,0.809,0.854,0.202,0.202,0.202 +sum-i64,default,current,dense partition: scattered rows,2,1.6,1.558,1.642,0.196,0.191,0.201 +sum-i64,default,current,dense partition: reduced groups,2,0.23199999999999998,0.225,0.239,0.1365,0.134,0.139 +sum-i64,default,current,dense partition: merged split partitions,2,0.02,0.019,0.021,0.019000000000000003,0.017,0.021 +sum-i64,default,current,dense: freed slabs and counted groups,2,0.024,0.012,0.036,0.018000000000000002,0.011,0.025 +guid-key,8,baseline,group: v2 indexed,2,0.008,0.008,0.008,0.0025,0.002,0.003 +guid-key,8,current,group: v2 indexed,2,0.0095,0.009,0.01,0.003,0.003,0.003 +guid-key,8,current,directory: allocated,2,0.6715,0.662,0.681,0.0005,0.0,0.001 +guid-key,8,current,directory: initialized,2,0.279,0.246,0.312,0.163,0.162,0.164 +guid-key,8,current,directory: inserted keys,2,1.001,0.836,1.166,0.744,0.707,0.781 +guid-key,8,current,directory: counted groups,2,0.089,0.078,0.1,0.074,0.074,0.074 +guid-key,8,current,directory: compacted groups,2,0.038000000000000006,0.034,0.042,0.0345,0.034,0.035 +guid-key,8,current,directory: remapped rows,2,0.068,0.057,0.079,0.069,0.069,0.069 +guid-key,8,current,indexed: grouped keys,2,0.001,0.001,0.001,0.001,0.001,0.001 +guid-key,8,current,indexed: stable row slices,2,0.5820000000000001,0.511,0.653,0.164,0.163,0.165 +guid-key,8,current,indexed: emitted keys,2,0.0015,0.001,0.002,0.001,0.001,0.001 +guid-key,8,current,indexed: emitted aggregate,2,0.502,0.495,0.509,0.3825,0.367,0.398 +guid-key,8,current,rank: selected dominant groups,2,1.4835,1.423,1.544,1.4020000000000001,1.37,1.434 +guid-key,8,current,rank: reduced remaining groups,2,0.003,0.003,0.003,0.003,0.003,0.003 +guid-key,default,baseline,group: v2 indexed,2,0.008,0.008,0.008,0.002,0.002,0.002 +guid-key,default,current,group: v2 indexed,2,0.010499999999999999,0.009,0.012,0.0025,0.002,0.003 +guid-key,default,current,directory: allocated,2,0.622,0.603,0.641,0.001,0.001,0.001 +guid-key,default,current,directory: initialized,2,0.3805,0.366,0.395,0.2245,0.224,0.225 +guid-key,default,current,directory: inserted keys,2,1.297,1.253,1.341,0.40249999999999997,0.395,0.41 +guid-key,default,current,directory: counted groups,2,0.078,0.076,0.08,0.0485,0.043,0.054 +guid-key,default,current,directory: compacted groups,2,0.022,0.021,0.023,0.0215,0.021,0.022 +guid-key,default,current,directory: remapped rows,2,0.0455,0.045,0.046,0.045,0.045,0.045 +guid-key,default,current,indexed: grouped keys,2,0.001,0.001,0.001,0.001,0.001,0.001 +guid-key,default,current,indexed: stable row slices,2,1.884,1.805,1.963,0.082,0.081,0.083 +guid-key,default,current,indexed: emitted keys,2,0.009999999999999998,0.009,0.011,0.001,0.001,0.001 +guid-key,default,current,indexed: emitted aggregate,2,0.4935,0.476,0.511,0.211,0.21,0.212 +guid-key,default,current,rank: selected dominant groups,2,1.7025000000000001,1.512,1.893,1.682,1.377,1.987 +guid-key,default,current,rank: reduced remaining groups,2,0.016,0.013,0.019,0.014499999999999999,0.014,0.015 diff --git a/bench/groupby_shapes/results/2026-09-15/processes.csv b/bench/groupby_shapes/results/2026-09-15/processes.csv new file mode 100644 index 000000000..cf7170ee9 --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-15/processes.csv @@ -0,0 +1,2701 @@ +case,workers,binary,sha256,round,rows,groups,cold_ms,peak_rss_kib,result_sha256,warm_median_ms,warm_1_ms,warm_2_ms,warm_3_ms,warm_4_ms,warm_5_ms +sum-bool,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.84029,71188,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,2.69803,2.69338,2.69803,2.69106,2.7116,2.70644 +sum-bool,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.83958,71028,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.66011,1.67381,1.66224,1.6541,1.65769,1.66011 +sum-bool,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,1.98643,95924,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.72391,1.72391,1.95374,1.69098,1.82202,1.68245 +sum-bool,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.22503,81584,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.11582,1.11817,1.11582,1.11727,1.11338,1.11132 +sum-bool,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,1.45689,126948,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.44421,1.55263,1.34111,1.7022,1.32502,1.44421 +sum-bool,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.56088,102120,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,0.734525,1.38769,0.735753,0.731474,0.73046,0.734525 +sum-bool,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.78327,199900,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,2.76534,2.76534,1.8398,3.20719,2.46263,2.90919 +sum-bool,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.78182,142920,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.53973,1.67467,1.68282,1.53973,1.11691,1.19448 +sum-bool,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.14442,348288,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,5.20144,4.7971,4.66656,6.50888,5.20144,6.0742 +sum-bool,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.996806,225176,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,0.918078,0.791002,0.918078,0.929396,0.940238,0.80067 +sum-bool,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.91682,749624,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,5.28817,5.28817,5.10961,6.28428,5.2399,6.18588 +sum-bool,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.839015,348196,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,0.643422,0.699736,0.643422,0.625711,0.714956,0.615977 +extrema-bool,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.19557,71028,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,3.02039,3.03857,2.98298,2.95707,3.02747,3.02039 +extrema-bool,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.84382,71208,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,2.62084,2.65252,2.62084,2.61993,2.61404,2.63297 +extrema-bool,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.4467,104088,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,2.23056,2.55055,2.32675,1.97633,2.23056,1.99682 +extrema-bool,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.8371,81524,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.73467,1.73584,1.73467,1.73169,1.76625,1.72338 +extrema-bool,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,1.95792,169416,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,2.04509,2.04509,2.43805,1.69009,2.07742,1.80743 +extrema-bool,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.06788,101976,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.1646,1.27576,1.23234,1.1646,1.16086,1.14701 +extrema-bool,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.35131,300728,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,3.60044,3.60044,3.18023,4.41703,3.61146,3.04843 +extrema-bool,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.28211,143196,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.72616,2.26358,1.99024,1.62486,1.63282,1.72616 +extrema-bool,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.79115,507888,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,4.63651,4.2763,4.91498,4.63651,3.84428,5.52086 +extrema-bool,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.13552,225128,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.83127,1.51155,1.47836,1.83127,1.9465,1.8874 +extrema-bool,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.13591,809292,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,5.60263,5.39616,6.54593,4.74846,5.60263,8.72862 +extrema-bool,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.988766,348356,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,0.814595,0.879929,0.836259,0.805611,0.805832,0.814595 +sum-u8,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.87442,70932,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,2.69868,2.69868,2.69519,2.68813,2.71272,2.7044 +sum-u8,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.82349,71536,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.65574,1.66772,1.65512,1.65544,1.66702,1.65574 +sum-u8,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.03569,95920,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.7313,1.7313,1.99937,1.7188,1.82547,1.68594 +sum-u8,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.45635,81744,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.1986,1.2939,1.29231,1.1986,1.17552,1.11363 +sum-u8,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.06063,126740,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.91371,2.20243,1.91371,2.4272,1.87738,1.4149 +sum-u8,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.61996,102036,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.43282,1.58715,1.49256,1.43282,1.42859,1.42507 +sum-u8,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.27418,200560,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,3.1271,3.32739,2.93051,3.75022,3.1271,2.96594 +sum-u8,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.50413,143120,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.37373,1.37279,1.40417,1.51601,1.36798,1.37373 +sum-u8,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.09348,348236,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,5.15294,5.46629,5.15294,5.49655,4.08691,4.78483 +sum-u8,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.04345,225136,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,0.884019,0.884019,0.946074,0.815991,0.925342,0.835646 +sum-u8,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.04075,756084,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,4.8532,4.85175,4.41111,6.24515,4.8532,5.48187 +sum-u8,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.797461,348112,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.36317,0.68255,1.29027,1.36317,1.49408,1.3846 +extrema-u8,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.29172,70728,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,3.0048,2.95851,3.00506,3.0048,2.95557,3.04542 +extrema-u8,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.08847,71264,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.80163,2.84814,2.82755,2.80163,2.75288,2.7626 +extrema-u8,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.76971,103816,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.2824,2.58948,2.4022,2.03546,2.2824,2.08734 +extrema-u8,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.15706,81532,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,1.87726,1.9192,1.87726,1.88585,1.86188,1.84688 +extrema-u8,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.89413,169504,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.06043,2.62644,2.41707,1.82885,2.06043,1.86134 +extrema-u8,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.6939,102360,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,1.28068,1.4003,1.29125,1.28068,1.26719,1.27358 +extrema-u8,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.84083,300744,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.81285,3.3643,2.45228,3.03001,2.49659,2.81285 +extrema-u8,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.95566,142896,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.00885,1.94925,1.94615,2.00885,2.18242,2.18841 +extrema-u8,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.86416,511956,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,4.22231,4.22231,4.95308,4.14618,3.75908,5.45574 +extrema-u8,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.21197,225216,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,1.15331,1.2029,1.15979,1.15331,1.14639,1.12049 +extrema-u8,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.1046,786732,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,6.30159,4.93492,6.30159,4.36774,6.92823,6.6932 +extrema-u8,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.04023,348556,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,0.899765,0.908493,0.871367,0.879776,0.899765,0.906541 +sum-i16,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.89775,71020,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.77812,2.82442,2.79117,2.77499,2.75549,2.77812 +sum-i16,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.82626,71200,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.66239,1.66239,1.65782,1.66249,1.66178,1.6702 +sum-i16,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.12622,96160,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.73741,1.73741,1.99618,1.72421,1.85394,1.71025 +sum-i16,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.22018,81820,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.11574,1.12077,1.11574,1.11486,1.11916,1.11272 +sum-i16,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.17748,126268,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.38873,1.88947,1.24751,1.59695,1.22203,1.38873 +sum-i16,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.903795,101956,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.740804,0.825396,0.740804,0.738327,0.732612,0.815267 +sum-i16,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.52005,200588,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.9961,3.09517,2.71377,3.48076,2.6593,2.9961 +sum-i16,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.92831,161968,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.81483,2.52651,1.82925,1.81483,1.7314,1.75493 +sum-i16,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.4798,345796,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.56419,5.09776,5.56419,5.68964,5.5041,5.58465 +sum-i16,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.05453,225344,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.872707,0.973037,0.872707,0.837555,0.933515,0.863656 +sum-i16,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.03637,743480,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.31133,5.31133,4.79074,6.37936,4.84088,7.27584 +sum-i16,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.79728,348440,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.29041,0.669796,1.28662,1.55945,1.43481,1.29041 +extrema-i16,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.91731,71016,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.67714,3.66649,3.67714,3.74056,3.67438,3.76203 +extrema-i16,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.89722,71092,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.70568,2.71448,2.701,2.73684,2.6997,2.70568 +extrema-i16,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.39343,104000,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.92125,3.09537,3.20204,2.68754,2.87872,2.92125 +extrema-i16,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.98565,81264,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.85644,1.92609,1.87198,1.83723,1.81977,1.85644 +extrema-i16,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.2415,169468,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.40672,3.18106,2.69763,1.97352,2.40672,2.18912 +extrema-i16,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.36892,101996,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.25062,1.25062,1.25083,1.25687,1.24374,1.22551 +extrema-i16,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.57199,301116,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.57657,2.7489,2.34147,3.08228,2.57657,2.41172 +extrema-i16,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.086,142952,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.90926,1.86531,1.90926,1.90711,1.91446,1.93456 +extrema-i16,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.00246,521792,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,4.68394,4.68394,5.11797,4.56625,3.93047,5.26844 +extrema-i16,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.34213,225132,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.41208,1.29081,1.30374,1.51365,1.56082,1.41208 +extrema-i16,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.40969,788788,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,7.14008,5.58003,7.14008,5.59483,8.07779,11.4438 +extrema-i16,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.09236,347924,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.898898,0.898898,0.950776,0.881793,0.890838,0.941293 +sum-i32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.88167,71012,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.74592,2.73498,2.73214,2.75758,2.75346,2.74592 +sum-i32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.86791,70916,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.69419,1.72918,1.69419,1.69984,1.68206,1.68109 +sum-i32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.10598,95908,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.79,1.79,2.1591,1.74042,1.86512,1.71671 +sum-i32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.26256,81484,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.12289,1.13623,1.12925,1.11939,1.12289,1.1172 +sum-i32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.14902,126220,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.54273,2.22347,1.54273,1.58564,1.21995,1.38908 +sum-i32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.52026,101844,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.41931,1.36022,1.35901,1.44123,1.41931,1.85309 +sum-i32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.17427,200368,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,3.55899,4.3805,3.06307,3.74775,2.93948,3.55899 +sum-i32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.31471,161556,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.1077,1.12382,1.1077,1.11467,1.09533,1.09382 +sum-i32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.95902,347860,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.45707,5.71295,4.40496,5.63328,4.11182,5.45707 +sum-i32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.0332,225164,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.877543,0.893425,0.776848,0.915086,0.877543,0.864 +sum-i32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.21954,747880,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.05723,5.05723,4.11742,6.54461,5.71183,4.5997 +sum-i32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.773389,348668,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.633517,0.731906,0.671649,0.633517,0.602312,0.630896 +extrema-i32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.83955,71292,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.70808,3.6211,3.71087,3.74143,3.70808,3.70338 +extrema-i32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.9813,71212,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.75414,2.77207,2.78026,2.72367,2.73333,2.75414 +extrema-i32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.72761,104104,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.72257,2.97785,2.72257,2.47591,2.84472,2.61447 +extrema-i32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.98594,81540,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.83091,1.83242,1.83091,1.83172,1.82626,1.83024 +extrema-i32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.23503,169680,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.33657,2.33657,2.72419,2.05254,2.34131,2.06144 +extrema-i32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.47832,101908,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.26274,2.30101,2.34428,1.24463,1.23061,1.26274 +extrema-i32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.05416,301020,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.44323,3.61815,3.18637,4.30708,3.44323,2.99451 +extrema-i32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.76638,142964,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.71234,1.74748,1.4407,1.62862,1.71234,1.76693 +extrema-i32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.98146,524540,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,4.71344,4.71344,5.61069,4.4217,4.1958,5.50827 +extrema-i32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.47876,225084,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.22097,1.23759,1.1688,1.11739,1.22097,1.23839 +extrema-i32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.78081,790660,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,6.97303,5.40454,6.97303,4.89004,7.00367,8.38284 +extrema-i32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.12598,348192,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.898904,0.962722,0.910816,0.882919,0.895498,0.898904 +sum-i64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.87089,71284,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.77038,2.75185,2.88294,2.79721,2.77038,2.72137 +sum-i64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.81832,71516,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.67541,1.68626,1.66998,1.67541,1.67656,1.67194 +sum-i64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.41509,95452,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.77134,1.73182,2.03726,1.74238,1.88921,1.77134 +sum-i64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.22561,81384,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.1268,1.14633,1.1268,1.12374,1.13079,1.12235 +sum-i64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.62602,124932,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.47581,2.64005,2.47581,2.89097,2.31939,1.71108 +sum-i64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.849439,102048,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.744637,0.759167,0.743123,0.74545,0.740711,0.744637 +sum-i64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.59449,200924,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.55585,2.63233,1.33782,1.85437,1.3039,1.55585 +sum-i64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.15011,161176,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.10074,1.08544,1.10074,1.10831,1.09346,1.28361 +sum-i64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.08889,348192,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.38384,5.5097,4.84105,5.69605,4.8983,5.38384 +sum-i64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.56997,247692,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.993263,1.20273,0.931189,1.01465,0.993263,0.842352 +sum-i64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.05133,755696,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.60908,5.75575,6.30116,8.1793,6.60908,7.15416 +sum-i64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.25708,370880,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.724293,1.41304,0.749217,0.724293,0.693685,0.667725 +extrema-i64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.3738,71032,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.09015,5.045,5.10732,5.03686,5.09015,5.26304 +extrema-i64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.18178,71260,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.01952,3.10265,3.01952,3.04974,3.01318,2.99773 +extrema-i64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.43894,104088,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.49743,3.66355,3.49743,3.17931,3.50063,3.33493 +extrema-i64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.24823,93980,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.17723,2.34969,2.27304,2.17723,2.15602,2.15382 +extrema-i64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.79783,169744,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.79897,4.0003,4.56504,2.38519,2.79897,2.4814 +extrema-i64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.57464,124288,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.29109,1.33885,1.29109,1.28042,1.30875,1.28702 +extrema-i64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.7868,300488,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.82336,4.02355,3.43439,4.63653,3.82336,3.59082 +extrema-i64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.06972,165780,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.03334,2.18041,2.09867,2.03334,1.88151,1.57936 +extrema-i64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,9.0657,575244,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,8.91074,9.24825,9.66747,8.57441,8.66291,8.91074 +extrema-i64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.96924,247784,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.2499,1.44755,1.21738,1.22868,1.2499,1.31216 +extrema-i64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,9.11157,803120,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,9.12967,9.12967,8.83384,7.58015,9.62072,14.1706 +extrema-i64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.38029,370904,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.954928,1.06755,0.957328,0.948123,0.930541,0.954928 +sum-f32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.937,71228,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.77775,2.79048,2.77111,2.76203,2.7851,2.77775 +sum-f32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.84458,71284,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.6798,1.69626,1.6798,1.67754,1.67959,1.68002 +sum-f32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.09803,95908,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.87767,1.80245,2.05198,1.85016,2.01871,1.87767 +sum-f32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.2839,81196,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.14327,1.17957,1.14222,1.14495,1.13926,1.14327 +sum-f32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.06562,126520,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.47256,1.47256,1.40705,1.58657,1.54584,1.45284 +sum-f32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.09538,102340,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.808428,0.953811,0.808428,0.809561,0.808311,0.803937 +sum-f32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.1108,212180,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,3.0653,3.01626,3.18348,3.0653,3.45475,2.71394 +sum-f32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.58673,161560,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.08441,1.4665,1.11984,0.950702,1.01654,1.08441 +sum-f32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.60359,348304,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.81083,5.81083,5.57055,7.31862,5.66732,6.53753 +sum-f32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.068,225116,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.943219,0.995222,0.943926,0.824814,0.943219,0.92049 +sum-f32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.09372,745780,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.15999,6.24001,5.22536,6.15999,5.32513,6.40455 +sum-f32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.862503,348132,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.761881,0.764289,0.781904,0.761881,0.703177,0.723051 +extrema-f32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.29616,70840,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.17719,5.17719,5.14367,5.17295,5.21798,5.19895 +extrema-f32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.18339,71448,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.01361,3.01361,2.97761,3.00998,3.14519,3.09498 +extrema-f32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.53513,103864,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.50803,3.50803,3.55205,3.26128,3.51876,3.32092 +extrema-f32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.44227,81632,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.94802,2.22282,1.95855,1.9472,1.94802,1.94641 +extrema-f32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.65165,169864,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.72763,3.00739,3.05829,2.34814,2.72763,2.48261 +extrema-f32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.57331,124904,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.26843,1.33014,1.28124,1.26396,1.26005,1.26843 +extrema-f32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.49114,300720,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,4.04377,4.06026,4.04377,4.60041,3.8142,3.39851 +extrema-f32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.79752,155096,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.04205,2.09008,2.06585,2.03743,2.04205,2.03086 +extrema-f32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.39953,530468,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.8185,5.8185,5.67183,5.96205,4.57332,5.82748 +extrema-f32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.19102,237208,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.36898,1.52377,1.3739,1.20524,1.36898,1.34927 +extrema-f32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.87895,796976,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,6.09747,5.59492,7.07396,5.15459,6.09747,8.17143 +extrema-f32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.98871,360744,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.897368,0.997214,0.867934,0.873048,0.903375,0.897368 +sum-f64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.93022,70960,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.79175,2.79401,2.8105,2.79175,2.77446,2.78018 +sum-f64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.82955,71268,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.68252,1.69222,1.68415,1.68252,1.68075,1.68203 +sum-f64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.11169,95852,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.87308,1.85657,2.15337,1.87308,2.00797,1.80837 +sum-f64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.27817,81536,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.15695,1.16368,1.15695,1.14237,1.1668,1.14956 +sum-f64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,1.45179,126656,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.47447,1.53171,1.29481,1.66701,1.31388,1.47447 +sum-f64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.846179,102108,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.760498,0.773798,0.75225,0.777849,0.755659,0.760498 +sum-f64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,1.77639,200000,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.36943,2.02087,1.88385,3.1588,2.36943,2.72247 +sum-f64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.52395,161408,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.44452,1.44452,1.46239,1.35454,1.43016,1.77104 +sum-f64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.6563,348532,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.18024,6.18024,5.36599,7.06626,5.54281,6.19652 +sum-f64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.61249,247656,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.941254,1.28477,0.975099,0.941254,0.900328,0.92951 +sum-f64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.16974,765764,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.81845,5.81845,5.60815,7.89719,5.7569,6.2862 +sum-f64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.03359,370848,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.730299,1.28277,0.753266,0.6683,0.691095,0.730299 +extrema-f64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.31407,70836,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.15537,5.15537,5.14357,5.12184,5.21763,5.2061 +extrema-f64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.27336,71288,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.08759,3.08759,3.0971,3.07173,3.06884,3.20079 +extrema-f64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.59552,104044,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.53716,3.66736,3.66265,3.24489,3.53716,3.35944 +extrema-f64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.88438,93932,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.996,2.0123,1.97998,1.996,1.9977,1.97338 +extrema-f64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.60766,169308,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.67298,2.75928,3.08629,2.43832,2.67298,2.48194 +extrema-f64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.54536,124252,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.28155,1.3202,1.28155,1.35461,1.278,1.2815 +extrema-f64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.95384,300912,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,4.18378,4.18378,4.03112,5.10731,4.33909,3.9398 +extrema-f64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.67616,166144,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.2691,2.2691,2.14593,2.49988,2.34315,2.22439 +extrema-f64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,8.85579,579204,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,8.69423,8.84633,9.02879,8.5786,8.69423,8.60861 +extrema-f64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.29208,247440,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.40104,1.45869,1.40104,1.40698,1.36075,1.35064 +extrema-f64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,6.93209,794720,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,7.93522,7.93522,8.24615,6.91533,6.82444,10.3779 +extrema-f64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.57679,370964,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.935988,1.06048,0.935988,0.937708,0.912394,0.927843 +sum-time,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.03342,103764,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,2.73568,2.80855,2.75533,2.73364,2.73568,2.73289 +sum-time,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.87622,104056,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.67089,1.69333,1.66178,1.67199,1.67089,1.66704 +sum-time,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.15667,128852,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.87344,2.19321,2.10765,1.76647,1.87344,1.71673 +sum-time,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.34512,114312,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.13,1.15044,1.13541,1.12217,1.12662,1.13 +sum-time,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,1.51846,159476,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.40421,1.54124,1.23045,1.58808,1.21609,1.40421 +sum-time,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.92859,134700,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,0.797314,0.872421,0.797913,0.797314,0.794227,0.796844 +sum-time,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.80151,232728,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,2.99744,2.99744,2.62486,3.28694,2.37073,3.09102 +sum-time,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.20847,193840,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.18101,1.17539,1.15574,1.18101,1.43878,1.47668 +sum-time,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.44694,380576,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,5.62165,6.06247,5.15752,6.50272,5.20146,5.62165 +sum-time,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.06292,257852,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,0.946718,1.0117,0.883038,0.946718,0.989274,0.920823 +sum-time,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.98859,776312,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,5.22843,5.22843,4.55856,6.65224,5.08982,5.7805 +sum-time,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.916089,380948,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,0.677662,0.734163,0.677662,0.631276,0.723872,0.677355 +extrema-time,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.25594,103508,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,3.82105,3.90262,3.82105,3.82678,3.79379,3.80891 +extrema-time,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.12522,103716,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.75617,2.81748,2.75617,2.75186,2.77177,2.74669 +extrema-time,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.04479,136600,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.66462,3.0032,2.76092,2.47984,2.66462,2.55308 +extrema-time,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.82062,114404,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.30933,3.65609,3.61415,2.30933,1.96338,1.95601 +extrema-time,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.3029,202516,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.44584,2.44584,2.72518,1.96536,2.45632,2.14028 +extrema-time,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.57987,134764,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.2203,2.3292,2.2203,2.27099,1.74925,1.22327 +extrema-time,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.17287,333244,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,3.34807,3.85478,3.34807,4.28448,3.21575,2.33555 +extrema-time,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.01632,175572,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,1.81567,1.62532,1.75563,1.81567,1.83076,1.89878 +extrema-time,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.46824,553036,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,5.00774,5.00774,5.95117,4.98168,4.55555,5.81368 +extrema-time,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.3524,257712,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,1.21062,1.15987,1.23262,1.21517,1.21062,1.20979 +extrema-time,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.27285,819248,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,6.47432,5.43791,6.92461,4.62348,6.47432,7.69696 +extrema-time,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.26016,381420,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,0.957532,0.949429,0.968058,0.950001,0.95888,0.957532 +count,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.8556,71284,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,2.68539,2.69513,2.67827,2.68539,2.68676,2.6838 +count,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.82407,71128,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.66212,1.66723,1.66764,1.66212,1.65963,1.64798 +count,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.08392,95664,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.7293,1.7293,1.95472,1.71223,1.8289,1.68496 +count,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.25438,81476,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.11279,1.11387,1.11592,1.11249,1.11279,1.11098 +count,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,1.59104,126700,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.45128,1.45954,1.2799,1.64446,1.38119,1.45128 +count,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.08845,101940,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.814687,0.850017,0.861421,0.814687,0.812903,0.810855 +count,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.89826,200656,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,2.661,3.10999,2.661,2.83955,1.62116,1.61615 +count,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.27541,143072,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.2486,1.23331,1.2486,1.18122,1.29684,1.30907 +count,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.49197,348504,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,5.03302,5.68589,5.00872,5.65626,5.03302,4.98535 +count,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.895732,225356,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.917731,0.745436,0.917731,0.840898,1.00167,1.07033 +count,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.47542,514080,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,3.35035,3.14198,4.75004,3.12175,3.35035,5.70022 +count,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.812567,347812,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.571386,0.61149,0.571386,0.598678,0.529666,0.56644 +statistics,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.07,87368,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,8.96031,8.95339,8.96031,8.95999,9.0755,8.99758 +statistics,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.92599,85560,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,4.65543,4.82249,4.65543,4.71422,4.63915,4.6201 +statistics,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,6.39504,112316,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,5.60452,5.74726,5.33603,5.18584,5.60452,5.7998 +statistics,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.40888,103848,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,3.02334,3.02334,2.98008,2.94815,3.13993,3.2207 +statistics,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.87336,165744,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,3.58863,3.85994,3.92157,3.58863,3.55773,3.50452 +statistics,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.39292,129032,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.43227,2.48575,2.47442,2.41492,2.42481,2.43227 +statistics,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,7.02099,257456,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,5.7504,6.06286,5.91904,4.7009,5.7504,4.94956 +statistics,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.79353,170136,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.00451,1.97497,1.94532,2.00451,2.07889,2.02566 +statistics,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,12.2717,581476,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,12.3497,12.3497,12.4565,12.4876,11.1388,10.5817 +statistics,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.52003,251712,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,1.74492,2.11646,1.74492,1.80293,1.70572,1.68096 +statistics,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,11.7429,919348,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,14.849,10.3831,14.849,14.0108,17.1618,15.3455 +statistics,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.44043,374952,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,1.34393,1.8436,1.35987,1.32596,1.34393,1.29919 +binary,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,22.1877,120208,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,19.0557,19.1045,19.2968,18.9699,19.0557,18.9378 +binary,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,14.9429,116388,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,12.5945,12.4872,12.5135,12.5945,13.3855,13.2425 +binary,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,14.9226,138912,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,13.3416,13.3416,13.0701,13.3648,14.3406,13.0671 +binary,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,17.6502,126148,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,9.13601,10.3794,10.3494,8.9351,9.13503,9.13601 +binary,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,73.4528,333832,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,69.2803,69.6538,69.2803,69.1794,69.3582,69.1315 +binary,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,7.5051,146292,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,5.21096,5.3389,5.21096,5.15189,5.19341,5.25614 +binary,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,38.7347,346284,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,36.6846,36.6891,36.381,36.1483,36.6846,36.7319 +binary,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.40418,186784,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,3.34235,3.61949,3.29996,3.5206,3.34235,2.97492 +binary,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,22.8368,590028,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,21.4506,23.6735,21.4036,21.1973,21.4506,21.8834 +binary,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.1355,267976,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,3.06828,3.48138,2.94632,2.88303,3.14134,3.06828 +binary,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,17.4139,975432,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,17.8475,19.0539,19.5383,17.8475,17.3059,16.2697 +binary,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.56608,392256,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.60549,3.02885,2.32059,2.60549,2.65005,2.50815 +truth,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,6.48339,71216,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,6.10125,6.10125,6.13575,6.01278,6.00053,6.22545 +truth,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.91542,71140,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,3.66675,3.76007,3.68785,3.64196,3.64557,3.66675 +truth,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.69897,103636,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,3.56343,3.78194,3.60784,3.22414,3.56343,3.35206 +truth,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.27608,93880,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,2.26301,2.30321,2.27373,2.26301,2.25653,2.25846 +truth,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.60405,169864,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,2.86427,2.86427,3.11602,2.46124,2.79261,2.95626 +truth,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.04326,124932,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.38068,1.45772,1.38068,1.44101,1.37652,1.37378 +truth,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.35845,300772,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,4.34187,4.49939,3.80025,4.97892,4.34187,3.74559 +truth,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.44802,165888,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.80245,2.06331,1.66614,1.6236,1.80245,1.80855 +truth,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,8.75881,575528,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,8.4582,8.4582,8.97999,8.26448,8.20604,8.59576 +truth,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.14872,247444,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.37689,1.44134,1.38018,1.34989,1.37689,1.37111 +truth,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,6.59733,780080,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,7.09272,7.06463,8.43708,7.09272,6.61121,9.99598 +truth,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.49982,370940,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.00154,1.07346,1.02175,1.00154,0.962531,0.984059 +product,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.40243,71012,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,6.12203,6.43706,6.12203,7.15438,4.32742,3.05447 +product,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.06025,71292,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.75696,1.8295,1.76872,1.75284,1.75084,1.75696 +product,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.11724,97488,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.86414,2.24532,1.86414,1.80135,1.80692,2.096 +product,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.29706,81276,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.11419,1.13609,1.11419,1.10926,1.11588,1.10997 +product,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,1.64008,151276,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.41243,1.26403,1.60322,1.41243,1.86678,1.25337 +product,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.4532,102092,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,0.744063,0.814421,0.744063,0.740692,0.748442,0.743549 +product,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.34846,257352,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.53155,2.53155,3.07104,1.6726,1.27782,2.94488 +product,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.61451,165736,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.22137,1.39293,1.19233,1.24166,1.21898,1.22137 +product,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.39751,491284,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,6.4375,6.34685,6.50708,6.36142,6.4375,6.67769 +product,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.73211,247596,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,0.959924,1.23881,0.948891,1.05771,0.959924,0.922268 +product,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.9685,749660,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,5.78913,5.78913,7.29188,4.78098,5.60269,7.46693 +product,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.00283,370964,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,0.730065,1.34973,0.740737,0.718545,0.730065,0.709702 +median,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,14.6937,97900,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,9.87829,12.955,12.431,9.87829,8.9371,8.39506 +median,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,10.5626,97816,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,8.72377,9.01783,8.72377,8.7086,8.69342,8.82372 +median,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.5414,107908,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,8.62141,9.09449,8.61114,8.8314,8.48827,8.62141 +median,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,8.18159,108436,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,6.83814,7.1823,6.83814,6.67734,6.86593,6.79445 +median,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,7.11485,128740,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,5.72896,6.04044,5.66313,5.91097,5.6899,5.72896 +median,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.9813,128984,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,4.82545,5.02015,4.80339,4.82545,4.77575,5.04244 +median,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,7.40288,169360,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,5.91534,6.30213,5.84539,6.05114,5.74906,5.91534 +median,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.93218,169992,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,3.24723,3.41331,3.24723,3.11969,3.56377,3.19304 +median,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,6.71281,252164,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,6.0568,6.58773,5.38729,6.2228,5.31905,6.0568 +median,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.28963,252236,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,2.46443,2.97236,2.46443,2.43692,2.49838,2.27186 +median,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,6.63415,374784,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,6.20873,7.46929,6.20873,5.78581,5.82048,6.23457 +median,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.81222,374768,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,1.86112,2.98967,1.86567,1.85645,1.81459,1.86112 +quantile,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.3724,97900,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,9.06721,9.21511,9.06721,9.13173,8.86307,8.92 +quantile,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,10.7099,98384,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,9.38608,9.38608,9.10067,10.0006,11.4791,9.08842 +quantile,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,8.77408,108352,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,7.29517,7.65838,7.29517,7.09397,7.1717,9.41937 +quantile,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,9.9228,108536,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,7.26262,8.38954,7.10681,7.26262,7.31646,6.99381 +quantile,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,7.21473,128584,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,5.78884,6.19138,5.73484,5.78884,5.74598,5.95093 +quantile,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.77405,129256,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,4.95457,5.17392,4.94313,4.94954,4.95457,5.0179 +quantile,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,7.83826,169584,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,6.29498,6.52167,6.3277,6.26823,6.29498,6.27668 +quantile,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.73815,169584,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,3.05778,3.28321,3.41327,3.04892,3.05778,3.05067 +quantile,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,7.65878,251676,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,6.06864,7.01032,5.81447,6.22226,6.06864,5.99771 +quantile,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.35571,251840,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,2.72634,3.22549,2.8279,2.54458,2.72634,2.52863 +quantile,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,7.44068,374288,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,6.07439,6.51165,5.98048,6.07439,5.99476,6.32413 +quantile,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.92083,375004,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,2.02671,3.20026,2.02671,2.00732,2.0589,1.94453 +mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,14.9062,92032,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,14.1143,14.2707,13.5617,13.9113,14.1143,14.4981 +mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,13.5079,91784,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,12.3546,13.2408,12.3546,12.1981,12.2278,12.6938 +mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,11.057,102580,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,9.45276,9.86654,9.33169,10.4915,9.36624,9.45276 +mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,10.8316,102580,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,9.36956,9.66573,9.28706,9.25829,9.36956,9.69404 +mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.0945,122420,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,7.47742,8.72509,8.17933,7.20592,7.32692,7.47742 +mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,8.58006,128952,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,5.91223,7.54112,5.85231,5.91223,5.95561,5.80813 +mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,7.99972,163416,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,6.93492,7.03241,6.97148,6.79828,6.78773,6.93492 +mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.33434,169776,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,3.6591,4.35397,3.83924,3.6591,3.60854,3.28123 +mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,8.26857,246040,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,6.93412,7.33781,6.93412,6.87969,6.78919,6.99958 +mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.78742,251588,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,3.10162,3.19348,3.10162,2.76288,3.18213,2.79539 +mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,7.60379,368872,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,6.66136,6.66136,6.88158,6.62374,6.60741,6.73627 +mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.32972,374992,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,2.2643,3.21549,2.12107,2.2643,2.22318,2.26727 +top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,17.1171,104072,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,15.044,15.9581,14.9858,15.0771,14.7537,15.044 +top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,15.5452,104080,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,15.2901,15.2901,14.1794,14.442,16.7418,16.5232 +top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,14.6924,114564,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,12.2218,12.9782,11.8517,12.2218,12.2295,11.8067 +top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,13.2831,114552,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,11.4695,12.2919,11.4695,11.2547,11.9937,11.2573 +top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,11.0059,134844,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,9.44971,10.2185,9.43747,9.44971,9.51135,9.28863 +top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,9.31282,135100,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,7.91628,7.91628,8.04576,8.09481,7.86764,7.86139 +top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.3323,175932,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,8.51149,9.57696,8.74306,8.51149,8.17515,7.68568 +top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,7.41507,176264,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,5.48736,5.48736,5.6203,5.35859,5.19069,5.79735 +top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.2422,257544,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,9.35391,9.53616,8.44847,9.70491,9.35391,9.33329 +top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,8.99083,257928,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,5.2836,7.34386,5.2836,6.01241,5.04879,5.21175 +top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.1203,380648,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,9.02756,9.73195,9.13989,8.98251,8.72479,9.02756 +top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,7.65863,381320,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,4.32188,4.32269,4.32188,5.33546,4.22438,4.22862 +first-last,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.6774,92020,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.73365,9.72001,9.73365,10.1115,9.57575,9.73966 +first-last,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,8.75653,91988,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,7.14955,7.54023,7.02019,7.30056,7.14955,7.11248 +first-last,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,11.2227,102316,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.5651,9.68282,9.94817,9.4608,9.5014,9.5651 +first-last,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.13035,102352,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,5.00115,5.04288,5.00115,5.07333,4.84317,4.77777 +first-last,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.7985,122596,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.67351,9.99117,9.50107,9.67351,9.79674,9.38679 +first-last,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.25234,128896,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,3.81232,3.99998,3.81232,3.88248,3.80182,3.6268 +first-last,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.7753,163552,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.40357,9.77686,9.3891,9.37981,9.79187,9.40357 +first-last,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.95179,169764,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,2.47088,2.63403,2.47088,2.63517,2.30841,2.21947 +first-last,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,11.9225,245788,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,10.2544,10.9814,10.686,10.2544,9.54208,9.71753 +first-last,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.31883,251656,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,2.50754,2.63539,2.60493,2.40235,2.50754,2.4437 +first-last,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.7784,368412,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.44323,10.1035,9.4362,9.47591,9.42264,9.44323 +first-last,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.93734,375028,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,1.66892,2.8782,1.66892,1.76487,1.66024,1.64703 +mixed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,12.8873,97960,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,11.4571,11.9895,11.5262,11.4571,11.167,11.1462 +mixed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,11.7995,97876,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,10.0429,10.7983,10.0545,10.0386,10.0429,9.9801 +mixed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,11.1109,108464,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,9.49329,9.82924,9.44342,9.50407,9.39641,9.49329 +mixed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,9.79907,108268,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.55293,9.05887,8.50503,8.79897,8.41508,8.55293 +mixed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.3919,128564,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.82884,9.00447,8.71718,8.92107,8.37731,8.82884 +mixed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.62543,129224,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,5.51961,5.77482,5.44296,5.51166,5.51961,6.10152 +mixed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.3794,170088,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.75482,8.9919,9.03373,8.58789,8.75482,8.68516 +mixed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.06707,169660,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,3.85682,4.49047,3.89437,3.4912,3.40258,3.85682 +mixed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.0829,251448,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,9.59381,9.68611,8.93656,9.13866,9.59381,9.62225 +mixed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.10905,251692,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,3.20344,3.93499,3.35856,3.09814,3.20344,3.13355 +mixed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.1497,374780,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.4749,8.78408,8.4749,8.25678,8.30583,8.56314 +mixed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.51339,375328,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,2.51578,3.51725,2.99119,2.27102,2.32611,2.51578 +distinct,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,66.2591,202112,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,67.8513,62.1871,68.4195,70.466,67.8513,65.3319 +distinct,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,50.252,202404,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,43.8485,44.9258,43.8485,44.4889,41.6349,43.7071 +distinct,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,23.7967,201604,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,21.8639,20.7527,21.9106,21.8639,19.8972,22.1483 +distinct,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,20.7799,151468,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,16.3903,16.1088,16.3379,16.6645,16.464,16.3903 +distinct,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,18.2618,230160,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,13.9913,14.0868,13.6736,13.9913,13.973,15.1355 +distinct,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,11.0521,200632,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,9.05558,9.38428,9.27537,9.05558,9.05151,9.04984 +distinct,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,14.5647,318308,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,11.3878,13.4087,11.913,11.2431,11.3878,11.3154 +distinct,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,7.02913,327144,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,6.6388,5.78297,5.98997,6.6388,7.62444,6.96114 +distinct,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,12.0528,576404,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,11.8296,13.9073,13.6841,11.1638,11.8296,10.9404 +distinct,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.86657,416040,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,4.59953,4.03057,4.59953,3.85295,4.96935,4.74798 +distinct,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,11.1932,865740,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,12.7345,13.0211,12.7345,11.7942,14.0979,11.4481 +distinct,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.54223,533092,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,4.11154,3.79864,4.32798,4.11154,3.92872,5.69304 +composite,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,458752,28.4447,130444,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,24.5532,25.5545,24.5532,24.6697,24.3368,24.1856 +composite,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,458752,15.8307,136856,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,12.3737,12.3737,11.3326,11.8554,12.555,13.92 +composite,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,458752,19.9874,153488,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,17.5843,17.4223,17.8099,18.4621,17.3643,17.5843 +composite,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,458752,12.6886,141212,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,9.67129,9.97512,9.5383,9.67129,9.59333,9.87162 +composite,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,458752,16.1869,198704,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,13.1602,13.1602,13.0865,12.8406,13.2499,13.6219 +composite,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,458752,8.23805,161576,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,5.99584,6.6311,6.09393,5.90171,5.99584,5.90468 +composite,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,458752,15.1309,288708,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,11.6286,12.2506,12.0222,11.6286,11.4287,11.2874 +composite,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,458752,6.7431,202696,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,3.88642,5.28849,3.97775,3.41282,3.88642,3.42872 +composite,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,458752,13.8946,383108,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,11.4113,12.6719,11.4113,11.8621,11.115,11.2504 +composite,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,458752,6.19201,284968,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,2.75768,4.23629,2.98197,2.75768,2.73691,2.68365 +composite,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,458752,15.8827,641528,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,13.4597,14.0636,15.2802,12.149,13.4597,13.0343 +composite,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,458752,8.32868,407324,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,1.98797,4.00548,2.26635,1.96912,1.91767,1.98797 +selected,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.30714,71280,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,4.98582,5.00533,5.00134,4.92106,4.98582,4.9671 +selected,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.80032,71552,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.61561,3.72197,3.61561,3.4471,3.78054,3.40851 +selected,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.44321,102320,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.36483,3.29993,3.36483,3.2101,3.85247,3.46906 +selected,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.60098,91800,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.27664,2.31814,2.28593,2.26952,2.27664,2.25809 +selected,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.26837,151544,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.2421,2.2421,2.42393,2.19346,2.64901,2.08777 +selected,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.78483,112864,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.48698,1.49311,1.46924,1.51299,1.48698,1.43808 +selected,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.5428,227244,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.24095,2.57649,2.68955,2.24059,2.13068,2.24095 +selected,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.56863,153656,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.21686,1.26533,1.25828,1.2024,1.21686,1.20186 +selected,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.65258,303024,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.51866,2.38704,2.20224,2.51866,2.68072,2.83039 +selected,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.31024,235888,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,0.90686,0.963211,0.954287,0.9024,0.90686,0.897027 +selected,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.40701,399520,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.52463,2.29277,2.43003,2.52463,2.58781,2.5829 +selected,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.28978,358152,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,0.890845,0.99111,0.937707,0.890845,0.869469,0.872511 +selected-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,166200,10.2694,102016,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,8.78676,9.70494,9.12377,8.72898,8.75761,8.78676 +selected-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,166200,8.43038,102232,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,6.79691,7.37049,6.79691,6.80562,6.74734,6.68985 +selected-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,166200,8.99262,112552,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,7.78292,8.47965,7.79619,7.74985,7.68305,7.78292 +selected-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,166200,8.04544,113080,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,6.94996,7.43494,6.94996,7.06836,6.83307,6.75281 +selected-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,166200,8.52385,133128,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,7.33744,7.87104,7.33744,7.36859,7.21128,7.28477 +selected-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,166200,6.27559,133016,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,4.95782,5.72984,4.95151,4.88112,4.95782,5.09679 +selected-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,166200,8.23644,173884,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,6.99259,7.51478,7.00962,6.99259,6.91127,6.9191 +selected-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,166200,5.93567,174180,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,4.24345,5.02129,4.29437,4.1303,4.24345,4.13979 +selected-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,166200,8.07299,255660,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,6.84877,7.51774,6.84877,6.76328,6.88498,6.66998 +selected-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,166200,4.65389,255836,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,3.46413,3.94908,3.46413,3.52771,3.39413,3.37768 +selected-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,166200,7.97204,379076,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,6.86623,7.62473,6.92763,6.78347,6.86623,6.82368 +selected-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,166200,4.5514,378904,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,3.3876,3.78701,3.44431,3.3876,3.14699,3.27106 +sparse,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,51.4365,128300,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,48.7955,49.1634,48.4996,48.7955,48.7653,48.8447 +sparse,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,50.9332,128900,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,48.1919,48.3485,47.5738,48.2274,48.1919,47.8485 +sparse,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,27.2192,136644,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,25.6334,25.8185,25.6334,25.7073,25.6236,25.4174 +sparse,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,26.2557,137424,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,24.6654,25.0916,24.7756,24.6654,24.6404,24.6553 +sparse,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,14.2391,188144,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,14.0137,14.4272,14.2093,13.7714,14.0137,13.6527 +sparse,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,14.3852,188400,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,13.4242,14.1531,13.5119,13.1712,13.4242,13.1213 +sparse,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,9.2167,319656,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,8.41367,8.60804,8.71999,8.04106,8.41367,8.14766 +sparse,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,9.17125,319488,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,7.96491,8.6797,8.7958,7.66524,7.96491,7.79114 +sparse,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,6.16413,475100,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.85785,5.73977,6.11637,5.51994,5.85785,7.20951 +sparse,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.80016,477096,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.25149,5.25149,5.9608,5.09766,5.18697,6.57785 +sparse,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.67306,766640,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.99421,5.90506,5.33968,6.88042,5.99421,6.16423 +sparse,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.28202,766712,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,7.07964,5.50482,6.32034,7.6724,7.07964,7.46585 +clustered,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,125000,4.04253,71296,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,3.90657,3.9595,3.88186,3.96491,3.90657,3.87843 +clustered,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,1.81919,71196,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.6895,1.72213,1.67135,1.6895,1.7024,1.68568 +clustered,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,125000,2.68523,95852,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,2.4402,2.4402,2.68276,2.39423,2.51774,2.37767 +clustered,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,1.49092,81276,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.36906,1.44795,1.36906,1.3479,1.36015,1.39617 +clustered,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,125000,1.68065,126560,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.77118,1.80393,1.63572,1.96508,1.60886,1.77118 +clustered,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,2.5876,117988,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.47583,1.63743,1.5823,1.47583,1.43512,1.4203 +clustered,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,125000,1.42258,200232,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.56107,1.56107,1.25272,1.85924,1.38126,1.99214 +clustered,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,2.5964,163624,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.33147,1.33147,1.16984,1.3641,1.37979,1.23297 +clustered,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,125000,3.02794,348280,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,3.66341,3.72268,2.64168,4.49803,3.00328,3.66341 +clustered,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,2.49779,245608,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.766491,0.766491,0.735916,0.789951,0.788196,0.672518 +clustered,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,125000,4.06496,541192,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,5.23627,5.21868,5.2917,5.4256,5.23627,4.7641 +clustered,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,2.90502,369164,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.647425,0.738407,0.652632,0.647425,0.574243,0.584134 +skew,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,4.2176,71216,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,4.26432,4.38031,4.14476,4.26432,4.13444,4.59832 +skew,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,1.77168,71556,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.51581,1.64639,1.53525,1.5019,1.51581,1.49192 +skew,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,2.89871,95548,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,3.03605,2.67551,3.03605,3.09616,2.69073,3.10384 +skew,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,1.49203,81292,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.05004,1.34945,1.05945,1.05004,1.04367,1.0374 +skew,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,3.4545,124964,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,3.5717,4.06416,3.10294,3.88896,2.88985,3.5717 +skew,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,1.8328,116424,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,0.777134,0.915906,0.817351,0.772175,0.777134,0.768198 +skew,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,1.94521,200616,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.99173,1.99173,1.73207,2.2895,1.76276,2.22373 +skew,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,2.11518,161376,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.00107,0.970357,1.00483,1.00107,0.996948,1.06149 +skew,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,4.73483,348280,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,5.23339,5.24925,4.78478,5.99927,3.81637,5.23339 +skew,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,2.66443,245640,2fda006d290f4d1ca6efe9f1a4b3d0e68f9072a6159bfb30e323d87e16fca0ac,0.952163,1.11363,1.03294,0.931563,0.952163,0.801994 +skew,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,16.3931,867144,2fda006d290f4d1ca6efe9f1a4b3d0e68f9072a6159bfb30e323d87e16fca0ac,15.0289,14.9146,17.1956,15.0289,17.0258,14.8534 +skew,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,2.82994,368696,2fda006d290f4d1ca6efe9f1a4b3d0e68f9072a6159bfb30e323d87e16fca0ac,0.66766,0.85046,0.700583,0.66766,0.658497,0.635572 +skew-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,11.2657,95868,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,9.79442,10.182,9.79442,9.9885,9.75816,9.77318 +skew-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,11.1047,96132,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,9.42775,9.54829,9.34192,9.42938,9.35409,9.42775 +skew-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,9.48278,106360,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.43291,8.61922,7.94816,8.43291,8.30049,8.44285 +skew-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,8.71258,106488,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,7.36349,7.43219,7.29562,7.31387,7.36349,7.41249 +skew-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,9.46837,126576,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.08383,8.15762,8.08383,8.07778,8.08814,8.0317 +skew-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,7.49421,128976,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,5.25443,5.91296,5.42806,5.21814,5.25389,5.25443 +skew-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,9.55001,167484,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.01248,8.01248,8.0608,8.07753,8.00872,7.94998 +skew-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,5.40529,170100,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,3.91945,3.98457,4.13849,3.91945,3.75127,3.77598 +skew-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,9.64263,249432,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.12543,8.12543,9.46729,8.12219,8.21167,8.06222 +skew-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,5.62606,251836,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,3.00079,3.07099,3.01626,3.00079,2.94836,2.87541 +skew-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,9.58608,372424,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.01948,8.1681,7.94957,7.99711,8.01948,8.06112 +skew-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,7.63693,374856,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,2.87342,2.97411,2.86821,2.87342,2.77464,2.90485 +float-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,17.2546,83848,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,16.984,17.0123,17.0048,16.9759,16.9693,16.984 +float-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,13.6356,91668,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,11.8105,11.8465,11.8105,11.7352,11.6714,12.1333 +float-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,12.6809,196416,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,10.1879,10.1493,9.70068,11.0372,10.1879,10.282 +float-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,13.5714,194388,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,9.81445,11.0141,10.1074,9.81445,9.54137,9.81198 +float-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,8.23468,200456,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.06818,6.35009,6.09162,6.05149,6.01739,6.06818 +float-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,10.022,233212,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.85521,6.32664,6.27494,5.85521,5.65612,5.69079 +float-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,6.00338,319460,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,4.47039,7.11225,4.94552,4.25242,4.47039,4.07499 +float-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.53505,319260,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,4.22017,5.79394,4.5598,4.22017,4.18863,4.05917 +float-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.79531,579572,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.28237,6.1021,5.28237,5.82686,4.70334,4.10144 +float-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.78268,251460,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,3.35546,3.07944,2.94772,3.44718,3.94618,3.35546 +float-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.86105,821908,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.36616,5.94558,6.37683,5.9855,6.36616,6.67794 +float-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.99373,374764,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.18478,2.28508,2.14315,2.18478,2.16719,2.25621 +float-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,20.8468,97576,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,17.4506,17.6691,17.3385,17.4506,17.4162,17.5836 +float-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,18.3168,97872,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,16.1113,16.2859,16.1113,16.0737,16.7511,15.9851 +float-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,17.56,108172,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,15.6153,15.7849,15.4878,15.6153,15.808,15.613 +float-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,14.4721,108704,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,12.7068,13.0868,12.5698,12.7068,12.6622,12.7527 +float-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,17.3184,128820,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,15.2205,15.5133,15.2205,15.159,15.2338,15.0673 +float-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,9.68062,128976,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,7.8102,8.12957,7.81859,7.76842,7.8102,7.80313 +float-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,16.9393,170032,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,14.1548,14.717,14.4683,14.1548,14.1191,14.0876 +float-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.43461,169992,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,4.97336,4.7916,4.71851,4.97336,5.25487,5.12325 +float-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,17.2911,251564,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,15.2495,15.2495,15.2306,15.161,15.615,15.319 +float-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.16432,251880,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,3.50342,3.56645,3.51657,3.39551,3.50342,3.46548 +float-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,16.8389,375124,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,15.3946,15.4684,15.5574,15.0626,15.3946,15.1482 +float-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.51458,375288,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,2.79968,2.81189,2.76676,2.79968,2.85093,2.67145 +symbol-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,1.28793,87416,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.20192,1.31293,1.27665,1.16115,1.09228,1.20192 +symbol-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,1.32932,87676,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.11563,1.31014,1.15113,1.11563,1.08444,1.08054 +symbol-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,0.658803,97680,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.535175,0.61429,0.577003,0.535175,0.527196,0.51392 +symbol-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,0.885494,97516,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.762758,0.762758,0.61527,0.579941,0.833321,0.863672 +symbol-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,0.609116,117828,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.520877,0.599433,0.480367,0.522014,0.464875,0.520877 +symbol-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,0.631533,118216,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.447363,0.505235,0.447363,0.456278,0.371256,0.311016 +symbol-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,0.493773,159464,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.364847,0.400439,0.386909,0.364847,0.352594,0.357892 +symbol-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,0.60245,159048,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.284705,0.39215,0.298257,0.278049,0.284705,0.284704 +symbol-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,0.528814,241004,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.253916,0.299072,0.253916,0.255983,0.227386,0.218522 +symbol-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,0.523397,241304,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.241693,0.356818,0.227359,0.29418,0.241693,0.229767 +symbol-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,0.590841,364520,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.267455,0.331202,0.285269,0.267455,0.230658,0.201164 +symbol-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,0.600484,364336,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.228613,0.32312,0.255222,0.228613,0.217408,0.204721 +string-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,24.1086,102260,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,24.8582,24.8587,24.8152,24.8393,24.94,24.8582 +string-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,13.6583,122420,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,12.4759,12.4758,12.4017,12.5225,12.4975,12.4759 +string-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,18.5117,204480,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,16.5369,17.1858,16.5838,16.4577,16.3964,16.5369 +string-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,9.304,139172,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,7.89615,7.89615,7.83963,8.01096,7.8834,7.92114 +string-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,10.9501,212640,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,8.38998,8.97755,8.71574,8.36379,8.38998,8.36709 +string-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,5.33728,159488,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,3.99779,4.07344,3.96737,3.99779,3.98737,4.02774 +string-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,7.28974,331988,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,7.34923,7.69628,6.90629,6.8253,8.62598,7.34923 +string-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,4.2364,200492,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,2.37275,2.47924,2.36184,2.33579,2.38713,2.37275 +string-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,6.29167,583600,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,9.08758,9.90364,6.6458,9.08758,7.07698,9.482 +string-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,3.69076,282584,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,1.85498,1.83191,1.70877,1.85498,1.88531,1.88685 +string-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,7.79372,600716,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,6.78953,6.22007,6.78953,8.44684,6.7563,9.1859 +string-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,5.13908,405652,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,1.46184,1.4952,1.54225,1.46184,1.38476,1.4092 +string-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,17.9138,122144,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.1418,16.4173,16.341,16.0222,15.8839,16.1418 +string-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,18.3007,122308,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.1742,16.1742,16.0162,16.478,16.2158,16.0344 +string-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,17.9247,132152,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,15.8768,16.166,15.7609,16.0647,15.8768,15.7775 +string-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,13.3246,139164,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,10.3696,13.0014,10.2948,10.3696,11.1644,9.94103 +string-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,17.5898,153288,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.1685,16.066,16.1522,16.1685,16.2219,16.2082 +string-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,7.29482,159640,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,5.66372,5.82924,5.64439,5.66372,5.75674,5.61409 +string-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,17.5647,194156,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.1895,16.1895,16.2164,16.1466,16.2626,16.1559 +string-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,5.73485,200472,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,4.47716,4.34412,4.34766,4.66207,4.67828,4.47716 +string-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,17.6526,276344,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.3873,16.3348,16.3873,16.4376,16.3782,16.402 +string-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,5.28202,282572,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,3.50546,3.50546,3.43036,3.54805,3.61045,3.32451 +string-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,17.6209,399836,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.3992,16.4035,16.3992,16.3741,16.5036,16.3113 +string-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,18.2273,405720,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,13.0554,26.4704,24.9632,12.8321,8.90182,13.0554 +wide-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,26.8064,126300,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,25.8716,25.8158,25.9571,26.092,25.8716,25.8581 +wide-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,27.3666,127104,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,23.3977,38.5049,27.3701,23.3671,23.1872,23.3977 +wide-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,28.3535,136620,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,26.9989,26.8385,26.9989,27.0577,27.0867,26.8819 +wide-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,16.4515,137156,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,15.0702,15.2168,14.9985,14.937,15.0702,15.3573 +wide-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,25.947,157508,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,25.3692,25.0713,25.4324,24.944,25.3692,25.4205 +wide-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,10.5362,151232,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,9.72351,9.69817,9.78445,9.63759,10.0629,9.72351 +wide-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,25.9871,198020,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,24.8693,24.7455,25.0569,25.0698,24.8693,24.7637 +wide-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,7.69959,192572,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,6.37823,6.43145,6.41067,6.37823,6.29529,6.16102 +wide-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,28.5858,280680,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,27.1455,27.1455,27.0041,26.9054,27.3767,27.9687 +wide-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.91491,274348,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,5.38196,5.21176,5.26908,5.56366,5.76009,5.38196 +wide-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,26.0338,403432,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,24.833,25.0911,24.77,25.0609,24.833,24.7497 +wide-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.60388,397340,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,4.65022,4.89588,4.98533,4.65022,4.6276,4.5258 +extrema-date,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.15692,103612,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,3.81809,3.81809,3.83815,3.75107,3.74904,3.83804 +extrema-date,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.05794,103988,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,2.72577,2.75955,2.72577,2.74477,2.70865,2.72264 +extrema-date,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.12221,137136,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,2.67211,2.92743,2.75538,2.44647,2.67211,2.54036 +extrema-date,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.02887,114072,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,1.95314,1.85051,2.00615,1.96507,1.95124,1.95314 +extrema-date,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.78463,202220,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,2.37253,2.40479,2.68104,1.92898,2.37253,2.03858 +extrema-date,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.44183,134836,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,1.74661,1.25325,1.25008,2.38408,2.31275,1.74661 +extrema-date,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.51182,333508,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,3.5386,3.85514,3.41045,4.61301,3.5386,3.07389 +extrema-date,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.08427,175952,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,1.75714,2.10021,1.7649,1.75714,1.67133,1.68471 +extrema-date,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.12062,546636,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,4.59321,4.59321,5.2148,4.4687,4.07675,5.44984 +extrema-date,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.48626,258160,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,1.28202,1.2178,1.28202,1.27575,1.28305,1.28889 +extrema-date,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.59071,821500,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,6.60827,5.32304,6.60827,4.21501,8.51169,8.7369 +extrema-date,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.23771,381320,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,0.971952,1.08132,0.999611,0.943058,0.901107,0.971952 +extrema-timestamp,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.33774,118112,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,4.24469,3.83403,3.78866,4.49992,4.24469,4.25849 +extrema-timestamp,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.50259,118644,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,2.99636,3.13318,3.02085,2.98377,2.96498,2.99636 +extrema-timestamp,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.69658,150964,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,3.07061,5.01634,5.14379,2.95746,3.07061,2.85244 +extrema-timestamp,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.31653,128860,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,1.96964,2.0128,2.18966,1.96964,1.95217,1.95654 +extrema-timestamp,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.27036,216788,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,2.37622,2.43854,2.75806,2.0696,2.37622,2.20418 +extrema-timestamp,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.10717,159492,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,1.44989,2.3921,1.4716,1.44989,1.44803,1.37132 +extrema-timestamp,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.53791,347644,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,3.47598,3.47598,3.27334,4.31667,3.55045,3.06513 +extrema-timestamp,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.29106,200540,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,2.36969,2.49989,2.3627,2.36969,2.41127,2.3554 +extrema-timestamp,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,7.96382,610136,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,8.25012,8.28211,9.38467,8.22174,8.06503,8.25012 +extrema-timestamp,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.44112,282748,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,1.36934,1.37025,1.29322,1.36368,1.37565,1.36934 +extrema-timestamp,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,6.6222,833548,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,6.64289,6.64289,7.74251,6.43422,6.17317,9.55552 +extrema-timestamp,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.69622,405512,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,1.00629,1.06446,0.970866,0.922028,1.13651,1.00629 +wide-mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,16.2128,118664,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,14.9849,14.8701,15.6246,15.135,14.8117,14.9849 +wide-mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,16.1086,118304,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,15.393,16.8264,15.393,14.8931,16.3029,14.6277 +wide-mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,17.104,128572,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,15.2818,15.0734,15.2818,14.8442,15.8747,15.5816 +wide-mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,11.212,128944,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,10.887,10.887,11.7166,10.7894,10.6246,11.2938 +wide-mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,17.3684,149428,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,14.7019,14.4213,14.9498,14.7019,15.1848,13.9063 +wide-mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,9.06012,151548,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,7.57069,7.27627,8.00032,7.83632,7.27282,7.57069 +wide-mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,15.2646,190088,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,14.9897,15.2891,15.1865,14.5683,14.597,14.9897 +wide-mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.43746,192540,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,5.05731,5.05731,5.07738,5.04922,5.27145,5.0135 +wide-mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,16.8686,272460,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,15.9337,15.1004,16.0798,15.3261,15.9674,15.9337 +wide-mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.42144,274240,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,3.92695,3.67197,3.92695,3.86633,3.95879,4.11306 +wide-mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,16.902,395444,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,15.7037,15.0385,18.8315,15.1872,16.5054,15.7037 +wide-mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.22961,397580,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,3.37304,3.32495,3.37304,3.65878,3.4759,3.34558 +wide-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,35.7342,186132,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,32.3679,35.3887,33.1501,32.3679,32.2649,32.2437 +wide-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,35.197,185976,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,32.2265,34.6461,32.9659,31.9342,32.1314,32.2265 +wide-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,35.0762,196208,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,32.6754,35.8379,32.6754,32.7733,32.2521,32.1118 +wide-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,23.0019,184228,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,19.7517,20.1343,19.7517,19.3358,19.4062,19.9327 +wide-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,34.2345,216856,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,32.2646,34.0825,32.0928,32.2646,32.4767,32.1355 +wide-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,13.5741,225448,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,11.9241,11.9241,11.7423,12.2623,12.1399,11.1841 +wide-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,34.1313,258136,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,31.1896,33.2628,32.2969,31.041,31.1512,31.1896 +wide-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,8.05092,342164,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,6.79814,7.51428,6.60253,6.79814,6.81776,6.64042 +wide-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,34.063,339756,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,31.5415,33.8259,31.8109,31.5415,31.4174,31.5028 +wide-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.99991,421916,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,5.43522,5.47083,5.08838,5.57293,5.00759,5.43522 +wide-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,34.0273,462444,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,31.1335,33.4701,31.8521,31.1052,31.1335,31.029 +wide-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.03558,503844,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,4.11873,4.05593,4.10877,4.11873,4.38209,4.62258 +extrema-ascending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.36856,71276,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,3.24853,3.20027,3.25239,3.26835,3.24002,3.24853 +extrema-ascending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.81039,71344,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,2.63505,2.64551,2.63638,2.63005,2.63505,2.62008 +extrema-ascending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.40784,104112,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,2.34938,2.41965,2.41156,2.10123,2.34938,2.06403 +extrema-ascending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.95237,81300,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,2.03362,1.93111,2.03362,2.42011,2.33297,1.93454 +extrema-ascending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.6842,170012,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,2.82645,3.04011,3.55606,2.65998,2.82645,1.94144 +extrema-ascending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.29832,102064,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,1.14128,1.15491,1.14128,1.13576,1.21489,1.13607 +extrema-ascending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.39622,300744,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,2.47483,3.84495,2.44821,3.36668,2.47483,2.18661 +extrema-ascending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.0532,143208,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,1.73392,1.88228,1.97674,1.63481,1.73392,1.66142 +extrema-ascending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.84158,526612,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,4.62157,4.62157,5.08246,4.55772,3.97723,5.63822 +extrema-ascending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.16658,225044,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,1.14602,1.09018,1.14602,1.13453,1.19,1.16579 +extrema-ascending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.37853,788748,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,6.50641,5.06861,6.68672,4.6404,6.50641,7.45416 +extrema-ascending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.00859,348128,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,0.915286,0.943585,0.930804,0.898552,0.853027,0.915286 +extrema-descending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.4257,71276,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,3.24878,3.23521,3.27217,3.26063,3.24878,3.21396 +extrema-descending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.97365,71260,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.70111,2.74928,2.70111,2.67353,2.70404,2.64923 +extrema-descending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.47662,104120,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.42602,2.61441,2.42602,2.11952,3.02664,2.1511 +extrema-descending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.82731,81808,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,1.72385,1.72385,1.72706,1.71555,1.72215,1.76218 +extrema-descending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.76106,169200,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.1981,2.85466,2.47524,1.82235,2.1981,1.99239 +extrema-descending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.18941,102136,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,1.17514,1.29819,1.29266,1.17514,1.14866,1.14931 +extrema-descending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.06577,300900,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.58927,2.58927,2.82453,3.3388,2.53442,2.55259 +extrema-descending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.04927,143084,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,1.90041,1.94451,1.77607,1.79675,2.00266,1.90041 +extrema-descending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.81476,530468,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,4.42162,4.42162,5.33163,4.12639,3.81384,5.12738 +extrema-descending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.25821,225364,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,1.34158,1.15404,1.47359,1.3856,1.32795,1.34158 +extrema-descending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.76804,792884,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,6.6306,5.43965,6.6306,4.50305,9.18433,8.06353 +extrema-descending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.14186,348104,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,0.878052,0.856128,0.878052,0.878615,0.908613,0.84217 +few-groups-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,7,5.94388,89740,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.58422,4.69747,4.57344,4.57654,4.58422,4.6002 +few-groups-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,7,3.27256,89944,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,2.06259,2.07425,2.04551,2.04271,2.06259,2.06833 +few-groups-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,7,6.00968,100008,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.44369,4.4961,4.44437,4.42446,4.42564,4.44369 +few-groups-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,7,3.29885,100016,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,1.87717,1.91881,1.90358,1.867,1.87717,1.82128 +few-groups-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,7,5.94414,120532,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.55567,4.54634,4.55567,4.53306,4.70488,4.71835 +few-groups-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,7,2.99803,118996,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,1.23304,1.78709,1.48208,1.2061,1.19754,1.23304 +few-groups-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,7,5.93984,161576,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.44186,4.46254,4.41802,4.46511,4.44186,4.42897 +few-groups-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,7,1.72231,159436,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,0.711055,0.858776,0.735081,0.711055,0.710213,0.698876 +few-groups-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,7,6.18019,243788,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.40912,4.75232,4.38569,4.49188,4.39255,4.40912 +few-groups-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,7,1.96048,241760,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,0.585207,0.696728,0.600166,0.585207,0.561486,0.565301 +few-groups-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,7,6.01457,367080,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.43963,4.44831,4.502,4.40219,4.39887,4.43963 +few-groups-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,7,2.64826,364564,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,0.524136,0.63984,0.525171,0.520738,0.524136,0.516333 +hot-top-ascending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,6.48262,87668,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.43292,5.46749,5.44298,5.4161,5.41606,5.43292 +hot-top-ascending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,6.39105,87676,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.30374,5.36107,5.29219,5.30612,5.30374,5.30369 +hot-top-ascending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,6.41472,97652,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.32106,5.3504,5.33351,5.32106,5.31588,5.31701 +hot-top-ascending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,4.39617,98012,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,3.34263,3.35037,3.33326,3.3748,3.30386,3.34263 +hot-top-ascending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,6.45316,118476,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.36024,5.36024,5.41415,5.37543,5.35467,5.31692 +hot-top-ascending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.14163,102064,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,2.05569,2.05569,2.48089,2.07087,1.8827,1.88937 +hot-top-ascending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,6.49846,159300,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.34689,5.36798,5.35681,5.33308,5.32787,5.34689 +hot-top-ascending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,1.60253,143188,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,1.27961,1.29203,1.27725,1.27961,1.27769,1.29629 +hot-top-ascending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,6.50102,241788,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.30873,5.33426,5.31652,5.30194,5.30873,5.30266 +hot-top-ascending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,0.996319,225044,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,0.770642,0.871506,0.777734,0.76844,0.770642,0.754544 +hot-top-ascending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,6.52277,363896,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.38156,5.3816,5.36836,5.35499,5.3968,5.38156 +hot-top-ascending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,0.983387,348396,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,0.726718,0.744492,0.802224,0.726718,0.6969,0.68669 +guid-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,12.1912,122404,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.4167,10.3913,10.4167,10.4361,10.386,10.7186 +guid-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,11.9214,122512,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.1571,10.1393,10.1663,10.3487,10.1237,10.1571 +guid-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,12.1628,132752,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.4106,12.5033,10.3919,10.5011,10.3945,10.4106 +guid-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,7.78616,133036,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,6.37446,6.36081,6.39574,6.38481,6.35152,6.37446 +guid-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,12.2024,153792,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.4277,10.4675,10.4424,10.4133,10.4116,10.4277 +guid-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,5.26008,153300,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,4.00091,3.98225,4.1241,3.98965,4.0369,4.00091 +guid-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,12.2225,194368,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.4225,10.4306,10.3828,10.4225,10.4314,10.3476 +guid-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,4.2104,193960,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,2.87786,2.85928,2.87786,2.97942,2.88137,2.84402 +guid-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,12.11,276668,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.4313,10.2965,10.4449,10.4091,10.5275,10.4313 +guid-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,4.31168,276144,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,2.464,2.69801,2.43942,2.464,2.45626,2.68984 +guid-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,12.2171,399596,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.4232,10.4232,10.3958,10.4392,10.4444,10.4144 +guid-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,6.5182,399148,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,2.51814,2.39389,2.45252,2.51814,2.72537,2.71789 +list-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,28.4436,102272,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.5626,27.6056,27.5626,27.636,27.5413,27.5347 +list-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,18.0228,102264,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,16.2965,16.3182,16.3567,16.244,16.2965,16.2152 +list-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,28.701,111824,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.6266,27.5849,27.7534,27.615,27.6349,27.6266 +list-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,9.87613,105864,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,9.42016,9.44067,9.39682,9.42016,9.37465,9.45267 +list-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,28.883,132744,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.8007,27.9883,27.8007,27.6902,27.6778,28.0782 +list-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,6.63537,126508,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,5.41276,5.42713,5.53939,5.3782,5.41276,5.39525 +list-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,28.5722,173764,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.5487,27.6735,27.5243,27.5487,27.578,27.497 +list-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,5.21227,167964,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,3.76403,4.44566,4.3843,3.72081,3.76403,3.69673 +list-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,28.4995,256012,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.6892,29.869,27.7878,27.6876,27.6892,27.6273 +list-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,3.49848,249928,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,2.97953,2.97953,3.0716,2.97593,2.90397,3.02504 +list-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,29.7033,379068,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,28.8316,28.9103,28.8316,28.7989,28.8712,28.6042 +list-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,3.55654,373204,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,2.73959,2.69413,2.79787,2.71917,2.82661,2.73959 +unique-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1000000,29.6957,136256,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,26.227,26.489,26.045,26.1819,26.227,31.9069 +unique-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1000000,10.2715,136864,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,6.89431,7.12514,6.9026,6.78869,6.79398,6.89431 +unique-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1000000,44.2299,202432,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,40.6758,41.0644,38.955,40.6343,40.7329,40.6758 +unique-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1000000,9.34964,147140,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,6.81446,8.09604,6.8282,6.81446,6.79518,6.79716 +unique-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1000000,24.0402,218568,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,21.7845,23.5314,21.7845,21.6237,22.2397,21.6083 +unique-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1000000,6.63805,167776,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,4.23984,4.80888,4.22783,4.28515,4.18095,4.23984 +unique-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1000000,15.7253,351736,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,13.0868,14.4996,13.5947,13.0704,12.8153,13.0868 +unique-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1000000,6.43324,208712,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,3.1277,4.01091,3.21773,3.1277,3.1126,2.85507 +unique-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1000000,11.6182,595496,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,10.1077,10.4489,9.81058,10.1203,10.1077,9.95489 +unique-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1000000,5.99508,290204,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,1.92042,3.49389,1.966,1.8938,1.89753,1.92042 +unique-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1000000,14.9883,942152,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,15.7681,12.6767,15.7681,17.1956,13.4543,17.589 +unique-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1000000,11.3444,413740,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,1.6321,4.8083,1.64395,1.59564,1.6321,1.57156 +nonnull,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.75811,71196,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,4.68292,4.69639,4.63486,4.66965,4.68487,4.68292 +nonnull,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.51679,71256,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,3.19578,3.25428,3.19559,3.2061,3.19205,3.19578 +nonnull,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.06495,97740,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.89208,2.98086,2.66807,2.80426,3.1169,2.89208 +nonnull,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.05788,95644,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,1.97992,2.7772,1.90526,2.00612,1.97992,1.96401 +nonnull,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.43359,150952,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,3.20365,2.86972,3.43361,3.20365,3.81886,2.74784 +nonnull,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.39501,120436,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,1.3081,2.26324,1.3081,1.3465,1.29475,1.2843 +nonnull,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.06224,258224,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,3.32978,3.32978,3.85982,3.16183,2.79784,3.94372 +nonnull,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.13808,165504,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.04219,2.04219,1.88258,1.95012,2.09599,2.13818 +nonnull,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,8.54358,575268,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,8.56328,8.33467,8.56328,8.66145,8.38703,8.6411 +nonnull,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.85727,247880,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,1.25631,1.46399,1.24808,1.25631,1.26065,1.23604 +nonnull,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,6.26275,747772,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,8.19573,7.52207,8.19573,7.53707,9.22197,9.05776 +nonnull,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.18873,371136,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,0.879086,1.42705,1.00248,0.875515,0.824587,0.879086 +hot-min-ascending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,2.0162,71188,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.89923,1.89798,1.89594,1.9203,1.91603,1.89923 +hot-min-ascending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,1.85838,71288,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.78773,1.78961,1.78773,1.78604,1.7933,1.78663 +hot-min-ascending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,1.50535,98204,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.64892,1.93286,1.72342,1.35494,1.64892,1.35061 +hot-min-ascending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,1.10308,81780,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.994686,1.18779,0.995858,0.994686,0.97431,0.985408 +hot-min-ascending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,1.22366,151264,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.05602,0.880723,1.25494,1.05602,1.5185,0.869136 +hot-min-ascending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,0.795058,102108,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.791202,0.847925,0.802817,0.761402,0.791202,0.562607 +hot-min-ascending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,1.58081,257428,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.76152,1.72517,2.19368,1.76152,1.37761,2.45598 +hot-min-ascending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,0.787264,143344,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.633649,0.585438,0.612934,0.64851,0.633649,0.670354 +hot-min-ascending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,7.73735,546540,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,9.14992,8.94202,9.33628,8.28456,10.0929,9.14992 +hot-min-ascending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,0.659868,225380,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.668133,0.650011,0.904835,0.668133,0.709572,0.667972 +hot-min-ascending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,8.16247,890748,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,12.8767,13.1873,12.8767,9.83281,13.4846,12.1938 +hot-min-ascending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,1.10179,348064,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.838542,0.789738,0.85636,0.829995,0.9721,0.838542 +hot-min-descending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,2.10358,71004,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.89569,1.89569,1.89503,1.90802,1.8971,1.89427 +hot-min-descending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,1.28819,71036,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.18551,1.19383,1.18468,1.18551,1.18543,1.18794 +hot-min-descending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,1.52897,97848,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.39337,1.52645,1.23572,1.32297,1.61131,1.39337 +hot-min-descending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,0.938629,81544,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.771648,0.780637,0.773712,0.770228,0.771648,0.769969 +hot-min-descending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,1.33665,151288,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.08599,0.884509,1.26433,1.08599,1.59658,0.899874 +hot-min-descending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,1.23835,102112,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.06404,1.11844,1.11702,1.06404,1.04507,1.03786 +hot-min-descending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,1.36059,257428,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.25509,1.2216,1.50045,1.25456,1.25509,2.3982 +hot-min-descending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,1.96361,161080,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.811252,0.841169,0.843199,0.811252,0.800802,0.802099 +hot-min-descending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,7.83943,542760,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,8.61445,8.18797,9.59729,7.49049,9.4569,8.61445 +hot-min-descending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,1.22691,224968,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.13767,1.18983,1.13767,1.13703,1.16158,1.10935 +hot-min-descending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,8.32896,872784,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,10.0381,9.21204,10.6605,10.0381,11.1187,9.45058 +hot-min-descending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,0.896045,348436,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.783659,0.815456,0.786995,0.783659,0.722985,0.724635 +all-null-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,4.6828,71276,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,4.55556,4.55621,4.56372,4.55556,4.54657,4.53759 +all-null-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,3.34507,71216,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,3.26973,3.27071,3.26761,3.25914,3.26973,3.30175 +all-null-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,2.53785,81548,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,2.49919,2.49853,2.51489,2.49919,2.4551,2.64637 +all-null-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,1.7187,81724,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,1.64614,1.64945,1.64614,1.64436,1.64629,1.64388 +all-null-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,1.50898,102076,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,1.4333,1.4333,1.42853,1.42998,1.52628,1.83168 +all-null-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,1.18321,102144,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.840595,1.05737,0.840028,0.840595,0.838072,0.843549 +all-null-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,1.58456,142876,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,1.4634,1.47564,1.4634,1.48467,1.4523,1.45303 +all-null-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,0.627092,142988,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.447994,0.601428,0.447835,0.445413,0.447994,0.448283 +all-null-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,1.17061,224864,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,1.0036,1.02441,1.02389,1.0036,0.994614,0.986501 +all-null-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,0.488689,225000,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.423322,0.439882,0.421007,0.425253,0.423322,0.417604 +all-null-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,1.07188,348380,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.86281,0.914931,0.86281,0.859338,0.903892,0.858384 +all-null-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,0.47369,348652,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.301478,0.32593,0.307594,0.301478,0.292598,0.301462 +distinct-skew,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,46.7975,200340,65c9530f85952e51e2e328375168e406e8f1b73d7aa2d83845ac77496ca9365b,43.1594,43.8148,43.9945,43.1594,43.0064,43.1065 +distinct-skew,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,36.3665,200616,65c9530f85952e51e2e328375168e406e8f1b73d7aa2d83845ac77496ca9365b,32.3478,32.3478,32.3464,30.0823,32.6055,32.6043 +distinct-skew,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,23.5966,277500,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,17.2965,17.2965,17.3342,16.377,17.7418,17.0983 +distinct-skew,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,13.3343,149540,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,10.6997,11.2129,10.954,10.6997,10.6272,10.6781 +distinct-skew,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,18.1881,324992,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,13.4373,13.4373,14.6036,13.1941,13.0278,14.1556 +distinct-skew,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,7.26155,213236,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,5.9741,5.97603,5.92593,6.12046,5.85422,5.9741 +distinct-skew,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,14.4501,447672,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,12.7995,14.9762,11.848,12.7995,13.6653,11.4578 +distinct-skew,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,6.49705,281152,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,4.53461,5.43757,5.1766,4.53461,3.99155,4.38712 +distinct-skew,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,15.1141,693424,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,13.7512,14.3983,17.9478,13.6227,13.7512,13.2679 +distinct-skew,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,3.49275,327556,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,2.98794,2.98794,2.94632,3.22518,2.97224,3.35685 +distinct-skew,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65537,14.8426,977208,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,18.8867,13.2923,14.8802,18.8867,30.3993,25.3579 +distinct-skew,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65537,3.50959,446880,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,2.7546,2.7546,2.63555,2.71939,3.01507,3.64287 +distinct-correlated,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,19.1869,122196,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,16.6562,16.8882,16.6562,16.559,16.5905,17.0334 +distinct-correlated,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,17.7174,122772,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,14.7172,14.8486,14.6666,14.7172,14.5835,14.7383 +distinct-correlated,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,14.0448,187232,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,11.92,13.0867,11.067,11.92,11.1796,12.5895 +distinct-correlated,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,10.5848,120648,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,7.00951,7.00951,6.91572,7.15257,6.97002,7.03604 +distinct-correlated,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,11.3484,195320,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,9.29031,10.4897,9.16476,9.29722,9.29031,9.04861 +distinct-correlated,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.93708,139352,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,3.91018,3.89787,3.91018,4.09971,3.85078,4.00176 +distinct-correlated,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,10.8823,318084,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,8.54343,10.7153,8.96223,8.54343,8.31718,8.43353 +distinct-correlated,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.57566,173328,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,2.32199,2.40032,2.29777,2.32199,2.34681,2.29735 +distinct-correlated,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,9.85951,576700,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,9.91688,11.2277,9.62982,11.0322,9.56824,9.91688 +distinct-correlated,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.98137,253752,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,2.17917,2.19326,2.17917,2.12559,2.19808,2.13686 +distinct-correlated,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,9.28634,699596,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,10.1387,10.1387,9.2087,11.6738,8.95669,13.1927 +distinct-correlated,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.45239,379160,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,2.12879,2.72081,2.12879,2.14448,2.05708,2.09437 +symbol-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,39.7466,98104,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,38.8601,38.8601,38.9741,38.7953,38.8966,38.6137 +symbol-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,34.3054,98316,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,32.7625,32.7816,33.2825,32.7625,32.5822,32.2607 +symbol-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,39.429,108200,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.1072,38.861,38.9458,39.2969,39.1072,39.3622 +symbol-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,23.6714,108352,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,18.8869,19.0903,18.8781,18.8869,19.0577,18.7976 +symbol-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,39.7666,128732,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,40.0674,40.5783,40.0674,39.6914,40.2155,38.7342 +symbol-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,11.2045,128972,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,10.8944,10.9175,10.8944,10.9715,10.8541,10.8742 +symbol-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,41.3553,169776,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.6642,39.6642,39.5133,40.1441,39.2968,39.7173 +symbol-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,8.7357,170224,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,6.55083,7.15473,6.69539,6.55083,6.06477,6.29061 +symbol-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,40.9141,251756,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,40.1824,39.8763,40.0323,40.4518,40.1824,41.0207 +symbol-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.57566,251752,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,4.60402,4.60402,4.76975,5.07904,4.37431,4.36427 +symbol-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,40.1495,374312,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.5485,39.5144,39.5485,38.842,39.6635,39.6222 +symbol-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.52136,375016,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,3.39421,3.55011,3.47783,3.39421,3.38971,3.3095 +symbol-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,44.633,116824,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,44.3386,45.1568,43.941,44.4455,44.1299,44.3386 +symbol-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,41.0897,116604,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,39.8809,40.431,39.7131,39.8809,39.3742,40.5416 +symbol-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,45.1952,126384,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,43.9217,45.3892,43.9217,43.8077,43.8401,45.6787 +symbol-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,23.9991,137016,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,23.3436,23.0333,23.3436,23.5181,23.1592,23.4672 +symbol-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,44.7031,147256,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,43.6642,45.0928,43.6553,43.6642,43.8093,43.6472 +symbol-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,13.9146,157632,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,13.5229,13.3521,13.5315,13.7789,13.5229,13.5094 +symbol-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,44.5224,188444,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,43.2701,45.0251,43.6772,43.243,43.2701,43.2041 +symbol-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,9.04237,183900,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,7.38323,8.10281,7.00896,7.71376,7.38323,6.93533 +symbol-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,46.9351,270216,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,43.6629,47.4045,46.2604,43.465,43.4823,43.6629 +symbol-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.85105,258092,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,5.30037,5.26436,5.33946,5.55254,5.30037,5.15998 +symbol-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,44.7094,393264,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,43.7177,45.0376,44.3924,43.485,43.7138,43.7177 +symbol-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.32198,374768,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,4.04649,3.90673,3.89084,4.1745,4.04649,4.09188 +distinct-string-values,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,30.8713,122216,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,22.7364,23.4156,23.333,22.7364,22.1992,22.6667 +distinct-string-values,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,16.8285,139436,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,15.4492,15.3721,15.4492,15.6838,15.7873,15.3742 +distinct-string-values,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,11.2418,159632,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,9.57085,9.57085,9.54093,9.62332,9.48749,9.64596 +distinct-string-values,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,7.07099,200764,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,5.66938,5.84139,5.73963,5.66938,5.41872,5.40679 +distinct-string-values,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.82709,282288,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,4.75964,4.75964,4.74465,4.64801,4.86795,5.36242 +distinct-string-values,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.77448,405232,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,3.81936,3.89633,3.67363,3.69625,3.85905,3.81936 +distinct-string-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,42.376,118320,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,40.8188,40.9049,40.8188,40.7837,40.7339,40.8472 +distinct-string-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,17.853,122496,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,16.8017,16.7892,16.6852,16.8176,16.8017,16.9493 +distinct-string-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,25.6576,200660,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,23.0613,23.4854,23.1299,22.9953,23.0063,23.0613 +distinct-string-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,10.8763,139104,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,9.15362,9.15362,9.10712,9.33269,9.17324,9.13956 +distinct-string-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,13.5964,211200,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,12.0014,12.1146,11.9583,12.0255,12.0014,11.969 +distinct-string-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,5.98426,159304,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,4.70674,4.79697,4.70674,4.71578,4.69021,4.6989 +distinct-string-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,10.3224,350092,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,7.7147,9.00034,8.04474,7.7147,7.59957,7.71443 +distinct-string-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,4.31926,200408,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,2.88225,2.92433,2.88225,2.86125,2.83374,2.97287 +distinct-string-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,8.74936,588392,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,8.73943,8.14178,7.62297,10.3225,8.73943,9.44202 +distinct-string-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,3.86207,282496,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,2.08663,2.25634,2.23843,1.96943,2.07981,2.08663 +distinct-string-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,8.60506,670096,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,7.95611,7.48319,7.95611,8.97775,6.63364,8.77945 +distinct-string-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,4.91473,405764,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,2.10601,2.21387,1.99258,2.10601,2.54409,2.10435 +mixed-distinct-string-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,45.1501,118136,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,44.8765,45.0261,44.8765,44.836,44.7875,44.9242 +mixed-distinct-string-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,34.4893,128572,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,32.8194,32.8139,32.8085,33.169,32.9581,32.8194 +mixed-distinct-string-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,28.3756,208540,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,26.4145,26.6796,26.9842,26.1468,26.4145,26.3161 +mixed-distinct-string-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,18.2224,143256,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,16.4944,16.7966,16.5833,16.4567,16.4944,16.3982 +mixed-distinct-string-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,15.8224,225244,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,13.8791,13.9784,13.83,13.8918,13.8717,13.8791 +mixed-distinct-string-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,10.6432,172240,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,9.11298,9.11298,9.49499,9.09834,9.06933,9.11781 +mixed-distinct-string-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,13.3222,348068,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,11.4747,11.4747,12.3032,9.57691,9.40773,12.3491 +mixed-distinct-string-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,8.77116,228900,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,6.5898,6.77825,5.88507,6.043,7.31587,6.5898 +mixed-distinct-string-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,10.7515,573532,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,10.7199,10.7199,9.55147,11.8863,11.2508,9.82592 +mixed-distinct-string-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,6.50908,323280,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,4.52784,4.24627,4.45334,4.52784,4.55067,5.12527 +mixed-distinct-string-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,9.66644,671664,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,9.63127,10.1047,10.5948,9.50055,9.59363,9.63127 +mixed-distinct-string-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,7.26533,475172,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,4.06717,3.76886,4.06717,4.48377,4.15477,3.72774 +distinct-guid-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,33.8955,122244,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,32.5114,32.4188,32.5114,32.7285,32.5098,33.0675 +distinct-guid-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,10.3539,116272,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,9.02441,9.04835,9.00118,9.02441,8.99759,9.12626 +distinct-guid-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,29.961,136592,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.5798,28.7301,28.7685,28.4914,28.5798,28.4708 +distinct-guid-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,6.88077,132780,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,5.56471,5.66491,5.5846,5.56471,5.54037,5.5384 +distinct-guid-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,29.6497,165104,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.1615,28.0894,28.3231,28.1615,28.1539,28.1989 +distinct-guid-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,4.72891,153356,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,2.91397,2.86558,2.90422,2.9774,2.91934,2.91397 +distinct-guid-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,29.4884,214808,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.0167,28.4103,28.2175,28.0167,27.9149,27.8909 +distinct-guid-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,3.10369,194328,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,1.82024,1.96388,1.8659,1.82024,1.79693,1.81419 +distinct-guid-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,29.7032,325180,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.5312,28.5312,27.9907,28.7345,28.2717,29.1056 +distinct-guid-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,2.82222,276072,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,1.33679,1.46563,1.3886,1.31265,1.25356,1.33679 +distinct-guid-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,30.1909,472796,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.4461,28.6776,28.3117,28.4461,28.8387,28.1263 +distinct-guid-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,4.75,399424,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,1.58194,1.81831,1.66114,1.58194,1.43913,1.51867 +mixed-distinct-guid-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,38.3147,122520,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,36.755,36.755,37.2943,36.533,36.8249,36.6334 +mixed-distinct-guid-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,38.6767,122208,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,37.1415,37.2354,37.3008,37.1415,37.0333,36.9553 +mixed-distinct-guid-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,34.5185,136736,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,32.3997,32.3997,31.9386,33.044,32.5393,32.1868 +mixed-distinct-guid-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,34.0138,136800,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,32.3837,32.3924,32.6001,32.3837,32.3074,32.2498 +mixed-distinct-guid-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,32.7646,165312,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.6305,31.9054,31.4963,31.3711,31.7061,31.6305 +mixed-distinct-guid-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,9.55148,196572,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,7.6089,7.14954,7.66155,8.40951,7.6089,7.37052 +mixed-distinct-guid-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,34.6114,214556,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.6049,31.6049,31.5087,31.4792,31.7202,32.1724 +mixed-distinct-guid-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,7.50016,237652,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,6.25889,6.47982,7.08005,6.1537,6.15184,6.25889 +mixed-distinct-guid-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,33.3007,317264,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.4391,31.0105,31.6077,31.4393,31.3729,31.4391 +mixed-distinct-guid-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,7.60041,352480,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,6.51012,6.51012,6.47034,6.6578,6.41722,6.67816 +mixed-distinct-guid-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,33.428,448780,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.385,31.5176,31.385,31.0861,31.1497,35.7837 +mixed-distinct-guid-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,8.53604,495848,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,6.3326,6.58484,6.3326,6.04157,6.65775,6.10152 +distinct-list-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,15.0125,95940,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,14.3766,14.3946,14.3363,14.3066,14.3766,14.555 +distinct-list-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,8.5087,106028,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,8.21029,8.16587,8.34826,8.21994,8.08977,8.21029 +distinct-list-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,4.54397,126708,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,4.2516,4.32791,4.2516,4.24468,4.24047,4.25216 +distinct-list-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,2.88339,167316,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,2.27311,2.39894,2.34016,2.27055,2.26557,2.27311 +distinct-list-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,2.02061,249812,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,1.87999,1.8188,1.87999,1.92496,1.89317,1.72619 +distinct-list-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,2.22404,372868,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,1.74418,1.7391,1.82788,1.74418,1.76621,1.73974 +mixed-distinct-list-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,39.1373,97772,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,38.6764,38.8147,38.5265,38.6764,38.5567,38.7985 +mixed-distinct-list-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,34.5949,110240,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,34.2652,34.4858,34.2652,33.8925,33.7245,34.4422 +mixed-distinct-list-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,11.3894,169988,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,9.19103,9.96493,9.27707,9.19103,8.44887,8.60559 +mixed-distinct-list-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,7.83904,214816,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,6.99476,8.19727,7.89962,6.95375,6.93646,6.99476 +mixed-distinct-list-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,7.41969,325436,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,7.2114,7.51099,7.2114,6.76906,7.35811,6.90826 +mixed-distinct-list-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,6.92429,454656,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,6.67453,6.77404,6.67453,6.28163,6.70164,6.4753 +mixed-distinct-string-values,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,27.11,118696,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,25.8163,25.8036,25.5723,25.8163,25.9968,26.6056 +mixed-distinct-string-values,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,68.2011,256660,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,43.215,46.4167,41.3472,43.712,43.215,40.6045 +mixed-distinct-string-values,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,60.1565,274992,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,35.7075,35.388,35.7075,35.0864,38.7015,37.6258 +mixed-distinct-string-values,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,56.7355,324408,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,32.5605,32.5605,34.8279,29.6316,33.9953,31.6911 +mixed-distinct-string-values,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,57.1036,408296,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,31.851,31.2992,32.7833,31.4307,31.851,32.6857 +mixed-distinct-string-values,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,57.3275,531452,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,31.1633,30.3879,31.1591,31.1633,31.939,49.8036 +hot-first-last,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.07106,97600,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.39774,6.41625,6.39092,6.42232,6.38666,6.39774 +hot-first-last,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,4.89523,97912,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,4.10858,4.10858,4.12463,4.11501,4.08435,4.07767 +hot-first-last,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.07491,108436,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.39604,6.38018,6.39604,6.39633,6.3611,6.41118 +hot-first-last,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,3.39263,107924,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,2.61303,2.60305,2.60362,2.67881,2.70029,2.61303 +hot-first-last,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.08247,128492,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.38603,6.3898,6.38241,6.40496,6.37645,6.38603 +hot-first-last,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,1.92987,120516,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,1.55441,1.55642,1.55323,1.66245,1.55441,1.54048 +hot-first-last,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.13169,169524,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.36792,6.43336,6.37617,6.36792,6.33814,6.35294 +hot-first-last,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,1.37468,161504,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,0.952553,0.959729,0.952553,0.953995,0.931745,0.940095 +hot-first-last,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.11044,251300,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.38843,6.40237,6.6307,6.35348,6.36831,6.38843 +hot-first-last,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,1.3866,243548,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,0.688426,0.689752,0.641659,0.704002,0.643642,0.688426 +hot-first-last,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.09356,374724,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.39356,6.40232,6.40578,6.38144,6.37287,6.39356 +hot-first-last,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,1.86109,366564,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,0.571058,0.621013,0.584178,0.571058,0.529152,0.530763 +hot-symbol-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,35.51,97840,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,35.9632,35.9632,35.7,37.058,37.0192,35.2077 +hot-symbol-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,32.784,98164,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,34.846,32.3957,34.846,34.8679,35.6716,33.9472 +hot-symbol-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,36.2219,108184,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,35.3792,35.2985,35.2322,35.5689,36.218,35.3792 +hot-symbol-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,18.1992,108176,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,18.1369,17.6246,17.5444,18.1369,18.3404,18.1951 +hot-symbol-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,37.3713,128124,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,36.7929,36.1574,37.5898,36.261,36.7929,37.5894 +hot-symbol-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,9.02556,120552,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,9.21701,9.21701,9.15483,9.35138,9.50826,9.14302 +hot-symbol-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,37.0574,169772,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,37.7483,37.7483,37.8528,37.923,36.9366,37.4829 +hot-symbol-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,6.48382,161336,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,4.83967,5.04522,5.17803,4.77764,4.83967,4.81059 +hot-symbol-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,35.4158,251512,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,36.9171,37.8022,37.2955,36.9171,36.372,36.3726 +hot-symbol-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,3.38306,243040,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,3.24176,3.24835,3.2039,3.30187,3.23553,3.24176 +hot-symbol-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,35.8858,374784,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,36.0372,36.3378,35.8111,35.3978,36.0372,36.8737 +hot-symbol-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,3.439,366564,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,2.39432,2.4064,2.37669,2.39109,2.39432,2.5817 +hot-wide-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,20.8993,117848,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.6449,19.5809,19.8154,20.2637,19.2861,19.6449 +hot-wide-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,23.7262,118380,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.9433,19.6612,20.0301,19.855,19.9486,19.9433 +hot-wide-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,20.6997,128600,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.5103,19.5103,19.3337,19.7434,19.1493,19.7666 +hot-wide-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,12.2725,128980,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,11.3624,11.3547,11.3624,11.0738,11.417,11.5484 +hot-wide-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,21.0214,149180,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.7847,20.0691,19.7847,19.6691,19.7144,19.9438 +hot-wide-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,5.80419,133052,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,5.83435,5.79693,5.83435,5.8617,5.83533,5.76693 +hot-wide-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,20.7487,190096,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.7788,19.7788,20.04,20.4764,19.6584,19.6365 +hot-wide-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,3.86621,173604,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,3.62027,3.71884,3.70313,3.62027,3.53731,3.28828 +hot-wide-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,20.7716,272468,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,20.1121,19.7742,20.1121,20.3751,19.821,20.5146 +hot-wide-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.37127,255772,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,2.19736,2.17984,2.19736,2.18102,2.52338,2.43689 +hot-wide-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,20.6251,395520,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.9948,20.2303,19.631,19.9948,20.0402,19.87 +hot-wide-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.07719,378632,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,1.62771,1.67367,1.62452,1.62771,1.61963,1.65214 +hot-mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,29.3913,171484,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.4402,11.638,11.4402,11.4883,11.398,11.2875 +hot-mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,29.4346,171492,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.534,11.5688,11.5313,11.568,11.534,11.5286 +hot-mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,29.1384,181688,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.3437,11.3569,11.3514,11.3202,11.2793,11.3437 +hot-mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,6.94512,99992,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,5.21663,5.33135,5.20123,5.17881,5.21663,5.23724 +hot-mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,29.0294,202108,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.5566,11.4533,11.6127,11.5566,11.6043,11.4169 +hot-mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,3.96442,118744,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,2.80806,2.80806,2.81626,2.79584,2.80372,2.81356 +hot-mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,29.5426,243028,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.2912,11.4903,11.2912,11.3293,11.2733,11.2354 +hot-mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.95134,159572,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,1.8088,1.83762,1.83312,1.80178,1.80582,1.8088 +hot-mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,29.7256,324904,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.8338,11.6448,11.5945,13.2601,13.0629,11.8338 +hot-mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.86293,241140,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,1.12556,1.14958,1.11779,1.15018,1.12556,1.12509 +hot-mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,29.4175,448592,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.3158,11.3931,11.3141,11.359,11.3158,11.2435 +hot-mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,3.89075,364708,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,1.07932,1.19253,1.07932,1.08029,1.04406,1.05227 +hot-wide-mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,32.4109,199904,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4016,14.6192,14.3566,14.348,14.4016,14.6116 +hot-wide-mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,32.5927,200208,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.5299,14.5782,14.5547,14.5299,14.4774,14.4408 +hot-wide-mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,32.7914,210192,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.3988,14.3749,14.3988,17.9554,16.7423,14.3572 +hot-wide-mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,8.77427,128680,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,7.57849,7.66926,7.52359,7.52212,7.57849,7.6933 +hot-wide-mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,32.4597,230700,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4663,14.5245,14.3408,14.4663,14.4758,14.3955 +hot-wide-mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,4.00538,132980,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,3.94496,3.98657,3.9676,3.92069,3.94496,3.93277 +hot-wide-mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,32.1898,271704,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4306,14.4144,14.5169,14.4306,14.3269,14.5124 +hot-wide-mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.89496,173624,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,2.12409,2.72613,2.12409,2.1052,2.52018,2.00472 +hot-wide-mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,32.627,353692,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4634,14.6417,14.4634,14.4594,14.4987,14.3719 +hot-wide-mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,1.85115,255700,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,3.26008,3.70022,3.56656,3.26008,3.1408,2.96272 +hot-wide-mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,33.004,477232,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4772,14.6679,14.3366,14.4139,14.6189,14.4772 +hot-wide-mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,1.69013,378912,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,1.26917,1.33334,1.30471,1.26917,1.25904,1.26029 +hot-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,6.96637,89632,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.85437,5.85121,5.93891,5.78588,5.85437,6.08712 +hot-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,7.20019,89804,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.90186,5.90186,6.12291,5.87375,5.90147,6.02515 +hot-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,6.96516,99996,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.83424,5.83424,5.84409,5.81104,5.82098,5.88307 +hot-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,4.96085,100008,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,4.10061,5.54685,4.80094,4.10061,3.1421,3.40108 +hot-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,6.95758,120716,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.83603,5.86076,5.8282,5.89942,5.83603,5.82109 +hot-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.85163,118784,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,1.70922,1.7397,1.70922,1.70467,1.71125,1.7023 +hot-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.00591,161276,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.88358,5.94751,5.88358,5.90855,5.88244,5.86962 +hot-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.37434,159532,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,1.08995,1.14111,1.10138,1.08995,1.07967,1.02965 +hot-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,6.98184,243740,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.84733,5.84791,5.83957,5.84733,5.84557,5.85503 +hot-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.52383,241552,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,0.742781,0.79781,0.749356,0.736187,0.733581,0.742781 +hot-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.11187,366644,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.82058,5.84337,5.86607,5.79269,5.82058,5.80989 +hot-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,3.8492,364792,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,0.613136,0.758457,0.614191,0.583983,0.582412,0.613136 +hot-wide-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,20.7506,118412,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.8942,19.901,19.8942,19.7246,19.8238,19.9737 +hot-wide-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,20.0408,118408,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.0285,18.8092,19.0285,19.0449,19.1615,18.7154 +hot-wide-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,20.5804,128692,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.4103,19.3083,19.0818,19.7365,19.4103,19.8136 +hot-wide-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,13.485,128908,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,12.6374,12.5727,12.7329,12.5826,12.9409,12.6374 +hot-wide-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,21.1044,149408,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.8146,18.8172,19.8539,19.8146,19.3921,20.0144 +hot-wide-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,7.51752,132456,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,6.78126,6.76114,6.78126,6.84279,7.04119,6.69577 +hot-wide-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,20.3459,190512,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.1342,19.1342,18.6069,20.1959,19.4999,18.9268 +hot-wide-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,4.38395,173844,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,4.20675,4.4244,4.37161,4.20675,4.12381,4.20628 +hot-wide-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,20.9398,272500,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.8103,19.6117,20.0763,19.8318,19.8103,19.7762 +hot-wide-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.83021,256092,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,2.47724,2.47724,2.54643,2.38477,2.44461,2.8142 +hot-wide-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,19.6909,395508,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.1067,18.4,18.9826,19.1067,19.24,19.1567 +hot-wide-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.43237,379092,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,1.95261,2.00632,1.95261,1.98056,1.90694,1.92574 +hot-symbol-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,35.7998,97384,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,35.769,35.9627,35.6465,37.3017,35.769,35.1316 +hot-symbol-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,34.0463,97932,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,33.8775,33.5009,33.9736,34.11,33.417,33.8775 +hot-symbol-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,36.1349,107980,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,34.9645,35.5437,34.9645,34.9038,34.8574,35.0326 +hot-symbol-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,21.0883,108332,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,21.075,21.075,20.707,21.2908,21.2867,20.9857 +hot-symbol-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,35.2368,128348,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,34.3895,34.7753,34.3009,34.5341,34.0015,34.3895 +hot-symbol-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,10.7044,120836,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,10.8506,10.846,10.9337,10.7481,11.0532,10.8506 +hot-symbol-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,36.5853,169772,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,36.3268,36.3268,35.3673,36.3332,35.5672,36.5469 +hot-symbol-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,6.89955,161288,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,6.29204,6.66956,6.37997,6.29204,6.24696,6.04361 +hot-symbol-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,35.6344,251040,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,34.9381,35.3196,34.9381,35.0581,34.8415,34.5188 +hot-symbol-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,3.70493,243524,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,3.2646,3.51238,3.26922,3.24333,3.2646,3.2599 +hot-symbol-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,35.0857,374520,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,35.2068,35.4826,35.0614,34.833,35.2068,35.4363 +hot-symbol-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.71892,366344,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,2.33323,2.43773,2.33323,2.28122,2.32078,2.55443 +hot-max-k,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.33929,89436,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.20819,6.26417,6.20857,6.20819,6.18595,6.18363 +hot-max-k,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,7.44675,89712,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.17833,6.15557,6.17069,6.22297,6.17833,6.21601 +hot-max-k,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.38344,99964,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.26789,6.23332,6.29365,6.30015,6.21414,6.26789 +hot-max-k,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,5.74057,100020,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,3.39216,4.53045,3.70555,3.39126,3.39216,3.38556 +hot-max-k,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.37509,120560,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.14683,6.14683,6.15845,6.14803,6.11074,6.13654 +hot-max-k,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,3.45289,118900,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,2.11833,2.17863,2.12537,2.11736,2.11833,2.08315 +hot-max-k,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.40579,161340,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.13231,6.16442,6.11848,6.13231,6.1134,6.16976 +hot-max-k,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.72658,159320,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,1.63454,1.64306,1.63454,1.60748,1.60889,1.64577 +hot-max-k,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,7.54174,243552,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.1588,6.1588,6.18051,6.1497,6.14376,6.19282 +hot-max-k,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,2.95809,241364,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,1.3723,1.4113,1.44847,1.3723,1.3356,1.29722 +hot-max-k,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,1,8.74676,366052,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.16771,6.15327,6.16171,6.16771,6.22595,6.18315 +hot-max-k,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,1,3.93952,364488,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,1.28793,1.32202,1.2297,1.28512,1.34106,1.28793 +hot-max-k,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,4.38078,364348,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,1.30467,1.41197,1.36479,1.30391,1.30467,1.29345 +hot-max-k,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.66154,366268,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.1954,6.1954,6.1982,6.19709,6.16666,6.18719 +hot-max-k,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,3.03695,241512,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,1.28785,1.3319,1.27833,1.28785,1.27558,1.2973 +hot-max-k,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.3466,243484,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.15276,6.15276,6.13943,6.13051,6.17855,6.18507 +hot-max-k,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.95818,159440,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,1.67287,1.70497,1.68008,1.67287,1.54847,1.66252 +hot-max-k,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.40997,161036,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.17699,6.19411,6.1624,6.15935,6.18093,6.17699 +hot-max-k,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,3.32557,118544,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,2.09552,2.39511,2.10925,2.0947,2.08309,2.09552 +hot-max-k,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.36134,120448,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.13649,6.17954,6.11833,6.11386,6.13649,6.16082 +hot-max-k,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,4.63966,100016,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,3.37885,3.41576,3.38793,3.37885,3.36666,3.36499 +hot-max-k,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.41444,100512,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.19853,6.16521,6.14905,6.20746,6.19853,6.2215 +hot-max-k,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,7.42152,89524,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.20461,6.24415,6.20461,6.18473,6.20186,6.24173 +hot-max-k,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.35581,89400,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.14517,6.17999,6.13176,6.09883,6.17752,6.14517 +hot-symbol-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.78521,367064,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,2.4075,2.32987,2.35645,2.46329,2.4075,2.48683 +hot-symbol-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,36.5757,375036,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,34.8419,35.4298,35.8719,34.8419,34.3354,34.1151 +hot-symbol-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,3.73629,243832,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,3.51375,3.57388,3.51375,3.56774,3.48701,3.49808 +hot-symbol-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,35.1957,251512,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,34.6904,34.6904,34.5469,35.6115,35.2213,34.5948 +hot-symbol-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,6.46817,161748,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,6.31368,6.6521,6.28895,6.31368,6.59148,6.22177 +hot-symbol-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,36.2894,170056,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,35.6767,35.6767,35.496,36.125,36.778,34.0816 +hot-symbol-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,10.5359,120504,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,10.8565,10.6673,10.9181,10.8565,10.8738,10.7988 +hot-symbol-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,36.6876,128988,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,35.7326,35.8249,35.3189,36.1001,35.7326,35.6006 +hot-symbol-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,21.5157,108188,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,20.9576,20.9051,21.4526,20.9576,21.0922,20.9508 +hot-symbol-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,36.6034,108208,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,35.7782,35.7782,35.8173,35.0556,35.2139,36.0853 +hot-symbol-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,36.6947,97980,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,36.2639,35.1252,36.4469,35.8448,36.3817,36.2639 +hot-symbol-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,35.5297,97488,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,34.6725,35.3185,34.4892,34.9638,34.6725,34.3975 +hot-wide-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.08679,379356,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,1.90556,2.05794,1.88617,1.8453,1.90556,1.91293 +hot-wide-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,20.1953,395248,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,18.9574,18.8774,18.8856,19.4267,18.9818,18.9574 +hot-wide-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.90661,255948,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,2.42207,2.45679,2.37142,2.42129,2.42207,2.47804 +hot-wide-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,20.6313,272340,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.5523,19.5523,19.5461,19.3172,19.5755,19.797 +hot-wide-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,4.50249,173908,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,4.05583,4.10232,4.08733,4.05583,3.96572,3.91609 +hot-wide-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,20.688,190272,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.5835,19.4707,19.773,19.5835,19.4924,19.9972 +hot-wide-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,6.50707,132800,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,6.57188,6.49527,6.57188,6.63152,6.52175,6.58614 +hot-wide-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,20.3871,149100,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,18.9803,19.0742,18.9749,18.7073,18.9803,19.4733 +hot-wide-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,13.3409,128604,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,12.527,12.4269,12.3787,12.527,12.6118,12.816 +hot-wide-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,20.1195,128420,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.2686,19.2686,19.5877,19.9543,18.7694,18.6463 +hot-wide-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,19.7617,118480,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,18.7577,18.7393,18.7948,18.8716,18.4584,18.7577 +hot-wide-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,20.3768,118412,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.4183,19.2237,19.4508,19.4183,19.023,19.4853 +hot-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,3.5996,364544,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,1.2043,1.2043,1.20508,1.29453,1.1801,1.14793 +hot-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,13.6961,366548,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.82342,6.3096,5.82342,5.82336,5.79051,5.82712 +hot-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.516,241396,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,0.737863,0.747169,0.737863,0.719652,0.729083,0.760156 +hot-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,6.99347,243344,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.86683,5.8922,5.80524,5.90029,5.81798,5.86683 +hot-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.23978,158972,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,1.11068,1.07944,1.11068,1.10765,1.11339,1.15358 +hot-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,6.99707,161372,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.77123,5.83518,5.76094,5.77123,5.77773,5.76894 +hot-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,3.39609,118396,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,1.74382,2.17152,1.74382,1.77724,1.68515,1.6945 +hot-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.00075,120288,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.80959,5.87178,5.80959,5.76542,5.8277,5.78385 +hot-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,4.27335,100256,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,3.0307,3.07352,3.0307,3.02827,3.08392,3.01985 +hot-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.02983,100276,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.7627,5.87535,5.74661,5.7515,5.8812,5.7627 +hot-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,7.00944,89648,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.87199,5.87199,5.86022,5.86306,5.9021,5.92574 +hot-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,6.99542,89216,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.77201,5.81899,5.75883,5.81855,5.77201,5.74921 +hot-wide-mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,1.59876,378852,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,1.27355,1.31259,1.28808,1.27355,1.25417,1.26915 +hot-wide-mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,32.4831,476880,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.533,14.4453,14.4599,15.0834,14.6472,14.533 +hot-wide-mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,1.7985,255844,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,1.58666,1.59394,1.64525,1.58186,1.58666,1.57662 +hot-wide-mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,32.4126,353804,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.3246,14.3528,14.3048,14.3813,14.157,14.3246 +hot-wide-mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.99208,173656,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,2.84235,2.83071,2.90296,2.85441,2.82717,2.84235 +hot-wide-mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,32.489,271568,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4314,14.4814,14.3281,14.36,14.4314,14.7776 +hot-wide-mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,4.0445,133016,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,3.9633,3.93929,3.99574,3.96831,3.9633,3.93339 +hot-wide-mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,32.4351,230740,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4628,14.6148,14.6362,14.4628,14.3024,14.3368 +hot-wide-mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,8.55513,128668,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,7.5789,7.51847,7.52946,7.61926,7.73311,7.5789 +hot-wide-mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,32.2614,210252,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4565,14.4649,14.5082,14.4452,14.4565,14.3845 +hot-wide-mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,32.4883,200188,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4751,14.5269,14.4702,14.4892,14.4751,14.4706 +hot-wide-mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,32.3858,200160,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.1858,14.1858,14.1017,14.1261,14.4256,14.3077 +hot-mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,3.57523,364732,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,1.07636,1.22994,1.07636,1.06259,1.07637,1.07101 +hot-mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,29.7357,448184,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.2567,11.4362,11.3465,11.2567,11.2557,11.2534 +hot-mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.90569,241648,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,1.14461,1.27966,1.14216,1.17699,1.14461,1.12531 +hot-mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,29.3684,324988,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.2777,11.3691,11.2777,11.265,11.2986,11.2546 +hot-mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,3.30586,159280,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,2.00511,2.05967,1.99641,1.98784,2.00511,2.01708 +hot-mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,29.6643,243536,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.2642,11.4028,11.4029,11.2534,11.2561,11.2642 +hot-mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,4.20375,118584,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,2.80936,2.83502,2.80936,2.89004,2.78967,2.79052 +hot-mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,28.9399,202104,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.4252,11.3294,11.4252,11.381,11.447,12.0946 +hot-mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,6.85102,100016,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,5.1869,5.21575,5.26013,5.17977,5.1869,5.18137 +hot-mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,29.5235,181684,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.2956,11.319,11.3416,11.2956,11.2937,11.2521 +hot-mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,30.0294,171128,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.4595,11.4294,11.5183,11.8617,11.3078,11.4595 +hot-mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,29.3602,171460,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.2549,11.3233,11.3523,11.2549,11.2245,11.2298 +hot-wide-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.0748,378992,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,1.67952,1.64705,1.68099,1.61336,1.69159,1.67952 +hot-wide-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,20.9251,395244,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.8463,19.8463,19.913,19.8819,19.5936,19.5788 +hot-wide-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.54247,255664,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,3.47676,3.47676,3.84349,3.87596,3.41171,3.28706 +hot-wide-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,20.7774,271792,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,20.1513,19.6643,20.1513,23.961,26.5153,19.9237 +hot-wide-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,4.04567,173836,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,3.53612,3.62767,3.53005,3.65599,3.53612,3.48336 +hot-wide-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,20.6322,189732,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.6198,20.4326,22.412,19.6198,19.072,19.4434 +hot-wide-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,5.79021,132528,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,5.86328,5.92554,5.77274,5.86328,5.81407,5.89558 +hot-wide-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,21.314,148844,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.8821,19.7585,19.8236,20.2231,19.9723,19.8821 +hot-wide-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,12.1016,128692,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,11.3624,10.9029,11.2671,11.6235,11.6195,11.3624 +hot-wide-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,20.7728,128640,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.9932,19.9932,20.2502,19.8747,19.8866,20.7545 +hot-wide-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,20.914,118412,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.7562,20.4155,20.0823,19.7562,19.7384,19.5182 +hot-wide-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,20.6581,118348,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.7952,19.7421,19.8052,20.4406,19.4477,19.7952 +hot-symbol-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.61013,366596,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,2.41344,2.47903,2.38246,2.38638,2.44902,2.41344 +hot-symbol-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,35.2741,374780,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,35.3531,37.4418,35.3709,35.3531,34.6107,34.9865 +hot-symbol-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,3.3892,243668,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,3.1623,3.03813,3.1623,2.99327,3.16914,3.18782 +hot-symbol-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,36.6978,251632,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,35.9338,36.2608,35.9338,37.0411,35.2739,35.924 +hot-symbol-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,6.44304,161452,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,5.66045,5.98338,5.66045,5.4916,5.46609,5.7164 +hot-symbol-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,36.5754,169828,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,35.2788,34.7036,35.2788,34.7241,35.4476,35.8681 +hot-symbol-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,9.32877,120708,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,9.09093,8.97134,9.1023,9.12318,9.09093,8.8811 +hot-symbol-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,36.5557,128692,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,36.5553,36.5554,36.5553,36.1761,37.3858,35.8881 +hot-symbol-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,18.3777,108276,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,17.7944,17.6692,17.3455,17.8008,18.0905,17.7944 +hot-symbol-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,36.6823,107652,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,36.5819,36.0978,36.7609,36.0659,36.5819,36.8011 +hot-symbol-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,34.5945,97656,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,33.4078,34.4612,33.992,33.039,33.4078,33.3568 +hot-symbol-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,34.4095,97836,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,36.8017,36.8017,36.7799,38.3341,36.3642,37.1784 +hot-first-last,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,1.9784,366812,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,0.524855,0.689018,0.559135,0.517809,0.524855,0.507565 +hot-first-last,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.10527,374336,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.42971,6.42971,6.43052,6.42422,6.42905,6.43955 +hot-first-last,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,1.51605,243676,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,0.939317,0.840781,0.983577,0.970071,0.939317,0.933048 +hot-first-last,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.02647,251232,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.39826,6.41603,6.39586,6.37933,6.42252,6.39826 +hot-first-last,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,1.46065,161016,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,0.943296,0.983698,0.972621,0.943296,0.937829,0.925688 +hot-first-last,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.05969,169768,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.42354,6.4279,6.41946,6.42354,6.42971,6.40577 +hot-first-last,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,1.86146,120624,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,1.49241,1.48099,1.4726,1.49241,1.5677,1.73147 +hot-first-last,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.18214,128468,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.39944,6.39339,6.39944,6.41916,6.38484,6.52574 +hot-first-last,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,3.45462,108436,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,2.58811,2.73456,2.60387,2.58811,2.58736,2.58496 +hot-first-last,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.08728,108176,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.38506,6.40752,6.41113,6.38506,6.38008,6.37048 +hot-first-last,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,4.69748,98124,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,3.87909,3.87909,3.91771,3.82407,3.78419,4.03123 +hot-first-last,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,7.09583,97912,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.40329,6.42886,6.40117,6.39057,6.43872,6.40329 +mixed-distinct-string-values,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,59.2207,531176,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,31.2679,31.2679,30.2094,31.0579,32.0421,32.3115 +mixed-distinct-string-values,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,58.0669,408308,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,32.1899,31.8295,32.1899,34.0676,32.4773,32.1172 +mixed-distinct-string-values,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,58.3363,324268,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,32.3774,32.3774,30.6909,31.608,32.7485,33.0371 +mixed-distinct-string-values,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,59.6659,274896,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,44.9527,44.8663,44.1589,45.8174,44.9527,45.413 +mixed-distinct-string-values,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,65.0125,256756,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,51.4016,52.149,52.133,51.1395,51.4016,51.2601 +mixed-distinct-string-values,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,27.8119,118680,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,26.7063,26.7063,26.968,26.9271,26.6712,26.6464 +mixed-distinct-list-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,6.75216,462320,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,6.85346,6.85346,7.42246,7.19175,6.84535,6.28807 +mixed-distinct-list-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,7.58198,329572,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,7.17242,7.26924,7.17242,7.46434,6.47688,6.72735 +mixed-distinct-list-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,7.41903,214856,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,7.00859,7.24344,7.45334,6.82764,6.85859,7.00859 +mixed-distinct-list-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,9.8477,169900,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,8.50203,9.68508,8.4708,8.82693,8.22869,8.50203 +mixed-distinct-list-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,34.5208,110740,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,33.6618,33.7194,33.537,33.6618,33.4603,33.6736 +mixed-distinct-list-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,39.1396,98156,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,38.7604,38.7604,38.5786,38.7371,38.7922,38.8154 +distinct-list-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,3.50549,372968,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,3.54334,4.40341,3.12147,3.55101,3.50695,3.54334 +distinct-list-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,2.18033,249688,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,1.72752,1.72752,1.7065,1.71992,1.75157,1.87675 +distinct-list-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,2.96067,167936,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,2.50874,2.59071,2.43754,2.3303,2.50874,2.73506 +distinct-list-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,4.53703,126652,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,4.33223,4.36636,4.52643,4.33223,4.304,4.23694 +distinct-list-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,8.61914,106304,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,8.3329,8.27419,8.3329,8.54448,8.23354,8.60426 +distinct-list-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,14.9217,95944,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,14.6101,14.358,14.6101,14.294,14.6312,15.2592 +mixed-distinct-guid-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,8.24364,479128,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,6.31025,6.36537,6.31025,6.40903,5.88059,6.14317 +mixed-distinct-guid-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,33.3119,468968,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.2683,31.375,31.2683,30.9474,31.3563,31.1215 +mixed-distinct-guid-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,7.60494,348020,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,6.5276,5.91042,6.5276,6.63394,6.98815,6.19413 +mixed-distinct-guid-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,33.0239,320808,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.5628,31.7996,31.8536,31.4886,31.5628,31.2184 +mixed-distinct-guid-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,7.38673,247816,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,6.0928,6.0928,6.00991,6.13048,6.03723,6.24427 +mixed-distinct-guid-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,33.8264,210808,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.3054,31.7151,31.1664,31.1218,31.3054,31.5348 +mixed-distinct-guid-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,8.37359,196740,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,7.25415,7.25415,6.79316,7.31076,6.93612,7.42285 +mixed-distinct-guid-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,33.628,165224,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.3845,31.2915,31.3845,31.4205,31.5277,31.2678 +mixed-distinct-guid-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,34.0543,137132,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,32.3134,32.2561,32.5624,32.2672,32.435,32.3134 +mixed-distinct-guid-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,33.6914,136604,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,32.2766,32.5049,32.3141,32.0362,32.03,32.2766 +mixed-distinct-guid-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,38.1202,122740,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,36.8961,36.8961,36.793,36.9557,36.935,36.7519 +mixed-distinct-guid-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,38.987,122384,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,36.8238,36.9675,36.8238,36.6904,36.825,36.663 +distinct-guid-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,3.8932,399360,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,1.47043,1.48488,1.49838,1.45661,1.47043,1.43051 +distinct-guid-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,33.1633,476644,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.5222,28.5222,28.6645,28.6461,28.099,28.2461 +distinct-guid-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,2.69334,276520,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,1.30574,1.44473,1.30574,1.3862,1.24919,1.29688 +distinct-guid-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,29.7524,320856,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.6772,28.9866,28.1612,28.7331,28.0266,28.6772 +distinct-guid-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,3.00404,194116,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,1.74199,1.87639,1.91842,1.74199,1.71653,1.73284 +distinct-guid-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,29.8262,214568,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,27.9686,28.5485,28.0057,27.7374,27.8866,27.9686 +distinct-guid-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,4.73443,153284,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,2.85656,2.86379,2.8477,2.85656,2.92921,2.8544 +distinct-guid-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,29.5131,165340,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,27.8987,27.9316,27.8447,27.8983,28.0404,27.8987 +distinct-guid-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,6.78034,132768,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,5.40282,5.40282,5.4521,5.37311,5.39046,5.47626 +distinct-guid-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,29.6837,136596,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.387,28.3956,28.387,28.4345,28.3131,28.2906 +distinct-guid-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,9.67227,116056,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,8.59811,8.98816,8.53804,8.76757,8.59811,8.5421 +distinct-guid-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,33.4932,122304,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,31.9255,32.1927,31.7873,31.9255,32.2069,31.854 +mixed-distinct-string-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,7.84305,487468,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,3.99634,3.70294,3.76675,3.99634,4.26113,4.01295 +mixed-distinct-string-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,10.2163,682616,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,10.1374,8.48462,10.1374,10.5103,9.90758,11.2625 +mixed-distinct-string-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,6.38486,336036,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,4.19809,4.19809,4.63514,4.01298,4.06034,4.36147 +mixed-distinct-string-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,9.737,573736,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,9.73404,12.783,9.09339,9.73404,11.6343,8.71487 +mixed-distinct-string-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,7.38497,229188,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,5.58209,5.76459,5.78894,5.58066,5.58209,5.46916 +mixed-distinct-string-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,16.5023,353684,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,9.31757,13.8748,11.6008,9.31757,8.91972,8.99558 +mixed-distinct-string-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,11.2706,171648,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,9.11103,9.11103,9.13956,9.09891,9.09798,9.18126 +mixed-distinct-string-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,15.4494,225028,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,13.5995,13.7129,13.6523,13.5089,13.4416,13.5995 +mixed-distinct-string-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,17.9577,143536,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,16.5728,16.6883,16.5728,16.53,16.5514,16.5854 +mixed-distinct-string-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,27.8705,208828,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,25.6296,26.6552,26.0106,25.566,25.6124,25.6296 +mixed-distinct-string-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,34.2599,128876,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,32.7586,32.7969,32.8944,32.7586,32.7325,32.7365 +mixed-distinct-string-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,45.1348,118068,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,45.1732,45.2749,45.1732,45.1403,45.162,45.277 +distinct-string-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,4.81321,405552,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,2.11694,2.23532,2.0696,2.18119,2.03856,2.11694 +distinct-string-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,8.69082,692108,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,7.44161,7.44161,7.27254,8.71549,9.74605,7.22382 +distinct-string-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,3.62774,282532,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,1.99306,2.0651,2.09996,1.95003,1.99306,1.93995 +distinct-string-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,8.60144,585508,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,8.04493,9.34299,8.04493,9.16823,7.98129,7.01563 +distinct-string-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,3.97352,200428,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,2.85194,2.86545,2.71285,2.53939,2.85888,2.85194 +distinct-string-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,10.7706,354020,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,8.06058,8.41366,8.06058,7.7196,7.88738,9.9089 +distinct-string-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,6.00461,159680,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,4.78189,4.82018,4.77081,4.8832,4.78189,4.77034 +distinct-string-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,14.8144,219124,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,11.979,12.142,11.9678,11.979,11.9389,12.1185 +distinct-string-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,10.5246,139052,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,9.27864,9.27864,9.08917,9.11822,9.38369,9.43534 +distinct-string-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,24.8749,202688,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,22.965,23.2401,22.965,22.774,22.895,23.3248 +distinct-string-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,17.8817,122252,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,16.7405,16.771,16.6818,16.7036,16.7405,16.9272 +distinct-string-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,42.7106,118396,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,40.9586,40.9782,40.9142,40.9085,40.9586,40.9746 +distinct-string-values,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.38633,405800,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,5.57619,4.50868,4.75127,5.57619,6.05791,6.13691 +distinct-string-values,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.30552,282756,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,4.65505,4.74458,4.59855,4.53234,4.6711,4.65505 +distinct-string-values,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,9.17245,200812,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,5.91698,6.00986,5.51605,5.91698,6.11462,5.73016 +distinct-string-values,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,11.3954,159464,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,9.43517,9.44575,9.40174,9.43517,9.44595,9.39476 +distinct-string-values,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,17.0474,138888,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,16.1663,16.1663,15.6204,16.223,15.5454,16.3476 +distinct-string-values,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,25.536,122516,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,22.7224,24.2302,22.7158,22.7224,22.7956,22.3876 +symbol-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.13454,374776,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,4.04844,4.04434,4.04844,4.11845,3.78695,4.07768 +symbol-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,44.5826,393432,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,44.0844,44.8747,44.0844,43.8453,44.0934,43.9834 +symbol-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.41526,264140,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,5.35388,5.21459,5.35388,5.39723,5.13408,5.48675 +symbol-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,46.0483,269644,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,43.8166,45.0659,44.1148,43.5323,43.8166,43.5792 +symbol-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,9.08567,183924,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,7.37157,8.13456,7.37157,6.83361,7.12643,7.75963 +symbol-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,44.9387,188240,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,43.7981,45.0164,43.9112,43.7869,43.4857,43.7981 +symbol-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,16.8873,157380,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,13.5237,13.5899,13.4935,13.5237,13.6894,13.3473 +symbol-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,45.2068,147424,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,43.6947,45.0131,43.6947,43.675,43.6576,43.8153 +symbol-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,26.0174,137104,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,25.2664,25.2664,25.476,25.3049,23.2673,23.1575 +symbol-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,44.802,126624,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,43.7246,44.8081,43.7908,43.7246,43.423,43.41 +symbol-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,40.6463,116464,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,39.7605,40.7975,39.7973,39.6302,39.7605,39.5313 +symbol-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,44.8164,116256,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,44.311,47.8608,44.311,44.3867,44.2622,44.2592 +symbol-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.85419,374740,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,3.5543,3.5543,3.5748,3.51799,3.40056,3.60533 +symbol-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,39.7437,375036,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.5253,39.4988,39.5322,40.1872,39.381,39.5253 +symbol-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.31708,251524,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,4.486,4.57676,4.557,4.486,4.28716,4.36328 +symbol-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,40.1852,251416,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.876,39.7756,39.876,39.9096,40.0227,39.5738 +symbol-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,8.15245,169552,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,6.87281,7.10385,6.59305,6.03839,7.35274,6.87281 +symbol-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,42.7238,169556,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,40.2329,41.7718,40.2329,40.5457,40.0,40.0071 +symbol-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,11.282,128948,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,10.8976,10.9479,11.0039,10.8161,10.8976,10.7278 +symbol-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,39.8108,128948,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.5203,39.3335,40.2923,40.0833,39.5203,38.7257 +symbol-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,20.1982,108456,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,19.0356,19.0356,19.2164,19.1217,19.0254,18.926 +symbol-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,42.5426,108440,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.7428,39.7428,40.0632,40.3848,38.9697,39.4115 +symbol-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,34.4135,97968,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,33.3166,33.5361,33.1113,33.4358,33.3166,33.1348 +symbol-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,40.8204,97928,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,40.3588,40.3588,39.5189,40.4655,40.198,40.4156 +distinct-correlated,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.29535,377968,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,2.07931,2.159,2.06213,2.12174,2.07931,2.06555 +distinct-correlated,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,9.07438,732536,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,10.7676,10.7676,10.2628,15.4782,9.82339,11.5527 +distinct-correlated,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.22627,253952,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,2.16498,2.16498,2.10623,2.13257,2.23683,2.42335 +distinct-correlated,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.4594,574620,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,9.78042,11.0452,9.78042,11.7006,9.2425,9.75305 +distinct-correlated,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.31437,177772,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,2.91264,2.91746,2.85148,2.91264,2.92774,2.80628 +distinct-correlated,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.3634,314128,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,8.55283,9.82685,9.47923,8.55283,8.26017,8.38753 +distinct-correlated,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.87688,138900,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,3.88561,3.8945,3.88561,4.01848,3.87444,3.88258 +distinct-correlated,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,11.7951,195296,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,9.50877,10.8351,9.54593,9.50877,9.08477,9.08954 +distinct-correlated,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,10.4343,121052,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,6.95891,7.06959,6.95891,6.92899,7.07832,6.90758 +distinct-correlated,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,14.0671,189280,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,12.9441,13.2416,11.7292,12.9833,11.5073,12.9441 +distinct-correlated,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,17.5764,122520,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,14.7803,14.8869,14.7392,14.7803,14.6683,14.9905 +distinct-correlated,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,19.4039,122064,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,16.8874,16.695,17.1056,17.008,16.7553,16.8874 +distinct-skew,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,3.48556,447276,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,2.8987,2.68305,2.65403,2.8987,3.14521,3.38236 +distinct-skew,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,17.4594,989260,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,20.2078,15.3797,23.6716,24.3031,20.2078,14.4085 +distinct-skew,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,3.68146,327920,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,3.14434,2.87639,3.07792,3.14434,3.55018,3.15832 +distinct-skew,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,15.4628,712140,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,13.4016,14.7451,15.2954,13.0223,12.2183,13.4016 +distinct-skew,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,5.07841,280904,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,3.99008,4.03878,4.05144,3.96491,3.99008,3.86257 +distinct-skew,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,14.3777,441212,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,12.7106,14.5927,11.5082,12.7106,11.2663,13.0895 +distinct-skew,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,9.25518,211112,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,7.28115,7.75456,7.88263,7.28115,6.39469,6.35724 +distinct-skew,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,17.8565,351548,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,14.7782,15.0914,14.7506,14.7782,13.8131,15.0561 +distinct-skew,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,13.5379,149684,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,11.0965,11.1544,11.2701,11.0707,10.861,11.0965 +distinct-skew,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,22.3314,288068,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,16.8935,18.9715,16.8935,16.6516,16.4973,17.8077 +distinct-skew,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,36.5383,200336,65c9530f85952e51e2e328375168e406e8f1b73d7aa2d83845ac77496ca9365b,32.3638,32.421,32.3197,32.3638,32.1434,32.6752 +distinct-skew,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,47.3734,200060,65c9530f85952e51e2e328375168e406e8f1b73d7aa2d83845ac77496ca9365b,42.816,43.3863,42.9122,42.6438,42.1036,42.816 +all-null-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,0.457733,348164,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.305104,0.349155,0.305104,0.301689,0.295588,0.315805 +all-null-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,1.06472,348380,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.853855,0.931846,0.862185,0.853855,0.833023,0.824738 +all-null-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,0.460429,224996,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.389389,0.389389,0.40128,0.408639,0.358347,0.383491 +all-null-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,1.15113,224864,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,1.02393,1.0471,1.02393,1.13871,1.01001,0.983917 +all-null-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,0.767926,142848,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.616547,0.643974,0.703516,0.609595,0.608522,0.616547 +all-null-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,1.4302,142628,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,1.28914,1.43933,1.42595,1.05192,0.984645,1.28914 +all-null-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,0.90048,101876,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.840608,0.849025,0.840608,0.836198,0.834236,0.846033 +all-null-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,1.49219,101536,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,1.44573,1.44076,1.42584,1.45328,1.80902,1.44573 +all-null-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,2.28923,81560,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,2.19605,2.25569,2.39177,2.19605,1.63995,1.64061 +all-null-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,2.51677,81312,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,2.46123,2.45688,2.45915,2.49518,2.75654,2.46123 +all-null-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,3.33676,71284,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,3.26528,3.27739,3.25391,3.26528,3.27139,3.24725 +all-null-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,4.61663,71100,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,4.55403,4.54499,4.63219,4.63133,4.55403,4.50292 +hot-min-descending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,0.920424,348036,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.64818,0.945838,1.63105,1.69857,1.72846,1.64818 +hot-min-descending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,8.22635,877080,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,10.3967,10.3967,10.7522,8.97714,11.6601,9.26234 +hot-min-descending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,1.26469,225304,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.94421,1.84684,2.10239,1.92354,2.11053,1.94421 +hot-min-descending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,7.91664,538760,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,8.22166,7.88381,10.0548,8.22166,9.24796,7.40973 +hot-min-descending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,2.04729,161552,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.912322,0.976582,0.927863,0.912322,0.909879,0.898509 +hot-min-descending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,1.47922,257608,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.7947,1.7947,2.27615,1.78687,1.29061,2.54518 +hot-min-descending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,0.606774,102368,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.531226,0.531226,0.519362,0.534582,0.86862,0.529343 +hot-min-descending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,1.92079,151556,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.78914,1.42089,1.96051,1.78914,2.38561,1.3925 +hot-min-descending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,0.84501,81760,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.783632,0.783632,0.788357,0.783176,0.777971,1.10123 +hot-min-descending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,2.1551,97756,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.43775,1.87746,1.3384,1.43225,1.67083,1.43775 +hot-min-descending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,1.29068,71476,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.17425,1.1996,1.17425,1.17309,1.17383,1.17452 +hot-min-descending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,1.98724,71224,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.89781,1.89933,1.89451,1.88563,1.89781,1.89796 +hot-min-ascending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,1.0172,348280,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.856921,0.850866,0.870935,0.856921,0.87233,0.856856 +hot-min-ascending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,8.61974,882712,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,9.95009,9.28077,10.732,9.95009,11.2521,9.32928 +hot-min-ascending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,0.734111,225400,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.766571,0.700934,0.766571,0.736093,0.931251,0.788466 +hot-min-ascending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,9.07751,538588,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,8.66542,8.65861,8.66542,9.86851,8.48773,8.66777 +hot-min-ascending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,0.549404,142964,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.492703,0.508534,0.433133,0.486555,0.492703,0.633032 +hot-min-ascending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,1.10758,257708,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.80286,1.59486,2.1942,1.80286,1.29858,2.43801 +hot-min-ascending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,0.639223,102340,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.599162,0.599162,0.743293,0.571852,0.626828,0.586195 +hot-min-ascending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,1.30177,151196,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.06474,0.91987,1.27825,1.06474,1.55885,0.877699 +hot-min-ascending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,1.03564,81548,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.03248,0.965458,1.09188,1.03558,1.02659,1.03248 +hot-min-ascending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,1.50684,97856,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.36765,1.48405,1.20726,1.30864,1.62344,1.36765 +hot-min-ascending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,1.86054,70784,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.78354,1.80415,1.78539,1.78198,1.78291,1.78354 +hot-min-ascending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,2.06486,71280,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.89488,1.90784,1.89211,1.89488,1.90093,1.89387 +nonnull,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.14658,370752,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,0.874103,1.42566,0.874103,0.872914,0.840663,0.939361 +nonnull,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,6.40073,747844,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,7.58751,7.58751,8.22045,6.3514,7.46727,10.2103 +nonnull,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.75882,247948,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,1.25077,1.44868,1.22308,1.29761,1.22468,1.25077 +nonnull,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.30559,575716,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,8.41041,8.41041,8.4463,8.62413,8.40587,7.81987 +nonnull,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.41113,165456,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.23125,2.29409,2.16999,2.23125,2.22333,2.26861 +nonnull,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.823,257572,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.35221,2.56276,2.30565,2.35221,2.34389,3.18817 +nonnull,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.29305,120756,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,1.21337,1.242,1.20441,1.21204,1.21337,1.21489 +nonnull,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.49027,151264,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.1281,1.95624,2.40286,2.1281,2.58885,1.91797 +nonnull,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.49878,95704,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.21053,3.60692,2.38683,2.19195,2.21053,2.19287 +nonnull,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.39655,97908,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,3.06122,3.26478,2.81553,2.80297,3.15705,3.06122 +nonnull,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.22125,71256,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,3.06331,3.04369,3.06331,3.11684,3.09618,3.0064 +nonnull,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.00797,70964,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,4.54254,4.68005,4.51614,4.61832,4.54254,4.49143 +unique-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1000000,11.3381,413240,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,1.73209,5.35805,1.76479,1.68509,1.73209,1.71217 +unique-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1000000,12.4491,962800,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,11.0086,10.9798,10.0725,11.6623,11.0086,13.0198 +unique-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1000000,6.3016,290700,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,1.88143,3.39532,1.99129,1.88143,1.84226,1.84109 +unique-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1000000,11.8857,596160,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,10.6303,10.6564,10.6303,10.7573,9.90503,10.1239 +unique-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1000000,5.82791,208528,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,2.73198,4.27215,3.00027,2.73198,2.63347,2.63566 +unique-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1000000,14.7041,333380,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,13.2508,14.7721,13.288,13.2508,13.2422,12.982 +unique-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1000000,7.11016,167712,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,4.05321,5.09842,4.42503,4.05321,3.91744,3.92355 +unique-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1000000,24.7197,218916,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,22.2002,23.9214,22.4294,22.2002,22.0387,21.8625 +unique-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1000000,9.9795,147380,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,6.98945,7.47582,6.78154,6.69073,6.98945,7.32405 +unique-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1000000,43.9082,201896,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,40.4074,41.726,40.1454,39.4468,40.4074,42.0355 +unique-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1000000,10.109,136808,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,6.76729,7.70642,6.76729,6.72166,6.81468,6.66101 +unique-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1000000,31.7378,136528,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,28.3488,28.5918,28.355,28.2726,28.3488,28.3255 +list-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,3.60166,372964,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,2.76222,2.73845,2.72298,2.76222,2.81395,2.8411 +list-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,28.6409,379116,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.6754,27.7449,27.6754,27.5109,27.679,27.6367 +list-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,3.5098,249676,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,2.99824,3.06721,2.95435,3.04046,2.99824,2.93803 +list-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,28.6869,255860,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.7824,27.7824,27.645,27.9939,27.8176,27.5406 +list-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,4.31066,167924,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,3.63171,3.98783,3.78595,3.48594,3.34136,3.63171 +list-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,28.5767,173620,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.6002,27.6293,27.6002,27.5455,27.5994,27.9577 +list-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,6.12192,126616,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,5.41346,5.42903,5.38443,5.41346,5.37186,5.41591 +list-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,28.9412,132636,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,28.0665,28.0665,28.1059,29.1557,27.9162,27.8823 +list-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,9.87848,106144,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,9.35463,9.35463,9.46713,9.35383,9.31574,9.40565 +list-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,28.6511,112268,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.7233,27.7292,27.9649,27.7035,27.6953,27.7233 +list-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,17.4859,101996,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,16.4535,16.5703,16.3557,16.3272,16.4535,16.5531 +list-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,28.6646,101836,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.6491,27.9673,27.9288,27.5281,27.644,27.6491 +guid-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,6.29287,399880,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,2.4193,2.44852,2.4193,2.39186,2.4363,2.38271 +guid-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,12.238,399396,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.5296,10.5796,10.3655,10.5917,10.5296,10.42 +guid-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,4.36779,276316,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,2.49245,2.40441,2.49245,2.56111,2.4863,2.67681 +guid-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,12.3149,276344,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.6907,10.7198,10.6907,10.6231,10.6709,10.7088 +guid-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,4.11737,194312,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,3.1572,2.829,3.13334,3.24515,3.18764,3.1572 +guid-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,12.2829,194664,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.5133,10.4909,10.5595,10.5809,10.4334,10.5133 +guid-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,5.23701,153588,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,3.88525,3.94526,3.8736,3.87745,3.88525,3.92857 +guid-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,12.6018,153072,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.9615,10.9775,10.9615,10.9275,10.9197,10.9697 +guid-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,7.92789,133040,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,6.37708,6.37708,6.49247,6.36249,6.39208,6.34174 +guid-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,12.1282,132660,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.6529,10.3661,10.6529,10.4589,11.5613,10.8085 +guid-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,11.9861,122708,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.6609,10.2563,13.9532,13.8676,10.6609,10.305 +guid-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,12.2227,122752,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.5166,10.4315,10.5166,10.6456,10.4653,11.0585 +hot-top-ascending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,1.02154,348556,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,0.709564,0.77386,0.741918,0.693303,0.696095,0.709564 +hot-top-ascending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,6.49138,364560,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.37463,5.46948,5.3693,5.36189,5.43588,5.37463 +hot-top-ascending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,1.12856,225320,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,0.763049,0.903153,0.763049,0.810472,0.761664,0.736016 +hot-top-ascending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,6.60741,241768,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.46717,5.40581,5.37185,5.51559,5.46717,5.47475 +hot-top-ascending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,1.44801,143212,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,1.34047,1.31049,1.34047,1.35136,1.36845,1.31261 +hot-top-ascending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,6.53261,159432,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.37697,5.36844,5.40278,5.32645,5.37697,5.41419 +hot-top-ascending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,1.95997,102280,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,1.87674,1.86391,1.87674,1.87073,1.99875,1.92671 +hot-top-ascending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,6.43816,118680,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.45601,5.45147,5.46447,5.44353,5.45601,5.45678 +hot-top-ascending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,6.2911,98224,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,4.11724,4.14184,3.87372,4.11724,4.31928,4.10329 +hot-top-ascending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,6.50204,98188,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.40962,5.45631,5.39542,5.43741,5.39009,5.40962 +hot-top-ascending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,1,6.4159,87836,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.38995,5.41261,5.37344,5.36096,5.41025,5.38995 +hot-top-ascending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,1,6.43096,87672,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.38656,5.3973,5.42124,5.37378,5.38656,5.38002 +few-groups-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,7,2.39552,365028,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,0.552251,0.73564,0.581221,0.552251,0.508612,0.511941 +few-groups-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,7,5.97938,366488,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.56004,4.56004,4.51061,4.68047,4.69011,4.4699 +few-groups-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,7,1.96191,241600,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,0.559137,0.68664,0.547553,0.559137,0.558475,0.56469 +few-groups-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,7,6.01618,243732,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.34745,4.34745,4.42045,4.35516,4.29704,4.31179 +few-groups-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,7,1.85696,159852,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,0.900485,0.933023,0.919189,0.900485,0.895434,0.886568 +few-groups-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,7,6.01417,161364,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.53674,4.48174,4.57924,4.58125,4.53674,4.52984 +few-groups-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,7,2.21448,118440,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,1.1877,1.23212,1.17177,1.19299,1.1877,1.1849 +few-groups-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,7,5.96232,120560,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.56055,4.48096,4.44925,4.57433,4.56055,4.61184 +few-groups-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,7,2.99341,100252,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,1.84696,1.89805,1.81344,1.93045,1.84696,1.76672 +few-groups-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,7,6.06117,99732,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.36927,4.36927,4.3381,4.35263,4.41569,4.44638 +few-groups-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,7,3.39713,90180,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,2.0637,2.10741,2.05043,2.05266,2.0637,2.06402 +few-groups-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,7,5.98107,89536,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.57103,4.56051,4.56286,4.57103,4.68809,4.61153 +extrema-descending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.13766,348376,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,0.816589,0.876699,0.848771,0.816589,0.777394,0.788987 +extrema-descending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.76794,794668,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,6.37629,5.80907,6.37629,4.98151,6.81568,8.122 +extrema-descending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.33349,225112,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,1.19614,1.12551,1.22311,1.22111,1.19614,1.15616 +extrema-descending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.93306,528376,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,4.56768,4.56768,5.33345,4.29052,4.32643,5.60786 +extrema-descending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.27525,143000,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.19676,2.13194,2.19676,2.22537,2.14961,2.31506 +extrema-descending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.20526,300680,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,3.13362,3.38777,2.92513,4.04059,3.13362,2.85756 +extrema-descending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.33886,102084,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,1.16895,2.18299,2.11783,1.16175,1.16895,1.15033 +extrema-descending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.51378,169476,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.92969,2.53246,3.60994,2.92969,3.03057,2.92097 +extrema-descending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.91099,81796,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,1.72005,1.72899,1.73318,1.72005,1.71537,1.71603 +extrema-descending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.04569,103900,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.44734,2.62224,2.39139,2.04737,2.44734,2.50671 +extrema-descending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.31361,71296,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.64595,3.13381,2.66266,2.64425,2.64595,2.63639 +extrema-descending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.46807,71256,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,3.21664,3.16703,3.21405,3.21664,3.29577,3.28579 +extrema-ascending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.13806,347868,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,0.991834,0.977533,0.991834,0.986269,1.04293,0.993183 +extrema-ascending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.34774,801064,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,6.30394,5.82879,6.49927,5.70493,6.30394,7.27933 +extrema-ascending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.26869,225320,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,1.12224,1.08836,1.12224,1.11278,1.19573,1.18912 +extrema-ascending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.96972,528156,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,4.50175,4.50175,5.40857,4.12894,3.96625,5.41964 +extrema-ascending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.59597,142724,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,1.47052,1.56037,1.55307,1.43397,1.43584,1.47052 +extrema-ascending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.82407,300580,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,3.21948,3.21948,2.84604,3.94367,3.29391,2.83153 +extrema-ascending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.30307,102168,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,1.18048,1.18048,1.16223,1.43786,1.19018,1.16553 +extrema-ascending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.05204,169720,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,2.89582,2.89582,3.4683,2.78136,3.32961,2.84558 +extrema-ascending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.02395,81536,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,1.72425,1.72295,1.72972,1.72425,1.72246,1.72836 +extrema-ascending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.37752,103788,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,2.43922,2.49866,2.44072,2.1612,2.43922,2.14545 +extrema-ascending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.84876,70768,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,2.6506,2.67357,2.6506,2.62636,2.6327,2.65242 +extrema-ascending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.72952,71028,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,3.53015,3.41495,3.55336,3.53515,3.42557,3.53015 +wide-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.73091,516700,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,4.00581,3.99339,3.99914,4.00581,4.88978,4.1478 +wide-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,34.3055,462760,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,32.4085,34.8649,32.4085,31.3798,32.8018,32.1995 +wide-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.8097,420080,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,5.29651,5.02695,5.29651,5.47951,5.08193,5.66828 +wide-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,35.1798,339284,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,32.5018,34.2069,32.5018,32.5743,32.1489,32.1403 +wide-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,7.61747,341928,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,6.85939,6.87724,6.84064,6.98384,6.85313,6.85939 +wide-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,34.3376,258096,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,31.383,33.6975,32.0854,31.0831,31.383,31.2647 +wide-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,14.1295,225224,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,12.0473,12.055,12.0473,12.8436,11.8865,11.2803 +wide-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,36.95,216460,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,32.4707,35.084,32.8854,32.3999,32.4707,31.6923 +wide-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,25.7063,212544,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,21.4238,20.309,21.8223,22.2112,19.818,21.4238 +wide-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,34.1592,195976,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,31.5954,33.6461,31.8772,31.4085,31.3677,31.5954 +wide-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,34.4335,185960,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,31.1848,33.312,31.3001,31.1111,31.1848,31.0507 +wide-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,34.6592,185900,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,31.5003,33.4585,31.9272,31.2772,31.192,31.5003 +wide-mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,8.24282,397332,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,5.62216,6.11269,5.62216,8.56363,5.47021,4.93423 +wide-mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,16.9505,395068,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,14.9622,15.0858,15.1169,14.8456,14.6744,14.9622 +wide-mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.42177,274688,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,3.78208,3.75469,3.78208,3.57943,4.00661,3.94034 +wide-mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,15.2564,272024,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,14.2123,14.0401,14.1877,14.3564,14.2123,14.6024 +wide-mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.74942,192284,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,4.51779,4.51779,4.55669,4.66575,4.30717,4.16922 +wide-mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,15.4623,190444,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,15.1289,15.6058,15.44,15.1289,14.99,14.5182 +wide-mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,7.82692,151456,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,7.02921,7.38798,7.35736,7.00798,6.74074,7.02921 +wide-mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,16.7449,149344,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,14.8806,15.8093,14.8806,14.6538,14.6593,16.1569 +wide-mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,11.5964,128920,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,12.2629,11.0695,12.2629,12.5821,12.3014,12.0888 +wide-mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,16.261,128736,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,16.1532,16.0866,16.1532,15.2626,17.0636,16.1789 +wide-mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,17.0749,118416,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,16.505,16.1791,17.1173,16.0151,16.505,21.0218 +wide-mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,16.9147,118608,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,15.4688,16.2231,16.2533,15.4688,15.3261,14.6161 +extrema-timestamp,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.64016,406020,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,0.969572,1.14552,0.943047,0.969572,0.946609,0.972825 +extrema-timestamp,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,7.64854,831812,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,7.52515,7.52515,7.98263,6.48786,6.55996,9.4945 +extrema-timestamp,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.30066,282412,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,1.306,1.33188,1.22981,1.22365,1.306,1.34471 +extrema-timestamp,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,7.70814,610856,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,8.13165,8.13165,8.88062,7.82586,8.09387,8.19981 +extrema-timestamp,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.99039,200596,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,1.99491,1.99491,1.91265,1.95106,2.06851,2.00351 +extrema-timestamp,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.82235,348260,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,4.1629,4.48857,3.64207,4.98965,4.1629,3.15957 +extrema-timestamp,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.35415,159060,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,1.34149,1.34149,1.31446,1.73672,1.42961,1.3348 +extrema-timestamp,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.57648,217104,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,2.32631,3.35621,3.19222,2.06749,2.32631,2.17042 +extrema-timestamp,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.32123,128664,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,1.95401,2.00067,1.95401,1.9569,1.94122,1.94467 +extrema-timestamp,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.99709,151452,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,2.85235,3.02598,2.85235,2.52351,2.94161,2.64677 +extrema-timestamp,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.54841,118380,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,3.22706,3.30265,3.38161,3.22706,3.10823,3.16154 +extrema-timestamp,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.46084,118656,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,3.82812,4.05508,3.78407,3.82812,3.81277,3.88881 +extrema-date,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.41779,381108,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,1.08279,1.13672,1.06005,1.09398,1.08279,1.03402 +extrema-date,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.53009,821560,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,6.61732,5.39975,6.61732,4.47156,8.28866,7.95528 +extrema-date,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.54416,257932,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,1.30598,1.25815,1.2327,1.30598,1.31616,1.32874 +extrema-date,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.15215,554828,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,5.07625,4.84019,5.11903,5.07625,4.42777,5.34205 +extrema-date,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.08133,175916,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,2.04679,2.09458,2.04679,2.10148,1.4522,1.43356 +extrema-date,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.1173,333464,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,3.26633,3.42377,3.03842,3.69165,3.26633,3.05931 +extrema-date,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.44761,134696,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,1.26555,1.35878,1.32086,1.26029,1.24636,1.26555 +extrema-date,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.28491,202724,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,2.32814,2.40801,2.6817,1.93417,2.32814,2.03767 +extrema-date,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.06032,114316,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,1.83369,1.86057,1.83201,1.83369,1.81411,3.29941 +extrema-date,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.95273,136604,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,2.82735,2.82735,2.83599,2.52023,2.90211,2.47315 +extrema-date,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.05031,104056,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,2.75919,2.75919,2.72437,3.07964,2.815,2.73202 +extrema-date,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.13681,103636,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,3.79154,4.13825,3.87141,3.72041,3.69948,3.79154 +wide-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.60295,397596,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,4.60456,4.93922,4.60456,4.56534,4.44005,4.8713 +wide-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,26.465,403236,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,25.152,25.5739,25.6244,25.1084,25.0472,25.152 +wide-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,7.01715,274860,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,5.25593,5.60455,5.0526,5.15158,5.25593,5.27667 +wide-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,26.1856,280332,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,25.3439,25.3439,25.8886,25.2157,25.4924,25.1027 +wide-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,9.23312,192632,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,6.49302,7.84356,6.40417,6.76497,6.39534,6.49302 +wide-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,26.4974,198308,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,25.2846,26.0664,25.7158,25.2083,25.2009,25.2846 +wide-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,11.4452,151564,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,9.65175,9.98836,9.74752,9.57449,9.65175,9.61265 +wide-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,26.2311,157168,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,25.5615,25.5615,25.4081,25.355,26.0583,25.5955 +wide-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,16.2268,137092,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,15.481,15.8752,15.1917,15.481,15.1139,15.637 +wide-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,26.1071,136856,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,25.5539,25.158,25.5711,25.5539,25.3769,25.6306 +wide-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,25.1943,126620,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,23.9332,23.9057,23.6616,23.9332,24.2428,24.1563 +wide-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,26.7717,126796,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,26.1743,26.4975,26.0652,26.1743,25.2807,28.7196 +string-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,6.92726,405756,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,2.93071,2.9425,2.91388,3.0131,2.93071,2.91825 +string-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,17.2369,399084,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,15.5833,15.557,18.286,15.9415,15.5351,15.5833 +string-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,5.41444,282468,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,3.26589,3.25887,3.26551,3.41359,3.34822,3.26589 +string-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,17.4448,276348,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,15.934,15.934,15.993,15.8923,15.9368,15.809 +string-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,5.6511,200012,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,4.43644,4.3721,4.51291,4.43644,4.82823,4.30426 +string-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,17.2795,194508,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,15.9005,15.7137,15.7601,15.9044,16.1314,15.9005 +string-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,7.43158,159988,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,5.64048,5.64048,5.58553,5.57244,5.68151,5.72017 +string-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,17.6045,153544,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.922,16.5058,17.092,16.8997,16.922,18.3285 +string-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,11.2604,139188,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,10.2885,9.71021,10.366,10.3383,9.67559,10.2885 +string-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,18.3036,132560,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,15.8557,16.2784,15.7652,15.9921,15.8557,15.7603 +string-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,17.8832,122496,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.2289,16.2352,16.2447,16.0592,16.2289,16.2034 +string-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,17.3024,122500,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.0069,17.1535,16.0837,16.0069,15.8346,15.93 +string-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,7.10256,405548,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,2.9287,3.53599,3.76834,2.78376,2.9287,2.68576 +string-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,7.62725,622992,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,8.49716,9.16789,6.63065,9.95459,8.49716,7.42256 +string-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,3.8401,282616,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,1.76062,1.82637,1.73925,1.76062,1.69242,1.78724 +string-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,6.75785,559200,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,7.19752,7.19752,7.33987,7.00543,7.42734,6.46499 +string-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,4.27529,200008,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,2.68372,2.68372,2.79775,2.74722,2.58757,2.64435 +string-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,6.92244,331848,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,6.84057,6.97216,6.85345,6.19181,6.84057,5.88183 +string-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,5.33978,159516,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,4.01938,4.67966,4.01938,3.97844,4.15374,3.99161 +string-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,12.9118,220708,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,8.85097,9.05827,8.85097,8.50076,9.19669,8.51305 +string-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,9.32846,138808,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,8.04631,8.46552,7.77614,8.92612,7.84565,8.04631 +string-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,18.6747,204288,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,17.0944,23.1744,17.0944,18.1073,16.8772,16.8951 +string-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,13.6654,122284,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,12.4629,12.44,12.5906,12.521,12.4629,12.4286 +string-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,25.2604,101984,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,25.4506,24.1622,24.6993,25.5798,25.4506,27.5942 +symbol-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,0.602288,364796,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.244925,0.329207,0.245295,0.244925,0.226753,0.227 +symbol-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,0.637399,364280,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.28981,0.489123,0.295135,0.289266,0.28981,0.268166 +symbol-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,0.463049,241644,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.194295,0.241786,0.205797,0.194295,0.180756,0.175281 +symbol-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,0.553113,241480,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.270062,0.351969,0.28795,0.270062,0.268161,0.250564 +symbol-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,0.452578,159300,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.269699,0.320602,0.285417,0.269479,0.269699,0.261796 +symbol-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,0.468192,159224,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.332787,0.356951,0.358477,0.314095,0.332787,0.313284 +symbol-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,0.680858,118720,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.518259,0.57201,0.511184,0.518259,0.52621,0.51355 +symbol-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,0.578837,118440,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.45528,0.527465,0.47163,0.450659,0.451896,0.45528 +symbol-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,1.06701,97912,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.85474,1.30524,0.940661,0.85474,0.637559,0.544542 +symbol-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,0.646787,97572,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.527431,0.588763,0.527431,0.515822,0.505286,0.562006 +symbol-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,1.26836,87564,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.06603,1.21338,1.07342,1.06603,1.0397,1.05689 +symbol-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,1.20587,87360,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.24551,1.21952,1.11115,1.24551,1.41,1.42464 +float-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.71736,374780,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,2.71596,2.84275,2.68173,2.76103,2.70167,2.71596 +float-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,16.7687,375288,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,15.0729,15.1967,15.1694,14.9661,14.9676,15.0729 +float-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.42875,252044,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,3.90144,4.13925,3.59585,3.90144,4.19531,3.53731 +float-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,17.6986,251992,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,15.1762,15.2292,15.1355,15.4098,15.1762,15.1504 +float-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.79562,170052,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,4.9976,4.86785,4.9976,4.86614,5.18719,5.02087 +float-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,16.5424,169812,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,14.4495,14.4675,14.3321,14.3661,14.5592,14.4495 +float-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,9.48208,129024,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,7.71641,8.65463,7.77231,7.71641,7.68746,7.71219 +float-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,16.4394,128796,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,14.763,14.763,14.7361,14.7252,20.3118,15.0222 +float-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,14.4314,108468,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,13.1091,13.2206,13.0807,13.0911,13.1091,13.1978 +float-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,17.8942,108196,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,15.9897,16.4316,15.9897,15.6279,15.8169,16.4519 +float-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,18.0583,98176,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,16.1153,16.2038,16.0278,16.1153,16.2474,16.112 +float-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,19.7565,97844,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,17.4703,17.8682,17.3565,17.4703,17.4896,17.3423 +float-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.99421,375236,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.20774,2.30044,2.20615,2.23098,2.19488,2.20774 +float-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.68635,820076,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.88659,5.88659,5.91781,4.84445,5.76627,6.4552 +float-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.55272,251748,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.96884,2.98373,2.96884,2.79894,2.96415,2.9967 +float-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.11303,579668,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.59802,6.01701,5.59802,6.00734,5.09017,4.12866 +float-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.08028,319424,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,3.91171,4.88546,4.09903,3.69479,3.91171,3.63757 +float-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,6.56727,325612,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,4.57498,6.18172,5.22858,4.57498,4.11639,3.96875 +float-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,8.78086,200756,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.49428,6.74622,6.24309,5.49428,5.48385,5.46267 +float-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,9.96683,247656,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.59638,8.69769,5.87656,5.59638,5.51773,5.48086 +float-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,12.9391,194624,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,9.9374,9.95463,9.9374,9.54921,9.92494,10.763 +float-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,12.677,196296,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,10.5752,10.1394,10.1658,11.1325,10.5752,10.9875 +float-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,13.6853,92048,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,11.8817,11.9209,11.8795,11.7827,11.8817,12.7169 +float-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,17.4989,83404,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,17.1283,17.0706,17.1793,17.1995,17.1283,16.9091 +skew-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,10.6083,374860,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,4.71337,3.48005,4.71337,5.25899,5.2556,3.49474 +skew-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,9.57011,372496,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.19677,8.47789,8.19677,8.28334,8.09515,8.0018 +skew-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,5.51971,251916,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,3.04306,3.12064,2.91116,3.2276,3.04306,2.79712 +skew-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,9.69318,249656,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.23569,8.2341,8.24975,9.71793,8.23569,8.14515 +skew-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,5.78151,169640,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,3.38648,3.38648,3.27362,3.3721,3.47111,3.57654 +skew-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,9.44513,167736,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.07484,8.12815,8.08814,7.9734,8.07484,7.97231 +skew-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,8.53092,129080,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,5.24752,5.26827,5.15887,5.16768,5.32253,5.24752 +skew-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,9.45465,127092,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.1159,8.1159,8.05085,8.01915,8.20779,8.37463 +skew-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,10.1543,106392,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,7.67812,8.38077,7.74939,7.42459,7.52561,7.67812 +skew-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,9.75891,106148,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,7.98767,8.095,8.18517,7.98767,7.97167,7.96691 +skew-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,11.2461,95936,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,9.56156,9.68513,9.55278,9.56156,9.43881,9.74646 +skew-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,11.4574,96060,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,9.91648,9.90991,10.4988,9.91648,9.79085,9.96663 +skew,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,3.08262,368152,2fda006d290f4d1ca6efe9f1a4b3d0e68f9072a6159bfb30e323d87e16fca0ac,0.725358,0.932801,0.751743,0.667414,0.713873,0.725358 +skew,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,15.9606,858380,2fda006d290f4d1ca6efe9f1a4b3d0e68f9072a6159bfb30e323d87e16fca0ac,15.9715,15.1968,16.035,15.6615,17.5592,15.9715 +skew,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,2.75485,245836,2fda006d290f4d1ca6efe9f1a4b3d0e68f9072a6159bfb30e323d87e16fca0ac,0.905748,1.19899,0.988912,0.858472,0.905748,0.901816 +skew,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,4.83814,348224,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,4.99499,4.87338,4.7825,6.18561,4.99499,5.75221 +skew,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,2.29984,161892,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.21501,1.26668,1.21501,1.15703,1.21473,1.22606 +skew,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,3.02396,200744,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,3.34912,3.34912,2.86285,3.5871,2.79196,3.35507 +skew,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,1.74497,116412,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,0.701036,0.750907,0.701784,0.693906,0.701036,0.696733 +skew,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,1.90793,126640,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,2.15646,2.5183,1.88115,2.68351,1.82705,2.15646 +skew,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,1.20375,81528,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.03178,1.08381,1.04424,1.02949,1.03178,1.02893 +skew,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,3.08191,95908,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,2.7627,2.91693,2.85481,2.7627,2.67948,2.58055 +skew,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65537,1.77172,71180,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.49999,1.58452,1.52148,1.49999,1.49295,1.49935 +skew,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65537,4.20997,71560,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,4.18159,4.18159,4.74942,4.16383,4.17254,4.35291 +clustered,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,2.66175,369116,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.651319,0.767395,0.612002,0.651319,0.705555,0.601086 +clustered,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,125000,3.77912,534824,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,4.94647,4.22916,4.94647,5.65494,4.8107,5.0472 +clustered,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,2.5471,245372,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.730307,0.730307,0.715768,0.74333,0.743894,0.718554 +clustered,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,125000,3.07933,348252,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,4.02383,4.412,3.01299,4.52053,3.38778,4.02383 +clustered,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,2.37559,163448,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.15424,1.30155,1.30895,1.03326,0.993231,1.15424 +clustered,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,125000,2.14469,200532,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,2.24196,2.24196,1.87522,2.80015,1.8073,2.29779 +clustered,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,2.61763,118260,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.35913,1.6162,1.46666,1.35913,1.05312,0.810637 +clustered,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,125000,1.93285,126640,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.77048,1.83137,1.57425,1.96346,1.54635,1.77048 +clustered,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,1.60285,81852,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.37731,1.46751,1.37731,1.37074,1.37483,1.39262 +clustered,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,125000,3.79227,95900,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,2.71856,3.22328,3.91129,2.71856,2.57844,2.43948 +clustered,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,1.81658,71212,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.62605,1.76019,1.62605,1.59724,1.6082,1.69116 +clustered,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,125000,4.12992,70700,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,4.06086,4.09184,4.06086,4.00956,3.95091,4.14468 +sparse,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.59978,766916,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.52047,5.83573,4.9401,6.64103,5.27614,5.52047 +sparse,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.80998,770492,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.97472,5.97472,5.59318,6.98849,5.80551,6.92121 +sparse,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.92215,475540,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.64545,5.64545,5.98932,5.18609,5.6249,7.01903 +sparse,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,6.59412,470908,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,6.67176,6.32125,6.67176,6.02987,6.73316,7.57169 +sparse,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,8.51001,319560,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,7.86008,7.86008,8.29283,7.68204,7.87987,7.69049 +sparse,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,9.62928,319664,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,9.1387,9.403,9.37777,8.11032,8.53899,9.1387 +sparse,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,14.5727,188120,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,13.3995,13.7705,13.7107,13.152,13.3995,13.3732 +sparse,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,14.6574,188148,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,14.2319,14.299,14.2319,13.7065,14.4784,14.0367 +sparse,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,26.1094,136888,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,24.9,24.8887,24.9671,24.9,25.1876,24.8638 +sparse,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,27.1138,136792,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,25.875,25.9227,25.875,25.8803,25.8443,25.6108 +sparse,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,50.7418,128928,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,48.1325,48.1729,48.0503,48.1325,48.4093,47.779 +sparse,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,52.427,128384,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,49.0826,48.8574,49.2559,48.8095,49.119,49.0826 +selected-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,166200,4.72004,379104,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,3.39838,3.99615,3.28053,3.3796,3.39838,3.43611 +selected-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,166200,8.09682,379088,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,6.98613,7.48775,6.90391,6.78211,7.8709,6.98613 +selected-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,166200,4.74086,255788,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,3.58029,4.01492,3.68687,3.58029,3.3677,3.37214 +selected-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,166200,7.98095,255652,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,6.84929,7.33704,6.84929,7.36089,6.76739,6.78281 +selected-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,166200,5.0736,173896,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,4.24202,4.72839,4.24202,3.95818,4.39201,4.09363 +selected-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,166200,8.30556,174180,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,7.0829,7.76715,7.20897,6.97738,7.08208,7.0829 +selected-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,166200,6.25852,133024,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,5.69107,6.01558,5.02669,5.74792,5.69107,4.91047 +selected-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,166200,8.51019,133056,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,7.2953,7.68198,7.2953,7.27383,7.15291,7.43618 +selected-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,166200,8.04807,112212,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,6.81745,7.13226,7.02485,6.81745,6.79999,6.67339 +selected-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,166200,9.14508,112560,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,8.13637,8.84,7.90278,8.08371,8.13637,8.46842 +selected-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,166200,8.47689,102228,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,7.17894,7.57556,6.96495,7.17894,7.07462,7.37246 +selected-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,166200,10.516,102228,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,9.45375,9.73797,9.28764,9.45375,8.93462,9.61504 +selected,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.34957,358900,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,0.967615,1.00588,0.950659,0.858469,0.967615,1.04619 +selected,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.49479,399456,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.57681,2.53811,2.57681,2.43092,3.54165,2.68204 +selected,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.32136,235024,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,0.992912,1.01261,0.992912,0.964559,0.998022,0.984845 +selected,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.25417,296896,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.64517,2.91343,2.85459,2.49826,2.51755,2.64517 +selected,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.4187,153984,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.16448,1.17725,1.15757,1.16448,1.15414,1.1737 +selected,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.49937,223036,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.58257,2.57337,2.58257,2.67377,2.66252,2.38679 +selected,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.09059,112592,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.72623,1.68352,1.72623,1.80194,1.75437,1.69275 +selected,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.14204,150844,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.65103,2.74288,2.51961,2.38819,2.76447,2.65103 +selected,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.41594,91800,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.99503,3.06141,3.0032,2.99503,2.24804,2.32387 +selected,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.60234,102320,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.39645,3.39645,3.48756,3.26686,3.58612,3.38779 +selected,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.83971,71552,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.4662,3.4662,3.62687,3.53078,3.39727,3.38004 +selected,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.45002,71256,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,5.01581,4.98943,5.15456,5.04322,5.01581,4.98757 +composite,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,458752,8.09947,407732,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,2.11917,3.94643,2.26656,2.11917,1.99519,1.98939 +composite,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,458752,15.85,628680,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,13.9814,15.5142,13.9814,11.1318,16.7959,13.8996 +composite,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,458752,5.83202,284568,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,2.8223,4.00263,2.97901,2.8223,2.70173,2.60765 +composite,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,458752,14.438,406924,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,13.3232,12.9816,11.9986,13.3232,13.923,13.3808 +composite,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,458752,6.5492,202568,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,3.59671,4.96585,3.99719,3.59671,3.38557,3.40401 +composite,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,458752,18.3378,288672,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,12.7104,14.865,15.1505,12.7104,12.1275,11.4221 +composite,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,458752,10.2996,161792,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,7.25574,8.21523,7.25574,7.30225,6.19413,6.00061 +composite,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,458752,18.9278,198428,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,14.4514,14.2482,14.0889,15.0735,14.4514,14.5895 +composite,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,458752,12.0937,140944,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,9.94601,9.98792,9.96081,9.94601,9.92929,9.87944 +composite,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,458752,20.7051,153304,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,17.9414,17.9414,17.1428,18.3119,18.286,17.7039 +composite,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,458752,15.2645,137112,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,11.4023,11.6423,11.3483,11.4023,11.2532,11.9461 +composite,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,458752,28.1179,130424,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,24.7237,25.2433,24.3534,24.7237,24.7757,24.1725 +distinct,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.54016,541212,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,4.51697,3.63494,4.51697,4.64513,3.90292,4.95391 +distinct,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,21.5251,871496,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,12.7154,18.2358,13.0839,11.7321,12.7154,11.8332 +distinct,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.10982,413820,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,4.79417,5.00398,4.79417,4.15206,4.90262,4.78954 +distinct,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,11.8043,574304,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,14.1603,14.1603,13.7117,15.1974,16.6525,12.4917 +distinct,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.94185,325932,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,5.89978,5.88237,5.89978,5.84314,6.50505,6.84647 +distinct,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,14.5872,318328,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,10.983,13.3426,11.1413,10.7994,10.983,10.8002 +distinct,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,12.3263,200596,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,8.63684,9.68538,8.84069,8.60392,8.58203,8.63684 +distinct,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,16.4544,189456,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,14.1693,14.475,14.1693,13.6898,14.1902,13.7325 +distinct,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,20.641,151460,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,18.0085,18.0085,18.3607,22.5797,17.726,17.013 +distinct,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,25.3826,197708,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,22.0173,20.4909,22.8686,22.6652,20.2675,22.0173 +distinct,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,51.8757,202328,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,46.6439,45.7679,46.9184,53.921,46.5784,46.6439 +distinct,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,60.1221,202120,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,65.0266,61.8933,65.3039,67.1745,63.4775,65.0266 +mixed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.62206,375296,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,2.39691,3.59433,2.33875,2.35587,2.42959,2.39691 +mixed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,9.91131,375016,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.76293,8.90196,8.45999,8.76293,8.94383,8.40338 +mixed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.81738,251552,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,3.18613,3.63062,3.11166,3.26821,3.02465,3.18613 +mixed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.6374,251460,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,9.53252,9.59082,9.21742,9.53252,8.90805,9.88826 +mixed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.56724,169420,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,4.19372,5.28892,5.3063,4.1132,4.19372,3.80469 +mixed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,9.89705,169684,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.16653,8.66157,8.16653,8.14384,8.03247,8.24166 +mixed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.68952,128996,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,5.59224,5.72922,5.51411,5.9169,5.5473,5.59224 +mixed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.0216,128984,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.5155,8.97608,9.03149,8.5155,8.44275,8.48087 +mixed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,10.249,108460,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.63315,8.63315,8.74266,8.33704,8.78502,8.15964 +mixed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,12.464,108192,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,9.90548,12.1083,11.3857,9.48726,9.90548,9.53676 +mixed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,11.9352,97956,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,10.4561,10.6928,10.4632,10.4561,10.0538,10.3292 +mixed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,13.0948,98208,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,11.4894,11.8809,11.4894,11.6445,11.2959,11.1359 +first-last,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.84119,374996,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,1.73831,2.9239,1.73831,1.73196,1.77345,1.64261 +first-last,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.9762,368868,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.79223,9.85424,9.67403,9.59329,10.0393,9.79223 +first-last,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.2632,252024,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,2.29957,2.70859,2.29957,2.13695,2.42436,2.11422 +first-last,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,11.5219,245820,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.7397,10.2264,9.52568,9.7397,9.59296,10.1261 +first-last,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.00039,169756,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,2.80826,2.75559,2.85507,2.62721,2.83073,2.80826 +first-last,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,11.1008,163672,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.89955,10.0775,9.89955,10.1077,9.7359,9.53899 +first-last,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.00619,128608,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,3.89115,3.92095,3.89115,3.90688,3.87792,3.7814 +first-last,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,11.0356,122364,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.73837,9.86271,10.166,9.63435,9.73837,9.60889 +first-last,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,8.70963,102096,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,4.93795,5.06517,5.1684,4.85304,4.74799,4.93795 +first-last,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,11.2701,101828,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,10.174,10.174,9.70986,10.1779,10.528,9.57707 +first-last,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.60903,91808,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,5.21215,5.55195,5.21215,5.11864,5.468,5.10579 +first-last,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,11.0288,91508,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.85053,9.92692,9.9003,9.39089,9.85053,9.46867 +top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,7.31707,380932,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,4.43491,4.22939,4.43491,5.34729,4.18389,4.47728 +top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.3461,381408,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,8.87715,9.37676,8.87715,9.29447,8.83777,8.86471 +top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.87216,258184,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,5.07571,5.15529,4.87027,5.80514,5.07571,4.71413 +top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.2523,257508,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,8.92543,10.0475,9.29022,8.75729,8.90672,8.92543 +top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,8.09414,175568,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,6.24956,6.72716,6.24956,6.52159,5.72037,5.71026 +top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.3731,176196,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,8.72913,10.3173,8.92493,8.38063,8.72913,8.49426 +top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,9.02819,135136,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,8.36305,8.36305,8.57733,8.65868,7.81342,7.99408 +top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.7378,135380,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,9.24068,10.6762,9.46029,8.84124,8.85347,9.24068 +top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,12.0289,114456,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,11.4129,11.4777,11.1685,11.4129,11.7297,10.7369 +top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,12.0317,114380,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,11.9175,11.9191,11.9977,11.9175,11.4684,10.9422 +top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,16.0986,104320,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,15.6211,16.3058,15.9047,15.6211,15.341,14.3958 +top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,16.155,104076,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,14.8611,15.819,15.0866,14.8611,14.7592,14.8558 +mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.71451,374572,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,2.23944,3.48196,2.31784,2.0973,2.23944,2.13492 +mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.01364,368424,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,6.94528,6.88762,6.95285,6.75859,6.94528,7.02072 +mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.60352,251608,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,3.0729,3.31049,3.0729,3.28142,2.84551,2.88455 +mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.51642,245244,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,7.34901,7.35795,6.51482,7.55824,6.74686,7.34901 +mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.25398,170096,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,4.64923,5.07012,4.77405,4.64923,4.43385,4.43518 +mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.05897,163768,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,6.95263,7.10525,7.08464,6.67489,6.95263,6.69127 +mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,7.63022,129128,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,6.32469,6.32469,6.57744,6.69177,5.89438,6.18471 +mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.78904,122552,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,7.28926,7.40124,7.36071,7.24954,7.28926,7.08548 +mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,10.5736,102024,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,9.27414,9.38319,9.29343,8.58878,9.27414,8.91766 +mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.7935,101828,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,9.9706,10.4738,9.56113,9.64168,9.9706,10.0277 +mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,14.309,91860,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,13.6757,14.329,12.5551,13.6362,13.6757,13.966 +mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,15.4251,91780,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,14.3276,15.0839,14.1373,14.3276,13.9689,14.6474 +quantile,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.07145,375288,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,1.91313,3.2638,1.90899,1.91313,1.90819,2.01198 +quantile,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,7.26129,375124,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,6.11016,6.43682,5.66927,6.11016,6.16337,6.04624 +quantile,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.32685,251504,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,2.54399,2.98285,2.68353,2.54399,2.4026,2.37656 +quantile,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,7.62052,251608,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,6.36227,6.94519,6.36871,6.19867,6.1241,6.36227 +quantile,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.13021,169920,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,3.32266,3.32678,3.37387,3.1921,3.17036,3.32266 +quantile,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,7.37716,169436,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,6.08637,6.17483,6.08637,6.25368,5.66742,5.60655 +quantile,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.80635,128988,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,4.82602,5.08931,4.80275,4.82021,4.84662,4.82602 +quantile,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,7.26203,129144,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,6.12741,6.12741,6.27726,6.39966,6.05127,6.03018 +quantile,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,8.68974,108604,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,7.29838,7.36488,7.29838,7.34771,7.20357,7.01206 +quantile,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.39108,108716,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,7.0466,7.28552,7.0466,6.83345,6.89185,7.0853 +quantile,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,11.3865,98192,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,9.40562,9.52272,9.28828,9.40562,9.69792,9.16433 +quantile,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.6932,98148,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,9.02098,9.55469,9.09483,8.89676,9.02098,8.92256 +median,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.3917,374764,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,2.15328,3.31445,2.32564,2.15328,2.05407,2.10179 +median,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,7.50485,374324,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,7.67243,9.89522,7.67243,8.80414,6.33963,5.81914 +median,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.20977,251756,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,2.70634,3.11541,2.70634,2.54216,2.73107,2.64816 +median,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,7.45873,251404,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,6.1368,6.65073,6.64796,6.06524,6.1368,6.08354 +median,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.95813,169108,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,2.96109,2.78687,2.64981,2.96109,3.55632,4.36741 +median,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,6.94981,170180,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,6.14112,6.42135,5.97819,6.15376,6.14112,5.80425 +median,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.97627,128796,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,4.79373,5.0143,4.7026,4.79373,4.93503,4.69965 +median,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,7.96551,128936,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,6.30041,6.65859,6.54584,6.24646,6.05916,6.30041 +median,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,9.04538,108368,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,7.49081,8.48638,8.13322,7.49081,6.91149,6.67129 +median,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.39062,108440,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,6.94624,7.16878,6.85249,7.75199,6.94624,6.87107 +median,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,10.6128,97976,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,8.97765,10.6125,13.2593,8.78251,8.97765,8.83906 +median,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,12.3794,97716,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,8.54634,11.0634,10.5691,8.51077,8.54634,8.49308 +product,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.61432,370192,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.19956,3.34138,2.19956,2.0964,2.14004,2.35633 +product,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.39619,751920,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,7.97594,5.84541,8.47614,6.043,8.16667,7.97594 +product,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.90349,247528,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.01769,1.10676,1.0528,1.01769,0.983338,0.958732 +product,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.62584,487244,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,15.6653,16.8481,19.0319,13.2754,15.6653,12.0426 +product,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.53157,165572,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.40885,1.48147,1.38906,1.37972,1.40885,1.41288 +product,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.08916,257756,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,3.32658,3.32658,3.61701,3.19641,2.72728,3.61852 +product,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.900089,102292,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,0.75735,0.75735,0.754286,0.735153,0.802852,0.870894 +product,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.81324,151016,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.22481,2.22481,2.5689,2.23757,1.94867,1.26052 +product,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.5407,81560,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.31084,2.35822,2.32251,2.31084,1.3139,1.11585 +product,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.63577,97892,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,3.16219,3.16219,2.86985,2.90323,3.4749,3.16238 +product,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.05807,71448,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.80061,1.79145,1.78954,1.82787,1.83201,1.80061 +product,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.94287,70748,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.65909,2.77733,2.65909,2.65893,2.62079,2.72095 +truth,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.61002,370928,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.00395,1.04987,1.00395,0.992519,0.978776,1.01371 +truth,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,9.02567,786488,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,8.33102,8.72828,8.33102,7.38787,6.4665,10.4689 +truth,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.10732,247376,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.36169,1.44631,1.36169,1.35435,1.32204,1.45592 +truth,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.68395,574940,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,8.54061,8.54061,9.56166,8.21552,8.61424,8.49799 +truth,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.65623,165288,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.97501,1.86423,2.00315,1.93528,1.97501,1.97752 +truth,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.85719,300756,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,4.26723,4.26723,3.90693,5.13002,4.68352,3.72796 +truth,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.0922,124656,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.72791,1.43997,1.43815,1.72791,3.02267,1.87266 +truth,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.31672,168960,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,2.97258,3.07636,3.19667,2.45715,2.97258,2.69311 +truth,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.75654,93916,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,2.62518,2.72117,2.62518,2.65732,2.32636,2.2929 +truth,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.0247,104116,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,3.61075,3.95971,3.61075,3.37319,3.84711,3.42033 +truth,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.10344,71004,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,3.82288,3.82288,3.96004,4.01333,3.77907,3.72775 +truth,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.95297,70816,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,5.39496,5.45115,5.4057,5.38679,5.38831,5.39496 +binary,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.70244,391776,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.34808,3.39033,2.34808,2.30887,2.39481,2.33028 +binary,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,19.6249,975144,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,17.818,18.2125,20.0357,17.818,17.6569,16.9354 +binary,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.95318,267912,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,3.20203,3.60772,3.33457,3.20203,3.05941,3.0092 +binary,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,23.305,590248,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,22.0336,23.1056,21.9073,22.1042,21.7251,22.0336 +binary,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.01083,186412,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,4.71506,3.78353,5.08377,4.79982,4.71506,4.06371 +binary,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,39.1694,384948,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,37.5576,37.7946,35.925,38.1251,37.5576,37.0208 +binary,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.98811,146236,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,4.66812,4.66812,4.83371,5.0257,4.55573,4.56554 +binary,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,74.285,333124,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,69.7511,70.1693,69.7511,70.0495,69.5092,69.7118 +binary,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,12.0254,125652,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,9.0343,9.0343,8.77185,8.98104,9.08273,9.32156 +binary,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,18.0474,138380,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,14.7891,14.7891,12.167,15.1816,14.3572,15.2467 +binary,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,16.2711,116200,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,13.3326,13.2829,14.0174,12.4433,13.3326,13.4394 +binary,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,22.1565,120168,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,19.4017,19.6987,19.3352,19.251,19.5473,19.4017 +statistics,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.41113,374424,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,1.31332,1.835,1.28588,1.31332,1.29679,1.36268 +statistics,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.66844,917836,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,9.82609,9.62313,9.89072,9.82609,11.6436,9.56054 +statistics,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.70056,251744,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,1.67178,2.17806,1.74649,1.67178,1.63972,1.6586 +statistics,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,11.2918,581676,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,11.5605,11.896,12.1685,11.1259,11.5605,10.0163 +statistics,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.1802,170128,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.31985,2.69685,2.26778,2.41688,2.16398,2.31985 +statistics,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,7.07728,257692,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,4.79875,4.30327,4.79875,3.80299,4.95298,4.9764 +statistics,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.02174,128704,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.49363,2.49363,2.41232,2.5565,2.51454,2.43797 +statistics,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.87303,165360,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,3.6614,3.96144,3.97796,3.6614,3.51517,3.46142 +statistics,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.40174,104020,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.93826,3.02018,2.93826,2.9249,2.91084,3.02544 +statistics,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,6.68112,111812,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,5.37813,5.82588,5.42221,5.37813,5.24579,5.15284 +statistics,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.01174,85628,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,5.03334,5.04346,5.03334,4.78305,4.67825,5.34689 +statistics,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,10.4227,87684,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,9.12161,9.12161,9.12708,9.07944,8.98944,9.63369 +count,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.797195,348440,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.568314,0.586059,0.558879,0.528483,0.568314,0.789352 +count,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.40525,514352,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,3.12832,3.03612,4.2567,3.12832,3.12652,5.66101 +count,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.862873,224856,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.07863,0.738077,0.699825,1.28022,1.30904,1.07863 +count,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.97783,348212,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,5.34109,5.59499,4.91296,6.2835,5.03211,5.34109 +count,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.41452,143012,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.06093,1.19163,1.15015,1.06093,0.988381,0.997968 +count,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,1.46607,200428,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.5962,1.64491,1.25746,1.79839,1.23385,1.5962 +count,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.890332,102092,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.731622,0.765846,0.733147,0.731622,0.725878,0.728055 +count,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,1.49805,126732,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.42286,1.42475,1.19607,1.55373,1.20114,1.42286 +count,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.26085,81564,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.11621,1.1225,1.1167,1.11447,1.11621,1.11194 +count,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.1081,96136,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.79808,1.76094,2.01526,1.79808,1.93662,1.72141 +count,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.00243,71024,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.66156,1.68529,1.65985,1.66156,1.66342,1.66131 +count,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.83967,71536,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,2.70349,2.74002,2.69525,2.70349,2.69798,2.70473 +extrema-time,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.2009,381164,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,0.975341,1.03548,0.975341,0.977562,0.9634,0.931355 +extrema-time,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.29501,821784,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,6.0584,5.29714,6.44482,4.99495,6.0584,8.12011 +extrema-time,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.51864,258044,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,1.26968,1.26753,1.21368,1.33039,1.34094,1.26968 +extrema-time,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.12448,559208,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,4.88719,4.8038,5.48605,4.08914,4.88719,5.30403 +extrema-time,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.23773,175660,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,1.4454,1.47183,1.4454,1.44573,1.43284,1.43912 +extrema-time,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.63579,333512,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,3.43526,3.62101,3.35724,4.41491,3.43526,3.08713 +extrema-time,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.42629,134552,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,1.86655,2.36437,2.36756,1.86655,1.51179,1.72445 +extrema-time,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.35032,202428,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.26375,2.96466,2.736,1.93874,2.26375,2.03733 +extrema-time,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.04083,114320,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,1.83252,1.8945,1.83107,1.83252,1.83393,1.83216 +extrema-time,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.92891,136688,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.62207,2.92989,2.74038,2.3993,2.62207,2.45952 +extrema-time,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.03932,103772,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.7832,2.75615,2.74385,2.85252,2.84963,2.7832 +extrema-time,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.17433,103604,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,3.7007,3.7007,3.6993,3.72646,3.71611,3.6937 +sum-time,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.924017,381096,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,0.647245,0.917457,0.61328,0.65907,0.590237,0.647245 +sum-time,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.89256,776768,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,5.19373,5.19373,4.79421,6.6825,4.78131,7.36168 +sum-time,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.21446,258120,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,0.957766,0.912894,0.993007,0.950749,1.07153,0.957766 +sum-time,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.419,380752,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,4.99328,4.99328,5.16638,5.84579,4.81785,4.82541 +sum-time,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.51064,194372,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.09803,1.24931,1.17344,1.09803,1.08866,1.08216 +sum-time,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.60146,232712,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,2.79129,2.7937,2.46501,3.06852,2.37901,2.79129 +sum-time,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.55576,135008,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.42086,1.42452,1.43644,1.42086,1.37994,0.777345 +sum-time,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,1.44973,159748,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.40797,1.43197,1.20924,1.57589,1.2234,1.40797 +sum-time,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.30562,114384,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.1196,1.13491,1.12198,1.1196,1.11652,1.11531 +sum-time,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.12834,128400,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,2.01221,1.82692,2.23515,2.22817,2.01221,1.79862 +sum-time,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.90925,104048,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.69125,1.69365,1.66197,1.69125,1.70026,1.67791 +sum-time,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.0503,103848,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,2.75243,2.8712,2.7511,2.73598,2.76809,2.75243 +extrema-f64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.5954,370708,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.923717,1.16131,0.954617,0.902499,0.883248,0.923717 +extrema-f64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.25926,790848,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,8.9053,8.9053,9.35622,8.38922,8.27391,11.6131 +extrema-f64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.29695,247436,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.79788,1.93871,1.79788,1.33175,1.59329,2.57698 +extrema-f64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,9.27134,575276,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,8.76005,8.87633,9.45591,8.23747,8.76005,8.50542 +extrema-f64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.82309,165596,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.45302,2.45302,2.55468,2.63667,1.65958,2.06841 +extrema-f64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.65861,300572,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.84827,3.99095,3.54934,4.72365,3.84827,3.35861 +extrema-f64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.5319,124748,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.36961,1.31247,1.50202,1.44523,1.26825,1.36961 +extrema-f64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.81263,169612,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.9827,3.9893,3.32079,2.5558,2.9827,2.63619 +extrema-f64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.08568,93884,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.98619,2.02,1.98619,1.98748,1.98505,1.98391 +extrema-f64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.58139,104304,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.49547,3.72614,3.49547,3.24996,3.68924,3.47196 +extrema-f64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.25216,71384,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.10238,3.15459,3.07671,3.21897,3.10238,3.09198 +extrema-f64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.74927,71000,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.46037,5.46037,5.35337,6.66895,5.99613,5.32497 +sum-f64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.9894,370964,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.754219,1.37078,0.688586,0.754219,0.73999,0.942628 +sum-f64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.94291,766280,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.56726,5.56726,5.0614,7.51961,6.28563,5.2838 +sum-f64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.723,247624,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.1375,1.53324,1.0718,1.05412,1.15465,1.1375 +sum-f64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.34072,348288,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.25893,5.07749,5.22207,6.88146,5.25893,5.72542 +sum-f64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.43998,161744,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.15863,1.15863,1.08147,1.08235,1.18735,1.18545 +sum-f64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,1.85443,200376,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.61604,1.7054,1.45971,1.9077,1.4241,1.61604 +sum-f64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.93962,101984,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.809736,0.828678,0.809337,0.809736,0.815341,0.80921 +sum-f64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,1.43863,126452,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.46925,1.54338,1.33473,1.6803,1.31569,1.46925 +sum-f64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.65832,81368,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.22185,1.28665,1.22185,1.15685,1.15131,1.32748 +sum-f64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.09335,96144,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.2634,1.80262,2.2634,2.89838,2.8813,1.77695 +sum-f64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.88394,71292,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.70614,1.70988,1.69234,1.68629,1.70614,1.71647 +sum-f64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.12794,71268,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.88778,3.02277,2.97867,2.88778,2.78092,2.76127 +extrema-f32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.92756,360668,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.87116,0.85205,0.87116,0.881467,0.856569,0.906978 +extrema-f32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.65287,801356,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,7.79704,6.7429,9.42298,6.14019,8.35993,7.79704 +extrema-f32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.08833,236996,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.29961,1.33538,1.28585,1.32147,1.28076,1.29961 +extrema-f32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.39288,530520,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,6.04386,5.88434,6.04386,5.18319,6.76619,6.73272 +extrema-f32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.6789,155396,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.72914,1.72852,1.68259,1.72914,2.02746,1.79984 +extrema-f32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.82842,300144,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.75369,4.28539,3.42046,4.55247,3.74214,3.75369 +extrema-f32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.56584,124712,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.28263,2.84838,2.28263,2.30599,2.26886,2.1442 +extrema-f32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.68409,169332,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.68963,2.78219,3.15018,2.40752,2.68963,2.43736 +extrema-f32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.24371,81288,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.05811,2.20275,2.01435,1.95322,2.05811,2.10261 +extrema-f32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.70369,104064,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.5222,3.78582,3.5222,3.26729,3.57192,3.44569 +extrema-f32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.52121,71296,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.1462,3.24443,3.15485,3.13725,3.1462,3.12207 +extrema-f32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.67338,71276,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.37186,5.37186,5.3504,5.28032,5.48491,5.54542 +sum-f32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.888018,348124,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.691866,0.739685,0.691866,0.739854,0.689387,0.671593 +sum-f32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.96294,747876,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.03726,5.03726,4.40303,6.68865,4.95445,6.01051 +sum-f32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.937978,224516,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.999886,0.999886,1.02859,0.932288,1.04792,0.895696 +sum-f32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.54834,348148,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.09278,6.09278,5.33504,6.94672,5.47155,6.179 +sum-f32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.5972,161628,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.41262,1.41262,1.54558,1.38004,1.39461,1.41766 +sum-f32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.79292,200896,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.87896,2.87896,2.61837,3.52686,2.68061,3.02558 +sum-f32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.57417,102120,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.764382,1.48574,0.888051,0.75212,0.764382,0.743456 +sum-f32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,1.48593,126484,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.44818,1.49662,1.28666,1.64737,1.28436,1.44818 +sum-f32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.27782,81496,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.1417,1.1814,1.14442,1.1417,1.14017,1.13831 +sum-f32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.18041,95888,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.85474,1.85474,2.08335,1.79023,1.91768,1.7868 +sum-f32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.14713,71276,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.69225,1.75727,1.71077,1.69225,1.68824,1.69164 +sum-f32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.01292,71392,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.80677,2.80413,2.78188,2.84321,2.80677,2.8083 +extrema-i64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.16176,371016,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.971834,1.04902,0.971834,0.942597,0.944018,0.993003 +extrema-i64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,6.83421,802864,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,7.77039,7.77039,8.47414,6.89221,6.71882,9.52796 +extrema-i64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.05314,247504,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.28618,1.29688,1.27874,1.40228,1.232,1.28618 +extrema-i64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,8.78043,579640,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,9.09246,8.67506,9.76121,8.56066,9.14221,9.09246 +extrema-i64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.79173,165664,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.93343,1.52692,1.83331,1.93343,2.13922,2.10999 +extrema-i64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.8067,301104,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.66589,3.79143,3.64391,4.96532,3.66589,3.4032 +extrema-i64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.56828,124940,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.72057,1.72057,2.21025,1.81319,1.31058,1.69863 +extrema-i64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.57333,169956,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.86292,3.32689,3.26614,2.54439,2.86292,2.65768 +extrema-i64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.28004,93628,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.24491,2.19191,2.75469,2.22735,2.24491,2.31182 +extrema-i64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.70684,103788,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.51387,3.60154,3.51387,3.24539,3.77843,3.45762 +extrema-i64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.30825,70972,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.07238,3.14826,2.98315,3.01774,3.10928,3.07238 +extrema-i64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.35731,71268,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.1923,5.22144,5.1923,5.17655,5.18673,5.27728 +sum-i64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.10859,370696,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.702752,1.38441,0.748274,0.702752,0.674324,0.653207 +sum-i64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.12699,763972,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.0133,5.61838,6.36595,7.23187,5.67204,6.0133 +sum-i64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.56608,247540,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.984199,1.30695,0.984199,0.824578,0.936366,0.993575 +sum-i64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.07437,348508,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.27388,5.8196,5.08732,6.54611,4.12997,5.27388 +sum-i64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.23437,161364,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.08148,1.00027,1.0913,1.08148,1.01892,1.11996 +sum-i64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.84855,200352,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.8754,3.1514,2.57329,3.25686,2.6927,2.8754 +sum-i64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.973966,102384,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.757089,0.809235,1.40125,0.757089,0.746738,0.744058 +sum-i64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,1.38438,126496,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.42855,1.43992,1.24249,1.72231,1.24567,1.42855 +sum-i64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.24817,81512,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.13033,1.13196,1.13033,1.13678,1.13011,1.12637 +sum-i64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.07136,96052,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.77488,1.77488,2.00325,1.7366,1.87778,1.72613 +sum-i64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.85004,71196,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.69983,1.69983,1.71458,1.68867,1.69043,1.73537 +sum-i64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.92031,70612,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.75781,2.7425,2.80392,2.80528,2.75781,2.74181 +extrema-i32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.13122,348652,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.919257,0.935951,0.91528,0.866751,0.919257,0.968624 +extrema-i32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.8832,788544,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,7.41861,6.50663,8.35458,5.59862,7.41861,8.37975 +extrema-i32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.29021,225132,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.19915,1.17125,1.18421,1.19915,1.22023,1.25106 +extrema-i32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.98587,532536,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,4.99137,4.55591,5.43937,4.99137,4.09104,5.17155 +extrema-i32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.1818,142960,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.96222,2.20113,1.85387,2.07383,1.96222,1.45335 +extrema-i32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.62409,300456,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.65816,4.00686,3.53932,4.46305,3.65816,3.23512 +extrema-i32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.39832,101636,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.2373,1.25187,1.24952,1.2355,1.2373,1.23658 +extrema-i32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.33044,169384,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.36885,2.48579,2.93913,2.17161,2.36885,2.18262 +extrema-i32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.96566,81556,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.85071,2.05727,1.88093,1.85071,1.82868,1.83981 +extrema-i32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.88071,103652,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.78975,2.9305,2.79102,2.47578,2.78975,2.57156 +extrema-i32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.02533,71036,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.76837,2.8245,2.77742,2.7582,2.74379,2.76837 +extrema-i32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.12055,70832,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.71278,3.71278,3.67871,3.68305,3.73933,3.74778 +sum-i32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.842703,348148,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.715552,0.754739,0.715552,0.713772,0.68151,0.738751 +sum-i32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.47484,751916,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.56176,5.56176,5.35696,6.27989,4.95183,6.07187 +sum-i32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.964804,225092,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.936073,0.951577,0.936073,0.904823,0.950654,0.934694 +sum-i32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.8718,348052,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.69508,5.69508,5.14861,6.31574,5.43827,6.21628 +sum-i32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.60714,161364,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.38774,1.55758,1.40349,1.38774,1.3862,1.26794 +sum-i32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.67614,200368,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.63331,2.90275,2.26228,3.09916,2.29427,2.63331 +sum-i32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.64107,102084,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.43105,1.43194,1.42512,1.43105,1.43074,1.48586 +sum-i32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,1.94087,126024,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.82137,1.96774,1.82137,2.04479,1.38007,1.50027 +sum-i32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.27422,80948,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.16606,1.17496,1.19838,1.13067,1.16606,1.12559 +sum-i32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.11253,95912,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.84076,1.79584,2.55342,1.82202,1.93993,1.84076 +sum-i32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.86823,71280,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.68166,1.69269,1.68166,1.66428,1.69881,1.67851 +sum-i32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.86588,70808,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.73827,2.79723,2.78415,2.73827,2.72219,2.72951 +extrema-i16,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.17709,347936,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.900764,0.900764,0.897055,0.992585,0.973387,0.873616 +extrema-i16,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.602,796684,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,6.44464,5.78114,6.44464,4.98364,6.46296,6.74754 +extrema-i16,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.17425,224776,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.16544,1.2994,3.16544,2.95966,3.8137,3.35388 +extrema-i16,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.0393,519956,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,6.08605,6.08605,5.9898,5.54262,6.48455,7.51152 +extrema-i16,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.55702,143264,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.13506,2.23366,2.01249,2.13506,2.01993,2.17019 +extrema-i16,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.64175,300860,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,4.10445,4.10445,3.64589,4.7853,5.21404,4.0091 +extrema-i16,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.42785,102020,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.23821,1.2668,1.23821,1.31227,1.22483,1.22232 +extrema-i16,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.29553,169156,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.28046,2.28046,2.61317,1.98184,2.20345,2.29276 +extrema-i16,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.97652,81804,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.82799,1.85831,1.83451,1.82799,1.80963,1.81279 +extrema-i16,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.82853,104064,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.79853,2.90894,2.75846,2.49075,2.79853,2.92257 +extrema-i16,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.90114,71016,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.71614,2.72907,2.69241,2.71407,2.71883,2.71614 +extrema-i16,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.80808,71284,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.68698,3.60928,3.64499,3.68698,3.72814,3.81096 +sum-i16,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.869246,347868,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.768048,0.768048,0.777278,0.68474,0.679888,0.811542 +sum-i16,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.2152,755784,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.39098,6.39098,4.57105,7.33007,5.58107,6.56002 +sum-i16,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.986881,225248,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.911208,0.912042,0.894263,0.911208,0.803985,0.988404 +sum-i16,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.42644,348296,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.0847,4.67897,4.8598,6.37527,5.0847,5.85372 +sum-i16,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.40602,161372,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.24192,2.26613,1.28126,1.2111,1.24192,1.2126 +sum-i16,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,1.511,200556,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.64152,1.64152,1.29194,2.70232,1.27219,1.64916 +sum-i16,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.846091,102064,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.736604,0.748681,0.732001,0.736604,0.738522,0.736319 +sum-i16,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.20243,126708,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.99741,3.05799,2.76709,3.31638,2.76455,2.99741 +sum-i16,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.24207,81540,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.1199,1.13778,1.1199,1.11996,1.11319,1.11927 +sum-i16,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.07411,95808,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.86035,1.74863,2.00661,1.72341,1.86035,1.86974 +sum-i16,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.82042,71052,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.67148,1.69007,1.67148,1.66134,1.66541,1.67806 +sum-i16,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.91036,71188,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.76208,2.81203,2.77805,2.76208,2.75833,2.73814 +extrema-u8,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.05219,348400,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,0.855764,0.836867,0.8609,0.846591,0.888436,0.855764 +extrema-u8,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.80561,790572,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,6.7347,5.2319,6.82257,4.39539,6.7347,7.71056 +extrema-u8,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.32113,224744,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,1.26912,1.201,1.30633,1.29195,1.26912,1.26595 +extrema-u8,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.71626,513848,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,4.82435,4.16887,5.11972,4.82435,3.80908,5.49646 +extrema-u8,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.02895,143428,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,1.80402,1.80402,1.79317,1.86074,1.7852,1.92837 +extrema-u8,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.48981,300752,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,3.1674,3.49894,2.73813,3.796,3.1674,2.89866 +extrema-u8,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.6361,102084,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,1.2945,1.47359,1.30044,1.29007,1.2945,1.29072 +extrema-u8,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.12289,169988,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.0939,2.15733,2.45101,1.86311,2.0939,1.84087 +extrema-u8,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.00317,81724,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,1.84599,1.87002,1.84599,1.83977,1.8443,1.84648 +extrema-u8,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.57568,104076,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.44941,2.90392,2.60822,2.25737,2.44941,2.3773 +extrema-u8,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.04867,71276,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.73332,2.75927,2.73332,2.73256,2.72158,2.7545 +extrema-u8,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.34432,71176,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,3.01021,2.94638,3.01021,2.94242,3.04458,3.04415 +sum-u8,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.75569,348396,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,0.679529,0.716696,0.673799,0.679529,0.653553,0.690485 +sum-u8,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.94519,749612,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,5.29948,5.02205,4.59029,6.2215,5.29948,7.44393 +sum-u8,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.927987,225228,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,0.83367,0.805128,0.797132,0.834755,0.849474,0.83367 +sum-u8,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.11795,347876,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,5.52286,5.52798,4.73587,5.52286,4.93838,5.55951 +sum-u8,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.62583,143360,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.32182,1.43002,1.31926,1.32246,1.32182,1.09651 +sum-u8,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.47299,200668,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,2.20252,2.70251,2.20252,2.8494,1.51483,1.56129 +sum-u8,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.5066,102108,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,0.736886,1.28151,0.738036,0.733264,0.736886,0.734921 +sum-u8,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.10796,126748,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,2.14737,2.14737,1.91598,2.45181,1.92735,2.15019 +sum-u8,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.23519,81572,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.11762,1.14103,1.12586,1.11724,1.11762,1.1149 +sum-u8,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,1.96725,95584,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.82254,1.70442,1.95663,1.69969,1.82254,1.90082 +sum-u8,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.8083,71276,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.67085,1.67085,1.66259,1.66308,1.67213,1.71765 +sum-u8,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.88014,71292,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,2.70509,2.70509,2.70326,2.76055,2.71211,2.69304 +extrema-bool,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.945417,348140,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,0.80697,0.803258,0.825097,0.841493,0.80697,0.791611 +extrema-bool,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.80797,790684,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,5.25158,5.05746,6.57739,4.43157,5.25158,7.79459 +extrema-bool,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.17772,225240,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.11937,1.09229,1.11937,1.10879,1.14229,1.15474 +extrema-bool,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.67069,515968,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,4.36614,4.36614,4.75671,3.9977,3.97283,5.25675 +extrema-bool,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.60531,143080,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.85889,1.80673,1.85889,1.87266,1.89122,1.80144 +extrema-bool,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.64507,300672,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,3.27256,4.10794,3.27256,4.36265,2.97388,2.73699 +extrema-bool,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.28104,102064,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.15341,1.17256,1.1591,1.14426,1.15219,1.15341 +extrema-bool,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.65964,169240,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,2.96493,3.14329,3.63847,2.55086,2.96493,2.60809 +extrema-bool,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.52555,81512,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.74829,1.74829,1.72983,1.74339,1.87231,1.77865 +extrema-bool,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.68893,103572,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,2.35797,2.57029,2.36224,2.02374,2.35797,2.27507 +extrema-bool,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.88251,71256,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,2.62198,2.65734,2.62198,2.63608,2.61404,2.61314 +extrema-bool,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.13015,70836,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,2.97816,2.9457,3.02856,3.04951,2.97816,2.95791 +sum-bool,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.831388,348172,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,0.672871,0.691153,0.672871,0.613557,0.708793,0.595071 +sum-bool,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.92407,753984,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,5.20232,4.96546,4.19663,6.28789,5.20232,6.16909 +sum-bool,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.909349,225260,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,0.936126,0.936126,0.753927,0.971002,0.76979,0.939233 +sum-bool,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.23555,348044,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,5.74788,5.74788,5.27354,6.57953,5.13441,5.93996 +sum-bool,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.10695,142732,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.39947,1.35875,1.34037,1.436,1.44179,1.39947 +sum-bool,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.1498,200380,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,3.0453,3.39418,3.0453,3.77519,2.93916,2.61833 +sum-bool,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.82576,102124,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,0.753534,0.753534,0.739168,0.739026,1.07206,1.999 +sum-bool,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,1.41542,126556,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.44454,1.44454,1.20631,1.57519,1.42361,1.46572 +sum-bool,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.24141,81812,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.11588,1.12247,1.11875,1.11588,1.11549,1.11542 +sum-bool,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.02529,95920,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.81046,1.81046,2.36575,1.73055,1.83194,1.69342 +sum-bool,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.92035,71228,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.67891,1.72891,1.68768,1.66856,1.67458,1.67891 +sum-bool,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.85819,71184,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,2.71166,2.69239,2.71616,2.71166,2.73272,2.68969 +sum-bool,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.85115,71036,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,2.70623,2.70623,2.80431,2.70285,2.73661,2.68738 +sum-bool,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.86115,71160,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.6701,1.68788,1.66897,1.67146,1.66664,1.6701 +sum-bool,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.19218,95888,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.85568,1.85568,2.08406,1.79097,1.92197,1.78767 +sum-bool,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.22261,81452,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.12262,1.14133,1.12576,1.12262,1.11685,1.11528 +sum-bool,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.11658,126536,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.83434,2.23684,2.03161,1.83434,1.19602,1.35643 +sum-bool,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.63984,101916,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,1.45734,1.39269,1.46063,1.45734,1.46087,1.2976 +sum-bool,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.40196,200392,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,3.10493,4.79955,2.79892,3.99594,2.6453,3.10493 +sum-bool,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.31776,142676,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,0.654634,1.20966,1.19969,0.654634,0.644844,0.648891 +sum-bool,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.9388,360336,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,5.77796,5.77796,5.42427,5.80639,5.83326,5.29225 +sum-bool,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.03402,225132,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,0.991297,0.871922,0.999089,0.991297,0.921766,1.02305 +sum-bool,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.83916,735536,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,5.23176,4.98861,4.30103,6.35297,5.23176,8.32043 +sum-bool,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.751294,348188,3a422bd9b13a7dd1317a73bf9bc8f1ebb834620bbc623153bb60a62d1f0ddbed,0.727257,0.727257,0.743947,0.714384,0.746599,0.59864 +extrema-bool,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.1141,71036,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,2.9712,2.93729,2.97948,3.0079,2.9712,2.94169 +extrema-bool,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.2982,71284,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,2.63876,2.73743,2.63876,2.65233,2.63766,2.63653 +extrema-bool,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.35578,104100,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,2.28751,2.35932,2.38367,2.07848,2.28751,2.0291 +extrema-bool,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.8987,81824,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.73093,1.75833,1.72918,1.73845,1.73093,1.72686 +extrema-bool,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.00109,169356,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,2.05007,2.10177,2.43658,1.78767,2.05007,1.86146 +extrema-bool,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.27627,102112,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.17544,1.1605,1.16679,1.17544,1.20548,1.2442 +extrema-bool,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.2465,300896,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,3.20235,3.29473,2.73177,3.9186,3.20235,2.79004 +extrema-bool,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.81964,143148,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.55584,1.64722,1.51178,1.55584,1.6903,1.31318 +extrema-bool,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.85732,519964,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,4.95944,4.31802,5.80327,4.4055,5.0544,4.95944 +extrema-bool,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.15571,225104,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,1.09655,1.09655,1.08618,1.06892,1.14738,1.15083 +extrema-bool,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.93852,796736,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,6.73474,5.27473,6.73474,4.70003,7.71843,7.25991 +extrema-bool,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.09948,348040,11a2b43ddb236cf91744ab8f1ced269bcd7426c87a195977b338f9c480bfea34,0.816067,0.873573,0.805542,0.781804,0.841479,0.816067 +sum-u8,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.89749,71020,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,2.69243,2.69673,2.68989,2.68626,2.69243,2.7258 +sum-u8,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.83119,71288,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.67102,1.70957,1.67711,1.67011,1.67102,1.66812 +sum-u8,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.01882,95908,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.76568,1.76568,1.97084,1.7137,1.82386,1.68395 +sum-u8,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.23258,81304,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.11675,1.12216,1.11675,1.11407,1.11559,1.1181 +sum-u8,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.06253,126472,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.55983,2.02207,1.21868,1.55983,1.37258,2.0512 +sum-u8,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.62609,102116,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.45201,1.45214,1.34294,1.45201,1.4591,1.35707 +sum-u8,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.64215,200724,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,2.7597,2.89031,2.51696,3.24793,2.48571,2.7597 +sum-u8,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.822,143104,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,1.68416,1.67507,1.69032,1.6815,1.68416,1.69404 +sum-u8,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.1956,348280,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,5.76142,5.76142,5.41673,6.42765,5.79343,5.60361 +sum-u8,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.874794,225088,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,0.970967,0.894523,0.992767,0.970967,0.969156,0.988511 +sum-u8,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.2537,753928,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,4.85066,4.85066,4.03366,6.23402,4.74317,5.65213 +sum-u8,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.825563,348148,6ee3c77ffbcfa78658c6e2d83db34cfa1d1e7dee8b7b22b8a489f69ac294492f,0.665353,0.71092,0.576204,0.665353,0.621738,0.879099 +extrema-u8,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.4381,71540,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.97454,2.98308,2.92587,2.97752,2.93115,2.97454 +extrema-u8,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.93953,71296,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.73847,2.73847,2.73466,2.7595,2.75544,2.73392 +extrema-u8,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.48205,104300,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.63531,2.63531,2.3887,2.67696,2.76112,2.42767 +extrema-u8,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.99968,81360,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,1.84769,1.86365,1.84769,1.84404,1.89317,1.84569 +extrema-u8,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.39392,169492,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,3.20168,3.46675,3.67042,2.7236,3.20168,2.66884 +extrema-u8,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.40759,102356,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,1.25231,2.12139,1.25231,1.23148,1.25694,1.24601 +extrema-u8,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.1983,300856,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,2.48752,3.31233,2.21271,2.97992,2.48752,2.17809 +extrema-u8,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.80796,143404,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,1.77538,1.42255,1.61364,1.8043,1.77538,1.81581 +extrema-u8,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.00778,525836,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,6.50457,6.41513,7.4278,5.12972,6.50457,8.99092 +extrema-u8,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.10064,225308,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,1.15755,1.25083,1.11319,1.15078,1.18427,1.15755 +extrema-u8,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.11715,788796,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,5.70256,5.27129,6.30238,4.68148,5.70256,7.30338 +extrema-u8,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.0892,348360,865618857312d182f2e0f74b03c647f75d8a300ec949898790a3af65cf896f38,0.843661,0.881366,0.83544,0.859857,0.843661,0.808841 +sum-i16,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.96202,70936,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.74681,2.7974,2.77003,2.74681,2.73822,2.74373 +sum-i16,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.80692,71272,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.66186,1.66186,1.66918,1.67275,1.66041,1.65701 +sum-i16,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.13511,95912,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.80138,1.78433,1.98512,1.75653,1.92652,1.80138 +sum-i16,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.24962,81312,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.11906,1.12193,1.11635,1.11906,1.12483,1.11508 +sum-i16,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,1.69613,126728,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.55175,1.55175,2.15549,2.72237,1.53106,1.46242 +sum-i16,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.860116,102248,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.733846,0.748929,0.734106,0.733846,0.732653,0.733282 +sum-i16,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.09957,200288,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.70254,3.04136,2.70254,3.51856,2.5729,2.53097 +sum-i16,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.68157,161352,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.38569,2.40652,1.33952,1.38569,1.39142,1.37904 +sum-i16,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.61316,348024,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.46538,5.7183,4.91869,6.25251,4.87105,5.46538 +sum-i16,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.984697,225364,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.847536,0.78233,0.847536,0.769683,0.898451,0.945173 +sum-i16,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.90248,752200,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.22668,5.22668,3.8811,6.32847,5.59414,4.9412 +sum-i16,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.803319,348340,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.662563,0.662563,0.761083,0.680655,0.595299,0.558726 +extrema-i16,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.79153,70828,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.63449,3.61463,3.92997,3.63449,3.62213,3.69568 +extrema-i16,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.03902,71272,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.76785,2.83916,2.77637,2.76785,2.73324,2.70189 +extrema-i16,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.71676,104012,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.77708,2.90091,2.80682,2.74284,2.77708,2.63124 +extrema-i16,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.9552,81804,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.82914,1.82914,1.86459,2.0768,1.82028,1.80574 +extrema-i16,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.29811,169720,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.31935,2.31935,2.67408,1.94376,2.3858,2.24344 +extrema-i16,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.10754,102304,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.30673,2.05449,1.30673,1.24024,1.22691,1.40687 +extrema-i16,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.11486,300688,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.51284,3.6578,3.18941,4.4203,3.51284,3.14189 +extrema-i16,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.40155,143220,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.33073,2.33073,2.27402,2.24877,2.33386,2.38206 +extrema-i16,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.97622,513868,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,4.74269,4.74269,5.41285,4.36586,3.97193,5.73477 +extrema-i16,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.42123,225296,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.19786,1.21543,1.14897,1.19786,1.17574,1.21057 +extrema-i16,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.41063,780836,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,6.76652,5.2385,7.98897,6.76652,5.49372,8.03675 +extrema-i16,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.23365,348320,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.91756,0.961586,0.91756,0.917412,0.904305,0.918044 +sum-i32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.91517,71232,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.76378,2.77684,2.81031,2.76378,2.74185,2.74641 +sum-i32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.83443,71032,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.67011,1.67705,1.67011,1.68484,1.66592,1.66788 +sum-i32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.02557,95880,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.94725,1.94725,2.0755,1.78079,1.97778,1.88613 +sum-i32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.24788,81472,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.12093,1.13607,1.12158,1.1187,1.12093,1.11829 +sum-i32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.14475,126700,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.47079,1.5902,1.27899,1.65838,1.29282,1.47079 +sum-i32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.50876,101940,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.42597,1.42842,1.50342,1.42597,1.41789,1.41518 +sum-i32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.79077,200676,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.99941,3.00226,2.63663,3.3638,2.99941,2.85613 +sum-i32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.61757,161372,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.39892,1.57098,1.40908,1.33071,1.36972,1.39892 +sum-i32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.10541,348116,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.68594,5.71128,4.11837,6.49644,5.11385,5.68594 +sum-i32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.07971,225332,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.927815,0.952383,0.966076,0.927815,0.911998,0.874751 +sum-i32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.95551,743744,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.48193,5.08471,4.02547,6.67328,5.48193,6.92368 +sum-i32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.900228,348360,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.70729,0.728319,0.737887,0.70729,0.698907,0.671743 +extrema-i32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.75309,71192,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.66742,3.7541,3.6378,3.62758,3.66742,3.66992 +extrema-i32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.92399,71052,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.74461,2.74461,3.0616,2.88203,2.73188,2.74064 +extrema-i32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.76321,104084,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.69386,4.26333,2.88666,2.48499,2.69386,2.49566 +extrema-i32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.01147,81816,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.83918,1.87183,1.84591,1.83918,1.83726,1.83214 +extrema-i32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.30307,169712,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.30941,2.30941,2.62019,1.96539,2.31793,2.06728 +extrema-i32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.55795,101752,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.49741,2.49741,3.12432,3.07029,1.46841,1.35127 +extrema-i32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.42816,300664,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.74659,3.84309,3.16676,4.20713,3.74659,3.12571 +extrema-i32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.03938,143120,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.1539,2.05424,2.0733,2.23041,2.25698,2.1539 +extrema-i32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.02253,526364,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.29829,5.0167,5.86367,4.61244,5.29829,5.6702 +extrema-i32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.35976,224888,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.16844,1.12443,1.12636,1.16938,1.24031,1.16844 +extrema-i32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.19531,798716,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,6.34178,5.22384,6.81806,4.89691,6.34178,8.97249 +extrema-i32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.17444,347912,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.966434,0.968256,0.966434,0.974492,0.933306,0.935826 +sum-i64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.94982,70660,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.72217,2.7217,2.79589,2.77962,2.71714,2.72217 +sum-i64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.82678,71252,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.69734,1.68153,1.68243,1.72232,1.70762,1.69734 +sum-i64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.06857,95644,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.80985,1.80985,2.23183,1.80151,1.92968,1.75867 +sum-i64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.28382,81600,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.12931,1.13989,1.12931,1.12623,1.1312,1.12567 +sum-i64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.03405,126520,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.48698,2.01122,1.48698,2.47016,1.26355,1.43119 +sum-i64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.01383,102372,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.50995,1.95929,1.50995,1.53051,1.4526,1.19237 +sum-i64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.57494,200700,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.48184,2.77026,2.48184,3.26996,2.31916,2.40788 +sum-i64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.44172,161588,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.37183,1.37183,1.3461,1.40304,1.47504,1.1828 +sum-i64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.13122,348032,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,4.9912,4.9912,4.39204,6.29289,4.29167,5.74993 +sum-i64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.65954,247708,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.924442,1.2183,1.00278,0.885094,0.924442,0.849855 +sum-i64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.00262,758084,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.73348,5.73348,5.03824,7.7309,5.73435,5.53548 +sum-i64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.83692,370948,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.741624,1.39059,0.771892,0.741624,0.635741,0.73617 +extrema-i64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.82831,71056,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.08735,5.08735,5.08244,5.24135,5.18092,5.06378 +extrema-i64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.32903,71276,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.01545,3.12728,3.01545,3.01052,2.97277,3.1387 +extrema-i64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.18446,103828,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,4.7806,5.75403,5.84954,4.7806,4.00212,3.48394 +extrema-i64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.23686,94168,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.20637,2.20637,2.15974,2.15114,2.2127,2.22414 +extrema-i64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.78335,169496,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.76945,2.94875,3.14099,2.71841,2.76945,2.61759 +extrema-i64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.06618,124676,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.3306,1.76739,1.75038,2.33616,2.37862,2.3306 +extrema-i64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.34814,300984,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,4.82427,4.82427,4.36998,5.36971,5.10919,4.67608 +extrema-i64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.66651,165976,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.15448,2.15448,2.09117,2.10357,2.20915,2.21575 +extrema-i64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,9.07953,575764,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,8.95139,9.04946,9.83788,8.95139,8.84789,8.61124 +extrema-i64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.68417,247292,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.0779,2.0779,1.92736,2.50718,1.89992,2.20799 +extrema-i64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.55167,799032,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,8.56589,8.56589,8.93097,7.10795,6.60781,10.2125 +extrema-i64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.35511,369984,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.84546,1.94477,2.98903,2.56804,2.96552,2.84546 +sum-f32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.0168,70584,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,3.00875,2.78599,2.78222,3.00875,3.28,3.63308 +sum-f32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.8637,71076,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.68725,1.68962,1.68725,1.68792,1.67605,1.67925 +sum-f32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.36505,95828,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.93356,2.30597,2.36378,1.89365,1.93356,1.81592 +sum-f32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.29512,81816,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.1516,1.15555,1.1516,1.14129,1.15759,1.14457 +sum-f32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,1.59253,126636,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.52216,1.52216,1.2887,1.65811,1.29631,1.55057 +sum-f32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.62792,102084,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.813678,1.48817,1.30895,0.747405,0.813678,0.749051 +sum-f32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.42114,200656,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,3.92003,4.65287,4.35592,3.92003,2.9849,3.20581 +sum-f32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.5593,161692,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.41729,1.51668,1.49052,1.41729,1.39224,1.41383 +sum-f32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.61041,348068,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.42308,6.25328,5.42308,5.57569,4.59409,5.12996 +sum-f32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.989356,224968,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.936274,0.985592,0.957235,0.936274,0.919365,0.924884 +sum-f32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.63756,749432,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.33537,5.17574,5.03423,6.39969,6.33537,6.64435 +sum-f32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.859856,347892,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.769746,0.771975,0.70962,0.769746,0.779364,0.759383 +extrema-f32,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.45893,70728,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.2173,5.2173,5.19447,5.16905,5.64314,6.33913 +extrema-f32,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.79765,71252,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.41404,3.42929,3.34674,3.46855,3.41404,3.34807 +extrema-f32,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.79498,103556,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.47998,3.58883,3.47998,3.18783,3.64225,3.37668 +extrema-f32,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.1615,81260,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.93629,1.95442,1.93144,1.94132,1.93589,1.93629 +extrema-f32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.64062,169752,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.75935,2.87806,3.08082,2.4648,2.75935,2.47302 +extrema-f32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.52351,124844,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.1719,2.50366,1.55242,2.1719,1.56323,2.4143 +extrema-f32,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.47151,300568,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.93878,3.92399,3.4884,4.87169,3.93878,4.40299 +extrema-f32,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.71138,155492,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.25974,2.23733,2.27922,2.25974,2.31113,2.11337 +extrema-f32,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,7.85691,524072,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.78419,7.71317,7.17424,5.14963,4.91064,5.78419 +extrema-f32,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.25065,237220,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.36876,1.37053,1.3277,1.37113,1.36876,1.35669 +extrema-f32,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.97652,799032,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,6.89382,6.89382,7.87566,6.55991,6.81618,9.11311 +extrema-f32,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.96186,360168,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.88938,0.906475,0.871579,0.88938,0.862072,0.962116 +sum-f64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.06451,71264,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.82134,2.80247,2.81538,2.92638,2.82862,2.82134 +sum-f64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.8702,71032,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.71467,1.69002,1.68373,1.71467,1.72396,1.71581 +sum-f64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.19726,95664,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.86262,1.86262,2.05911,1.79026,1.93896,1.78919 +sum-f64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.30281,81572,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.15491,1.16474,1.22124,1.15444,1.15491,1.15119 +sum-f64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,1.46419,126468,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.56379,1.56379,1.47415,1.90958,1.47106,1.60907 +sum-f64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.852956,102120,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.752888,0.770762,0.761013,0.752888,0.750591,0.750054 +sum-f64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.87027,200432,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.83838,2.83838,2.12251,3.71407,2.683,3.06731 +sum-f64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.55583,161544,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.04212,1.13364,1.07083,0.951883,1.04212,1.0235 +sum-f64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.53291,347836,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,4.87357,5.57076,4.19162,5.4293,4.73244,4.87357 +sum-f64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.12924,247224,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.15357,1.80168,1.16779,1.15357,1.06455,0.98901 +sum-f64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.26616,757820,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.55662,5.92328,6.55662,8.70734,6.50538,7.61652 +sum-f64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.14149,370676,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.724414,1.37785,0.757157,0.615723,0.724414,0.705094 +extrema-f64,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.31395,71172,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,5.27082,5.27082,5.17917,5.27433,5.25782,5.33925 +extrema-f64,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.34132,71260,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.12152,3.12152,3.12152,3.14127,3.20533,3.09696 +extrema-f64,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.54316,104052,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.55687,3.61936,3.55687,3.24695,3.58579,3.36362 +extrema-f64,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.92952,93828,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.99237,2.0308,1.99237,1.98599,1.98561,3.63661 +extrema-f64,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.0839,169404,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,4.26624,4.26624,5.52586,5.05271,2.85005,2.55109 +extrema-f64,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.85165,124416,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.1762,2.32657,2.333,2.1762,2.1434,2.12971 +extrema-f64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.49804,300436,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,3.59231,3.59026,3.59231,4.70554,3.91083,3.48255 +extrema-f64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.18382,165560,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.20456,2.31365,1.90996,2.18028,2.27675,2.20456 +extrema-f64,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,8.93854,579892,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,8.75644,8.86904,9.45229,8.41882,8.5251,8.75644 +extrema-f64,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.2529,248112,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.38904,1.469,1.38904,1.40554,1.38304,1.35196 +extrema-f64,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.93316,799296,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,8.48831,8.48831,8.62285,7.26735,7.139,10.04 +extrema-f64,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.4704,370968,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,0.951923,0.990057,0.961422,0.942184,0.951923,0.925166 +sum-time,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.99617,103528,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,2.787,2.787,2.72343,2.73618,2.83941,3.45961 +sum-time,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.92033,104048,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.6833,1.70375,1.6833,1.67497,1.67298,1.68797 +sum-time,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.10483,128656,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.86956,1.86444,3.49477,1.86956,1.97838,1.72974 +sum-time,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.30305,114344,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.12442,1.14007,1.12442,1.12463,1.12244,1.12193 +sum-time,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,1.42619,159540,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.37783,1.47821,1.20527,1.57274,1.23692,1.37783 +sum-time,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.70774,134860,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.46261,1.51577,1.47352,1.46261,1.43114,1.44219 +sum-time,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.94654,239204,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,3.07464,3.03051,3.07464,4.2949,3.26049,2.99757 +sum-time,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.20855,194140,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,0.690969,1.16169,1.0959,0.677496,0.690969,0.635805 +sum-time,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.76982,381000,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,5.80926,5.80926,5.47076,9.00239,5.49961,6.96855 +sum-time,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.11726,257628,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,0.840266,0.840266,0.843939,0.819195,0.786915,0.902151 +sum-time,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.4625,784216,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,5.25617,5.04001,5.25617,6.96696,5.23577,6.22391 +sum-time,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.00208,380584,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,0.684125,0.73864,0.684125,0.645244,0.717648,0.668059 +extrema-time,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.32439,103912,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,4.15443,4.19362,4.07913,4.15443,4.18642,3.98465 +extrema-time,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.16901,103712,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.74267,2.81731,2.74502,2.73609,2.72974,2.74267 +extrema-time,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.95558,136888,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.67632,2.97394,2.71875,2.40554,2.67632,2.56424 +extrema-time,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.05112,114000,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,1.82292,1.85465,1.83494,1.82292,1.81984,1.81903 +extrema-time,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.37772,202316,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.3535,2.3535,2.67339,2.01699,2.3948,2.10449 +extrema-time,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.27393,135012,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,1.23548,1.28283,1.23701,1.2239,1.21879,1.23548 +extrema-time,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.38634,333536,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,3.3869,3.3869,3.08649,3.74785,3.50592,3.35011 +extrema-time,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.12102,176408,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,2.07508,2.13498,2.07087,2.04868,2.07508,2.3621 +extrema-time,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.15189,548868,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,4.58504,4.58504,5.00727,4.44872,4.37367,5.37933 +extrema-time,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.44419,257688,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,1.35331,1.28194,1.29534,1.36485,1.38688,1.35331 +extrema-time,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.71958,815180,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,6.37881,5.14518,7.43469,5.95924,6.37881,8.48257 +extrema-time,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.26234,380908,3fb7228765127f2d73aa2570db52f8b24c2bdf1a790ba53a8cd98c73df02ad9c,0.938134,0.977705,0.938134,0.887691,0.881754,0.962433 +count,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.97521,71280,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,2.70961,2.71326,2.69662,2.71175,2.6864,2.70961 +count,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.8133,71328,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.67589,1.67589,1.652,1.70262,1.68314,1.67243 +count,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.05857,95952,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.69495,1.69495,1.95616,1.68666,1.81537,1.67973 +count,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.28119,81564,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.14216,1.14216,1.11907,1.1211,1.14894,1.17236 +count,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.17445,126420,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,2.15762,2.16984,1.89048,2.44413,1.93029,2.15762 +count,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.922323,102364,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.753063,0.779595,0.739469,0.753063,0.731415,0.756368 +count,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.64736,200420,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,2.67004,2.95167,1.96483,2.67004,2.62802,3.07816 +count,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.49971,143464,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.37565,1.48458,1.66784,1.37565,1.34329,1.36254 +count,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.54667,348028,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,5.71665,6.15755,4.8514,6.45394,5.08592,5.71665 +count,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.874575,225304,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.794509,0.804171,0.836477,0.794509,0.777719,0.716884 +count,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.55967,518660,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,3.75944,3.26737,4.2807,3.0479,3.75944,5.09491 +count,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.691022,348348,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.640817,0.651992,0.700268,0.638022,0.609615,0.640817 +statistics,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.3892,87336,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,9.28278,9.28278,9.29485,9.19219,9.34405,8.97132 +statistics,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.48597,85496,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,4.77758,5.1526,4.77758,4.63902,5.0415,4.72366 +statistics,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.50684,111884,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,5.47555,6.04063,5.47555,5.22717,5.58763,5.34668 +statistics,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.70717,103868,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.95429,3.05811,2.95429,2.93803,2.93484,3.04544 +statistics,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.72496,165232,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,3.62727,5.04058,4.03942,3.62727,3.45713,3.46668 +statistics,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.87071,128720,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.33279,2.45623,2.31381,2.34322,2.31064,2.33279 +statistics,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,7.95221,257988,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,4.9641,6.49976,5.40706,4.9641,4.93846,4.69199 +statistics,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.51943,169612,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.94112,2.94112,2.44557,2.60261,3.07122,2.96758 +statistics,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,11.4514,581652,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,11.2437,11.2437,12.3484,12.0873,11.2289,10.1871 +statistics,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.20474,251988,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,3.0429,3.96188,2.75405,3.0429,2.15232,3.05116 +statistics,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,8.83014,895368,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,10.1526,10.1526,10.4579,9.51525,10.7387,9.26781 +statistics,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.34917,375216,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,1.31323,1.86114,1.31323,1.32374,1.30819,1.28974 +binary,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,22.59,120128,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,19.8211,19.6817,21.0429,19.7482,19.8211,20.5775 +binary,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,15.7881,116140,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,12.9851,12.8687,12.9969,12.7585,12.9851,13.1183 +binary,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,15.3747,138716,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,13.0788,13.0788,12.3887,13.7774,13.0504,13.2752 +binary,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,12.4055,125852,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,8.93177,8.89782,10.3047,8.59942,8.98147,8.93177 +binary,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,74.5284,333296,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,69.4054,70.5948,69.537,69.2526,69.4054,69.0486 +binary,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,7.07972,146228,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,4.48696,4.64167,4.48696,4.48942,4.44347,4.38647 +binary,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,37.6699,345724,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,37.2518,37.2518,35.7087,37.4985,37.1479,38.527 +binary,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.14853,186524,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,4.20339,4.42268,4.34727,4.17743,4.20339,4.18962 +binary,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,23.2496,589876,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,23.735,23.735,23.6773,22.3252,25.2315,26.6661 +binary,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.71538,267984,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,3.02919,3.31686,2.78745,3.04468,3.02919,2.98383 +binary,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,17.5195,975100,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,19.6924,18.6306,20.0692,20.2657,19.6924,16.5939 +binary,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.53566,391760,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.41457,3.00994,2.39865,2.33989,2.56298,2.41457 +truth,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.82654,70584,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,5.46096,5.51255,5.46096,5.36633,5.47787,5.43701 +truth,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.99828,71276,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,3.74443,3.76418,3.71741,3.73599,3.79986,3.74443 +truth,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.69746,104012,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,3.56076,3.79915,3.86063,3.34181,3.56076,3.45747 +truth,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.46303,93924,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,2.40171,2.65062,2.3353,2.45169,2.40171,2.34512 +truth,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.76764,169752,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,4.34945,4.79177,5.52714,4.34945,4.26771,3.47822 +truth,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.66291,124864,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.37816,1.48451,1.38564,1.37758,1.37816,1.37015 +truth,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.73312,300424,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,3.76155,3.76155,3.37069,4.78619,3.97441,3.62133 +truth,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.73755,165452,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.3451,1.37907,1.3379,1.3451,1.34027,1.34674 +truth,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,8.77798,575516,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,8.67441,8.78062,8.97637,8.62473,8.1435,8.67441 +truth,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.19255,247668,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.32519,1.27124,1.27015,1.3572,1.4291,1.32519 +truth,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.99748,780168,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,9.71802,8.43229,9.71802,8.22864,11.7317,10.0307 +truth,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.72339,370700,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,1.15224,1.18493,1.15224,1.24047,1.0264,1.06862 +product,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.89712,71204,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.67908,2.67908,2.66995,2.89877,2.77321,2.64602 +product,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.07336,71268,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.76825,1.82173,1.78081,1.76018,1.76825,1.75486 +product,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.33631,97756,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.87708,2.02579,1.75869,1.8088,2.1228,1.87708 +product,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.29667,81560,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.12213,1.13211,1.11482,1.12028,1.12655,1.12213 +product,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.67184,151240,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,3.01334,2.63074,3.23323,3.07001,3.01334,2.02418 +product,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.08554,102108,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,0.83067,0.934898,0.823052,0.83067,0.857211,0.829309 +product,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.26525,257964,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.43203,2.37953,2.82939,2.43203,1.8106,3.17282 +product,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.84591,165532,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.01477,1.42546,1.06474,1.01477,0.618822,0.624596 +product,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.45542,487476,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,6.26258,6.26258,6.55683,5.89791,6.0934,6.81362 +product,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.65615,247556,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,0.955742,1.28511,0.955742,0.948038,0.966675,0.932516 +product,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.69608,750156,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,5.69577,5.65048,6.73522,4.79093,5.69577,7.67701 +product,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.25206,370944,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,0.737456,1.33171,0.829548,0.678235,0.675183,0.737456 +median,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.3721,97836,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,8.65121,9.40379,8.46052,8.65121,8.92334,8.50854 +median,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,13.0019,98164,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,8.85014,9.03717,8.65944,8.82509,9.1774,8.85014 +median,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,8.22119,108464,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,6.77647,7.32467,6.70665,6.68054,6.77647,7.52707 +median,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,8.47708,108684,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,6.87061,7.37794,6.86862,7.15875,6.77961,6.87061 +median,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,8.95415,128984,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,7.19842,7.35669,7.17608,7.3127,7.17929,7.19842 +median,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.69455,128792,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,4.83751,6.02095,6.88193,4.83751,4.76413,4.75339 +median,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,7.31426,169524,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,5.87511,6.3503,5.85699,5.73293,5.95946,5.87511 +median,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.00116,169724,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,3.477,3.69651,3.0661,3.477,3.66113,3.26696 +median,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,9.66563,251744,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,8.09029,8.92286,8.09029,8.35083,7.89616,7.92081 +median,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.28629,252020,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,2.62713,3.0987,2.55776,2.45011,2.62713,2.71059 +median,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.69945,374444,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,6.0952,6.52728,5.93221,6.0952,6.19537,5.90938 +median,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.9519,375064,315c2bcea545f0cbf21d3803a7f79fb249382fba46b012cf56b181d933d7337d,2.02125,3.11185,1.98314,2.02125,2.23559,1.80961 +quantile,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.9949,97912,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,8.96879,9.40026,8.96879,9.03966,8.80953,8.90476 +quantile,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,11.4102,98076,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,9.5986,9.47194,9.6263,10.3612,9.5986,9.32497 +quantile,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.7101,108208,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,8.59232,8.59232,7.81228,7.95447,9.32068,8.94176 +quantile,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,8.56898,108460,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,7.25403,7.37509,7.14733,7.19015,7.25403,7.47197 +quantile,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,7.17542,128708,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,5.88758,6.22516,5.91078,5.82864,5.88758,5.72058 +quantile,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.76244,129012,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,4.86182,5.77331,5.14063,4.86182,4.80276,4.80766 +quantile,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,7.71253,169388,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,6.23011,6.83145,6.18744,6.1749,6.23389,6.23011 +quantile,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.86419,170104,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,3.68453,4.39757,3.90633,3.68453,3.67018,3.38297 +quantile,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,7.21631,251752,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,6.01546,6.17105,5.91893,6.14306,6.01546,5.93225 +quantile,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.5659,251508,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,2.6464,3.50603,2.6464,2.42918,2.80776,2.46643 +quantile,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,7.87623,374968,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,6.31817,6.31474,6.47321,6.34551,6.07093,6.31817 +quantile,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.93497,375160,96a79df3576987f2fa9ad0dff274338b7a859ebe99538ac3fa57ad32179b7165,2.02239,3.11678,2.04481,1.92833,2.02239,1.93837 +mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,17.6407,91744,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,16.5603,16.7299,16.2939,16.2559,16.5603,16.6222 +mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,14.5042,91424,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,13.2323,13.2732,12.6083,13.311,13.2323,12.7088 +mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.7309,102180,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,9.75906,9.93958,9.72091,9.97833,9.75906,9.43524 +mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,11.7647,101756,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,9.38486,9.448,9.38486,9.28252,9.22973,11.0999 +mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,8.44344,122836,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,7.33475,7.84879,8.34259,7.17525,7.33475,7.24402 +mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,7.14074,128880,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,6.04566,6.33398,6.09676,5.76788,5.90623,6.04566 +mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,8.62604,164160,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,6.74749,7.20127,6.88872,6.74259,6.71961,6.74749 +mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.47839,169468,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,4.34563,4.87463,4.34563,4.36306,3.70078,3.74202 +mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,7.42677,245452,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,7.0938,6.80786,7.36024,7.0938,6.65925,7.95389 +mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.00277,251468,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,2.99592,3.53434,2.6745,2.72624,2.99592,3.2161 +mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,7.86541,368632,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,6.68187,7.03647,6.60002,6.32059,6.79335,6.68187 +mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.29127,374924,079dae3cfbf4364a968d4cd8d501466115acff0d63e052acf45f638867a9e471,2.56245,3.47571,2.68824,2.56245,2.41462,2.42744 +top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,16.2045,104336,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,16.4288,16.6903,16.4288,16.6795,14.8692,14.7135 +top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,16.2429,104300,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,14.4145,15.3235,14.667,14.4145,14.3991,14.0292 +top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,12.4368,114120,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,10.987,12.0226,10.987,10.8958,10.6584,11.5282 +top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,12.1724,114532,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,11.2584,11.9134,11.2728,10.9391,10.7906,11.2584 +top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,12.1946,135128,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,10.0223,11.3104,10.4458,9.52052,10.0223,8.88823 +top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,10.4877,135400,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,8.52705,8.82526,8.52705,8.07394,8.66849,8.1544 +top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.1034,175744,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,8.51969,9.74605,8.8178,8.21056,8.51969,8.33867 +top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,7.19992,176000,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,5.65515,5.6882,5.60614,5.97738,5.41449,5.65515 +top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.1675,257916,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,8.77014,9.56965,9.36184,8.48937,8.6706,8.77014 +top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,7.10462,257748,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,5.08641,5.04666,4.83381,5.8088,5.14667,5.08641 +top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.0901,380904,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,9.2609,11.1623,8.74106,9.2609,9.46247,8.80016 +top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,7.31308,381192,74a7c372ed0f9913a9decfb2db436f7a96b0248f39c0b065fc40b7130ff4b0fc,4.37288,6.94434,4.37288,5.60176,4.30872,4.1434 +first-last,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.7744,91772,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.49286,9.75972,9.49286,9.45267,9.365,9.74144 +first-last,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.58853,92128,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,5.09187,5.81492,5.05813,5.09187,5.12214,5.05451 +first-last,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.9182,101768,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.57088,9.70391,9.5696,9.45327,9.78928,9.57088 +first-last,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.15343,102424,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,4.80917,5.1094,4.8609,4.78338,4.80917,4.76626 +first-last,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.8256,122572,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.87538,9.87538,10.0165,9.49955,10.2561,9.54301 +first-last,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.01999,128984,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,3.77614,4.03446,3.77165,3.73172,3.82879,3.77614 +first-last,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,11.0627,163444,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.85779,10.2304,9.45489,10.6902,9.85779,9.57776 +first-last,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.44369,169780,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,2.95415,3.35344,2.95415,3.10766,2.5495,2.62361 +first-last,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.7393,245112,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.90291,9.94692,9.97121,9.70888,9.90291,9.86535 +first-last,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.71548,251732,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,2.38619,2.70161,2.38619,2.33691,2.3008,2.56288 +first-last,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.8919,368632,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,9.59397,10.5478,9.59397,9.65032,9.56821,9.53969 +first-last,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.69349,374776,abe8fdad86ed848fb271761af768db6a441a0d0a35552758f7baf58991b5d51c,3.05581,4.64963,2.85944,3.02279,3.05581,3.1448 +mixed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,13.2937,98172,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,11.7087,11.7087,11.4959,11.4222,11.8002,11.7638 +mixed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,12.2544,97996,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,10.4236,10.4236,10.5627,10.0583,10.364,10.4303 +mixed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,11.1009,108156,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,9.72997,10.1164,10.147,9.72997,9.50993,9.31474 +mixed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,10.8467,108168,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.48696,8.94421,8.55279,8.48696,8.47612,8.23093 +mixed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.013,128728,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.70454,8.81984,8.76469,8.45896,8.70454,8.42857 +mixed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.75193,129128,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,5.54403,5.73016,5.49858,5.53932,5.54403,5.99727 +mixed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.3634,169584,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.72915,9.23899,8.72915,8.75432,8.32431,8.43657 +mixed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.86456,169880,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,3.54915,4.46148,4.37975,3.54915,3.2703,3.31584 +mixed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.0824,251696,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.88077,9.32441,8.64053,8.88622,8.88077,8.61068 +mixed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.06253,251740,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,3.1964,3.73216,3.1964,3.00112,2.95728,3.22513 +mixed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.6373,375032,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,8.98491,9.25638,8.97445,9.46049,8.98491,8.56627 +mixed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.38745,374820,88f62d709a6c9e8fd94118a587ce18e4e3b0188981ffac364e434f9756015ca4,2.36638,3.51913,2.359,2.50129,2.35296,2.36638 +distinct,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,58.817,202100,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,66.0718,66.0718,67.7597,62.236,63.7942,68.8727 +distinct,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,50.4498,202152,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,43.667,39.09,43.7317,43.4068,44.1955,43.667 +distinct,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,23.3274,197464,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,20.3189,20.3189,19.5507,22.0351,22.0042,19.6171 +distinct,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,20.2438,147388,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,16.8146,17.7647,16.1025,16.8146,16.3384,17.8909 +distinct,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,17.0743,195084,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,13.9905,14.4202,13.8059,13.6296,13.9905,14.2965 +distinct,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,11.9849,200420,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,8.58601,9.40238,8.98949,8.54317,8.50662,8.58601 +distinct,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,14.4633,317652,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,11.2294,13.5319,11.9425,11.0699,11.2294,11.1108 +distinct,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,7.39156,313160,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,6.10714,6.28525,6.70725,6.00781,5.97568,6.10714 +distinct,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,11.8774,576552,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,11.4286,13.3862,12.9604,11.4286,11.3481,10.8613 +distinct,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.86062,419696,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,4.68746,4.38861,4.68746,4.25574,5.32764,4.72579 +distinct,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,11.9068,868100,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,13.3885,12.2811,13.9247,11.4038,19.745,13.3885 +distinct,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.48183,541232,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,4.04345,3.71128,4.76956,4.04345,3.60797,4.54108 +composite,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,458752,28.6778,130200,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,26.5636,26.6014,27.051,24.5943,24.8765,26.5636 +composite,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,458752,15.4139,137024,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,11.9506,12.4729,12.4175,11.9506,11.5255,11.1109 +composite,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,458752,21.0376,153056,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,17.8262,17.7493,17.9718,18.0278,17.8262,17.3536 +composite,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,458752,12.1405,141500,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,9.76793,9.89491,9.45546,9.76793,9.83082,9.64788 +composite,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,458752,17.084,198204,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,13.4477,13.2683,12.7787,13.4786,13.7666,13.4477 +composite,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,458752,10.3638,161792,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,6.02458,6.53514,6.04998,5.98621,6.00601,6.02458 +composite,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,458752,15.795,263784,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,12.1516,12.6561,13.5721,11.4306,12.1516,12.0814 +composite,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,458752,7.43645,202624,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,4.45521,5.27973,4.7534,4.45521,4.30842,4.36413 +composite,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,458752,15.663,432340,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,13.7275,14.8077,15.4833,13.7275,11.7997,13.1121 +composite,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,458752,5.92705,284548,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,2.66625,3.71416,2.88596,2.64331,2.62359,2.66625 +composite,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,458752,16.9261,628804,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,14.4627,16.6701,15.4964,13.9364,14.4627,13.5013 +composite,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,458752,7.91615,408184,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,2.23277,4.25204,2.23277,1.92819,2.55921,2.03762 +selected,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.60358,71192,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,5.21121,5.18322,5.22586,5.59939,5.21121,5.12674 +selected,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.97622,71184,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.42853,3.44109,3.42853,3.35358,3.41774,3.54525 +selected,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.7569,102280,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.46257,3.47986,3.46257,3.30328,3.54867,3.27904 +selected,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.68798,91892,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.37131,2.43389,2.38717,2.33294,2.32644,2.37131 +selected,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.32106,151552,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.30887,2.26341,2.47305,2.30887,2.67018,2.05554 +selected,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.81404,112520,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.46632,1.59677,1.49248,1.46632,1.44502,1.46164 +selected,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.64946,215172,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.84631,2.52036,2.88253,2.86562,2.70265,2.84631 +selected,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.47255,153412,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.19063,1.32571,1.18258,1.19063,1.14722,1.19898 +selected,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.56568,298968,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.83638,2.58004,2.87422,2.79716,2.83638,3.31125 +selected,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.29443,235336,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.05103,1.06339,1.05155,1.05103,1.03534,1.03917 +selected,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.31749,401824,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,2.43385,2.21864,2.17794,2.47273,2.47696,2.43385 +selected,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.23576,358892,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,0.969571,1.04762,0.969571,0.91104,0.898442,1.03629 +selected-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,166200,10.7281,102292,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,9.04923,9.65404,9.12235,8.91849,9.04923,9.04548 +selected-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,166200,8.318,102288,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,7.70592,7.70592,12.1811,8.70316,7.61025,6.99605 +selected-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,166200,8.95689,112368,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,7.85309,8.51065,7.85309,7.89832,7.7712,7.76108 +selected-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,166200,8.28135,112288,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,6.73172,7.32485,6.73697,6.69335,6.50042,6.73172 +selected-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,166200,8.61267,133072,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,7.42797,7.87419,7.42797,7.17519,7.24439,7.54715 +selected-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,166200,6.30604,133128,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,5.13332,5.61379,4.99129,5.13332,5.14485,4.87491 +selected-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,166200,8.67422,173944,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,7.52727,8.32982,7.38442,7.52727,7.38671,7.71331 +selected-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,166200,5.24638,174352,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,4.14747,4.48994,3.85648,4.03825,4.27064,4.14747 +selected-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,166200,8.06611,255540,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,6.90073,7.64788,7.12958,6.82725,6.90073,6.80668 +selected-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,166200,5.10821,256116,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,4.1507,5.38815,4.20795,3.80325,4.1507,3.6658 +selected-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,166200,8.11669,379100,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,6.78016,7.37056,6.88952,6.7405,6.78016,6.69928 +selected-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,166200,5.84318,378444,90c0fe8a409b8a932d9521b00fde89bd3f5abbec914a2364ae0ca74974a480c3,3.98713,5.25497,3.98713,4.09277,3.88217,3.9027 +sparse,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,52.1926,128572,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,49.4262,49.9504,49.49,49.4133,49.4262,49.1756 +sparse,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,50.838,128412,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,48.9286,48.2464,49.6003,48.9939,48.9286,48.2631 +sparse,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,27.2804,136452,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,25.8446,26.0085,25.8926,25.7679,25.8446,25.6197 +sparse,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,26.3035,136900,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,25.0285,25.2238,25.0285,25.1342,24.6559,24.9582 +sparse,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,15.1147,188144,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,14.0987,14.7281,14.1076,13.8321,14.0987,14.0853 +sparse,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,14.6249,188404,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,13.4894,14.1494,13.5918,13.3609,13.4894,13.1138 +sparse,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,9.06025,319232,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,8.27334,8.27334,8.64911,8.00794,8.37474,8.17781 +sparse,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,8.44511,319192,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,7.92178,7.8638,8.31405,7.70697,7.99102,7.92178 +sparse,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.30127,474820,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.83669,5.83669,6.44544,5.55886,5.76045,7.98882 +sparse,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.71968,475388,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.32882,5.32882,5.99789,5.18718,5.16086,6.77907 +sparse,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.5858,766840,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.94257,5.94257,5.17984,6.98341,5.49895,6.89208 +sparse,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.31451,760156,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,7.01408,5.31366,5.83199,7.84975,7.01408,7.25467 +clustered,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,125000,4.14846,70988,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,4.03779,4.02979,4.0451,4.03779,4.07657,4.0348 +clustered,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,1.90996,70944,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.63635,1.74806,1.59333,1.60536,1.63635,1.65177 +clustered,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,125000,2.73228,95640,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,2.50677,2.50677,2.68546,2.40331,2.59719,2.42018 +clustered,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,1.57733,81572,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.37457,1.44856,1.36946,1.35564,1.41468,1.37457 +clustered,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,125000,1.69999,126684,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.74988,1.82095,1.63722,2.0881,1.59138,1.74988 +clustered,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,2.42888,118484,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.793945,1.19522,0.860278,0.769478,0.788499,0.793945 +clustered,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,125000,2.15661,200656,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.95101,2.28295,1.34976,1.95101,1.65508,2.05417 +clustered,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,2.60764,163528,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.01171,1.07273,1.00676,1.04345,1.01171,0.991273 +clustered,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,125000,3.39181,348288,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,3.71156,4.33113,3.55499,5.18437,3.71156,3.67737 +clustered,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,2.51699,245668,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.775206,0.792722,0.743264,0.79204,0.775206,0.665469 +clustered,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,125000,3.8535,529184,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,4.70193,4.64003,4.55316,4.88728,4.70193,5.97512 +clustered,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,2.76275,368588,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.676644,0.748321,0.681508,0.676644,0.57734,0.501224 +skew,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,4.25534,70984,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,4.18905,5.3272,4.18905,4.19315,4.18048,4.17063 +skew,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,1.7264,70940,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.51251,1.5944,1.51251,1.50343,1.54077,1.50748 +skew,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,2.9129,95908,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,2.76077,2.65655,2.97842,2.76077,2.75111,3.00182 +skew,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,2.13281,81572,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.03589,1.46372,1.04002,1.03224,1.03057,1.03589 +skew,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,1.95135,125908,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.9792,2.18446,1.83549,2.17125,1.90108,1.9792 +skew,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,2.70235,116480,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.23609,1.60881,1.54399,1.23609,1.18389,0.690515 +skew,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,3.30196,200368,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,3.43376,3.56911,3.20703,3.94766,3.15409,3.43376 +skew,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,2.28635,161252,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,1.18396,1.20426,1.15634,1.16138,1.20307,1.18396 +skew,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,4.67595,345992,9493dd1efb9b30127aa0aa3416330a7cfc92c30c1dcf11a3fbbbd573487efcf2,4.87804,5.90914,4.58549,4.87804,3.94083,5.87466 +skew,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,3.12934,245388,2fda006d290f4d1ca6efe9f1a4b3d0e68f9072a6159bfb30e323d87e16fca0ac,1.03914,1.22902,1.03914,0.999904,1.05796,0.979057 +skew,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,16.3664,870980,2fda006d290f4d1ca6efe9f1a4b3d0e68f9072a6159bfb30e323d87e16fca0ac,16.4291,14.9681,16.689,16.2977,17.7416,16.4291 +skew,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,3.31695,368700,2fda006d290f4d1ca6efe9f1a4b3d0e68f9072a6159bfb30e323d87e16fca0ac,0.671146,0.832137,0.68375,0.671146,0.626167,0.617946 +skew-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,11.4869,96124,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,10.0365,10.081,9.98945,9.96402,10.0365,10.0653 +skew-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,11.1708,96148,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,9.78617,9.67605,9.67772,10.451,10.2942,9.78617 +skew-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,9.44488,106344,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.05682,8.04102,8.09668,8.05682,8.1771,8.00144 +skew-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,9.48749,106628,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,7.50223,7.60007,7.33207,7.50223,7.35734,7.56392 +skew-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,9.29586,126520,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,7.98298,7.98298,8.15003,8.24909,7.94741,7.94409 +skew-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,6.72001,128896,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,5.20295,5.39369,5.20295,5.14517,5.22316,5.17727 +skew-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,9.73882,167476,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.53584,8.53584,8.4946,8.75912,8.69223,8.49818 +skew-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,5.99453,169940,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,4.54027,4.68472,4.54027,4.60747,4.37947,4.43998 +skew-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,9.9695,249892,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.49557,8.63926,8.35736,8.69419,8.21253,8.49557 +skew-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,6.49192,251492,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,3.21754,3.4121,3.33843,3.0901,3.21754,3.08807 +skew-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,9.59724,372876,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,8.34596,8.34596,8.50481,8.25126,8.07651,10.1487 +skew-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,7.31127,374776,0a108eae43b1b94c348cc8775f32deb19b3fcdf8d52e3c1aea1dccf4affc60a7,2.43173,2.71021,2.88149,2.41335,2.43173,2.41606 +float-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,17.2641,83780,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,17.5623,17.9854,17.6568,17.4622,17.5623,17.4295 +float-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,14.4515,91788,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,12.2315,14.3543,12.2315,12.1663,12.3496,12.1867 +float-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,13.0299,187896,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,9.6721,10.263,9.64047,10.0013,9.65897,9.6721 +float-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,12.6111,194028,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,9.53161,10.1678,9.49221,9.4223,9.53161,10.7801 +float-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,7.58575,196632,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.77648,5.8849,5.52167,5.75633,5.78263,5.77648 +float-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,7.53899,200572,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,5.55434,6.55344,5.44198,5.58041,5.55434,5.48785 +float-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.24717,318964,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,4.33426,5.75441,4.90448,4.33426,4.15301,4.13431 +float-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.89967,319432,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,4.44235,5.94486,4.61496,4.27616,4.37512,4.44235 +float-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,5.42804,575832,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,4.81118,6.0826,4.81118,5.66479,4.62844,3.92314 +float-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.49635,251668,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,3.00571,3.17197,3.00571,2.99744,3.05567,3.00222 +float-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.841,809584,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.35678,5.7666,6.53824,4.21332,6.35678,6.38556 +float-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.18318,374760,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.21732,2.26004,2.18095,2.21732,2.1811,2.2597 +float-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,19.8795,97928,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,17.8861,18.0388,17.5662,17.7796,18.3187,17.8861 +float-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,19.0044,98192,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,17.5674,17.5674,17.4492,17.4242,18.3116,17.6631 +float-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,17.6882,108464,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,16.0519,16.0519,17.1202,15.7771,15.8138,17.3868 +float-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,14.0416,108164,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,12.8489,12.927,12.7408,12.6913,12.8489,13.1909 +float-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,17.5295,128620,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,15.4097,15.5065,15.3869,15.1845,15.4097,15.5574 +float-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,9.42671,128988,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,8.43862,8.05236,8.26794,8.6698,9.02102,8.43862 +float-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,17.4629,169832,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,15.0049,15.1521,15.229,15.0049,14.9792,14.8447 +float-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.07107,169584,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,5.08105,5.01593,5.09059,5.08379,5.08105,5.01351 +float-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,17.0123,251712,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,15.2665,15.1691,15.2665,15.3154,17.247,15.0103 +float-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.40859,251948,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,3.75214,4.77185,3.93972,3.55705,3.5025,3.75214 +float-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,16.9807,374800,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,14.8192,14.8012,14.8564,14.794,14.8192,15.1995 +float-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.50285,375256,8254dd5b7c07aa3f6b65b862ba1f87f9e25680cc1eb068c06cea381991733c2f,2.87693,2.76585,2.87693,2.74775,2.92872,2.93504 +symbol-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,1.32903,87576,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.28408,1.26803,1.23856,1.28408,1.40616,1.39091 +symbol-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,1.48091,87740,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.57754,1.51158,1.39806,1.57754,1.77528,1.61904 +symbol-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,0.641313,97944,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.509225,0.595049,0.525993,0.509225,0.48575,0.491314 +symbol-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,0.708576,97928,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.543715,0.620946,0.543715,0.542042,0.541217,0.554063 +symbol-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,0.673842,118000,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.26361,0.594073,0.286913,0.260731,0.26361,0.262207 +symbol-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,0.405305,118332,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.290411,0.336999,0.29863,0.289471,0.288813,0.290411 +symbol-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,0.498166,159016,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.366862,0.340903,0.366862,0.360086,0.373337,0.375819 +symbol-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,0.472912,159544,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.306208,0.310955,0.276954,0.307328,0.306208,0.274127 +symbol-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,0.522826,241248,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.247904,0.309169,0.247904,0.228341,0.262858,0.228421 +symbol-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,0.487714,241348,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.203067,0.26535,0.212373,0.203067,0.185455,0.187783 +symbol-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,0.642414,364024,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.228584,0.365865,0.253343,0.22847,0.228584,0.223258 +symbol-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,0.628995,364516,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.264675,0.336441,0.267067,0.242134,0.23881,0.264675 +string-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,24.5845,102268,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,25.1998,25.3694,25.5293,25.082,25.1998,24.9172 +string-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,13.6668,122312,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,12.6624,12.5025,12.5864,12.8346,12.6624,12.7278 +string-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,18.0921,204948,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,16.1519,17.1184,16.44,16.1519,16.0198,16.0218 +string-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,9.61895,139164,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,7.97788,7.87524,7.97788,8.00426,7.92187,7.98503 +string-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,13.1077,213056,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,9.23148,9.01993,10.0395,8.56593,9.29859,9.23148 +string-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,5.44554,159704,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,4.05695,4.1312,4.10308,4.05695,3.98583,4.03747 +string-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,10.3892,331652,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,6.05845,7.681,6.86847,6.05845,5.98844,5.84879 +string-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,4.11222,200760,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,2.62839,2.62839,2.68267,2.733,2.24487,2.36203 +string-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,6.21834,581724,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,7.34633,7.01172,8.70047,10.1338,6.55368,7.34633 +string-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,3.98798,282908,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,1.85294,1.88373,1.80572,2.02801,1.85294,1.70755 +string-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,5.90381,612764,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,9.44994,9.85191,8.87668,10.6193,9.44994,6.47171 +string-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,5.28317,405196,09cd04e65ed6be6cf5faedcd46662a9984066e100cb4d6d6cf76f790225543a9,1.65204,1.66522,1.62412,1.63058,1.65204,1.70589 +string-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,18.4697,122232,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.7862,16.7558,16.5173,16.7862,17.2996,16.8204 +string-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,18.2363,122484,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,17.1929,17.2502,17.1814,17.1929,21.2101,16.966 +string-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,18.0939,132832,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.8161,16.2583,16.8161,18.165,16.8667,16.512 +string-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,11.6696,139184,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,10.2204,10.0267,10.0352,10.3035,10.2204,10.5157 +string-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,17.4659,153144,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.3727,16.2352,16.4007,16.3727,16.355,16.4645 +string-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,7.41355,159472,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,5.84084,5.8759,5.95901,5.84084,5.78163,5.72078 +string-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,17.5648,194096,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.322,16.4312,16.3124,16.322,16.3608,16.2913 +string-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,5.63034,200436,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,4.08082,4.10393,4.08082,4.25327,4.02217,4.00055 +string-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,17.6196,276220,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.4249,18.5605,16.4249,16.0776,17.5627,16.0592 +string-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,5.51269,282412,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,3.55165,3.45819,3.55165,3.40698,4.76066,3.58602 +string-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,17.3931,399552,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,16.2267,16.0827,16.2606,16.0623,16.2267,17.165 +string-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,6.67514,405512,8f60991140aec2ac36e339f8e555b4179d074f0fe79eed44b7d684500c38245e,3.06918,3.06918,3.03135,2.93538,3.16859,3.13674 +wide-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,26.3884,126864,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,25.8824,26.0986,25.8824,27.9846,25.7392,25.6269 +wide-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,24.5555,126832,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,24.2249,24.4352,23.2766,24.1758,24.2249,24.2296 +wide-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,27.4303,136908,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,25.466,25.4496,25.8117,25.466,26.2781,25.3063 +wide-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,20.1947,136844,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,15.6897,17.2642,15.504,16.0334,15.1834,15.6897 +wide-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,26.5416,157164,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,26.2113,25.2062,26.2113,27.5958,26.618,25.1064 +wide-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,11.3327,151288,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,9.62012,9.8095,10.3072,9.56257,9.62012,9.60421 +wide-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,26.126,197872,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,25.4932,25.4932,25.2978,26.3291,26.5421,25.4685 +wide-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,8.23361,192232,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,6.8224,6.8224,6.84085,6.8468,6.6009,6.71617 +wide-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,26.9108,280232,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,26.2252,25.2533,26.2252,25.1587,28.9913,29.7314 +wide-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.87333,274440,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,5.47628,5.84436,5.43018,5.47628,5.71488,5.44794 +wide-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,26.4643,403408,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,25.2752,25.4559,25.2752,25.17,25.1346,26.0916 +wide-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.57926,397836,b02ca6967d31a92dee708b618aedbd72ee0bd35f695dd388fa2b1f32316356cc,4.64733,4.71514,4.52535,4.64733,4.54828,4.86851 +extrema-date,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.36179,103576,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,3.84679,3.93978,3.71646,3.76022,3.84679,3.88441 +extrema-date,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.13843,104000,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,2.74614,2.8165,2.72725,2.74614,2.75457,2.71323 +extrema-date,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.08403,136536,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,2.80293,2.8587,3.13542,2.52471,2.80293,2.61327 +extrema-date,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.93943,114340,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,1.8392,1.95264,1.8392,1.8395,1.82834,1.82766 +extrema-date,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.44538,202556,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,2.68444,2.39682,2.90148,2.68444,3.31058,2.49894 +extrema-date,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.57452,134876,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,1.24206,1.3015,1.25011,1.22366,1.24206,1.22455 +extrema-date,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.03726,333476,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,3.2172,3.47687,2.51444,3.14492,3.2172,3.43502 +extrema-date,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.18208,175612,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,2.016,1.98706,1.97316,2.01863,2.09452,2.016 +extrema-date,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.23502,550940,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,4.71376,4.71376,5.27696,4.15405,4.64905,5.28476 +extrema-date,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.3852,257908,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,1.28514,1.29593,1.28514,1.29473,1.23574,1.27652 +extrema-date,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.42393,825648,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,6.07856,5.30458,6.56574,4.49304,6.07856,6.68193 +extrema-date,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.11965,381660,e8a7e2e5f899bc199263d97c5a3b87323c649f9cc0f41e4a2fe651c770e7ddd6,0.917081,0.960974,0.895857,0.950326,0.881561,0.917081 +extrema-timestamp,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.36463,117956,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,3.79317,3.7753,3.79317,3.7428,3.87285,3.86587 +extrema-timestamp,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.65425,118004,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,3.01585,3.14155,3.14098,3.01385,2.99939,3.01585 +extrema-timestamp,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.437,150744,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,2.90708,3.22218,2.90708,2.66577,2.98057,2.68244 +extrema-timestamp,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.44476,128608,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,1.99382,2.03454,2.01536,1.97259,1.98257,1.99382 +extrema-timestamp,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.60233,217056,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,2.52327,3.33121,2.76684,2.08067,2.52327,2.27499 +extrema-timestamp,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.13563,159104,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,1.43267,1.33455,1.51324,1.43267,1.48813,1.36666 +extrema-timestamp,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.56856,348112,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,3.93447,3.93447,3.48157,4.72386,4.01513,3.41169 +extrema-timestamp,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,9.62104,200056,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,3.88882,5.00276,3.67921,3.88882,3.59794,4.56098 +extrema-timestamp,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,7.82457,610284,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,8.19396,8.20707,9.09653,8.00375,8.19396,7.94256 +extrema-timestamp,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.35516,282728,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,1.38569,1.38569,1.31899,1.39322,1.34696,1.43523 +extrema-timestamp,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.10537,836152,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,6.69323,6.69323,7.97764,6.34507,6.15393,9.34888 +extrema-timestamp,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.94833,405548,21b125cdb444576cc6c38fb471f631df4998150b30e1dc016a2b034fd4665bd6,0.965193,1.13388,0.987138,0.940032,0.959287,0.965193 +wide-mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,17.1317,118052,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,15.3161,15.4091,14.3294,15.3161,15.7234,15.222 +wide-mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,17.642,118824,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,14.6737,14.0511,14.9919,14.9814,14.166,14.6737 +wide-mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,16.344,128704,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,15.0474,15.3584,13.8666,15.0192,15.7607,15.0474 +wide-mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,12.5719,128824,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,10.8142,10.6423,10.8142,11.3328,10.4148,11.0258 +wide-mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,16.4067,149376,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,15.0342,14.4053,15.2027,15.4543,15.0342,14.8205 +wide-mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,8.28158,151292,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,7.16337,7.17925,7.18436,7.16337,6.91705,7.06841 +wide-mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,15.7403,190384,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,15.0585,16.0288,13.6559,15.1354,15.0585,14.9691 +wide-mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.12025,192740,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,4.70674,4.70674,4.80697,4.69444,4.70638,4.80752 +wide-mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,16.4999,271960,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,15.1603,14.7449,15.4453,15.1603,16.2042,14.3416 +wide-mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.71673,274420,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,4.08904,4.31777,4.28517,3.6883,4.08904,3.79095 +wide-mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,16.5646,395516,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,14.7953,14.5982,15.5711,14.4453,14.7953,15.8508 +wide-mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.12808,397492,5be0b38a6df4fc7db0b5513daa478fd6c5ceecc77f9753ec0110ee4d866db233,3.1722,3.211,3.1035,3.12935,3.1722,3.21835 +wide-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,34.2616,185520,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,31.3843,33.3658,31.7467,31.0202,31.3843,31.1873 +wide-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,37.4449,186088,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,31.4075,36.8228,31.4075,31.7027,31.0432,31.307 +wide-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,35.2802,196528,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,31.5953,33.666,31.8861,31.5953,31.5746,31.2706 +wide-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,20.1318,212896,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,20.7082,20.7082,19.704,21.6601,18.8383,21.7701 +wide-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,34.542,217032,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,32.0714,33.9047,32.0205,32.0714,32.1695,31.8171 +wide-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,12.0801,219116,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,11.6107,11.365,12.1349,12.0443,11.6107,11.0294 +wide-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,35.6135,257716,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,32.9149,35.7076,33.1277,32.6563,32.7824,32.9149 +wide-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,11.0561,329872,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,8.86685,8.30953,9.56953,8.86685,8.30496,9.13001 +wide-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,37.8777,329324,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,33.286,35.8873,33.4872,32.675,33.286,32.7915 +wide-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.55738,415512,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,5.66479,5.08965,5.66479,6.4676,5.2108,6.17504 +wide-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,34.8732,462892,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,31.4531,33.5101,32.5587,31.2068,31.4531,31.0141 +wide-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.97946,503872,466bba3961b2f9f6a09783b562968e8e65a7414c1bb7fcf054557ef3725e9a3d,4.36842,4.02707,4.02682,4.36842,4.3975,4.46964 +extrema-ascending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.06803,70756,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,3.39566,3.44508,3.39566,3.35656,3.35361,3.40497 +extrema-ascending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.86688,71308,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,2.68225,2.7014,2.68225,2.65968,2.65998,2.69837 +extrema-ascending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.52752,104344,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,2.35063,2.49818,2.35063,2.09661,2.52984,2.22588 +extrema-ascending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.85794,81272,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,1.7298,1.76558,1.78195,1.72288,1.7298,1.71735 +extrema-ascending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.21643,169944,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,2.20538,2.27951,2.5837,1.94831,2.20538,1.9486 +extrema-ascending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.44156,102104,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,1.16382,1.26155,1.16465,1.15865,1.16304,1.16382 +extrema-ascending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.27675,301032,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,3.29902,3.74523,3.29902,4.25403,3.16838,2.77622 +extrema-ascending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.93777,143108,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,1.93112,1.61381,1.78918,1.93112,1.94334,2.02331 +extrema-ascending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.01411,526316,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,4.33142,4.33142,5.14613,4.015,3.91343,5.39318 +extrema-ascending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.27784,225092,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,1.16881,1.11826,1.15957,1.16881,1.18798,1.21317 +extrema-ascending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.12422,803140,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,6.62015,5.05977,6.75578,4.62615,6.62015,7.40841 +extrema-ascending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.979915,348364,2dfbe4fb4b5669af75ae073129cf442aa0e870d4d5975f719c17dd7f443af7b2,0.848123,0.848123,0.899427,0.849372,0.813243,0.826676 +extrema-descending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.46788,70832,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,3.22514,3.23643,3.20244,3.17727,3.23225,3.22514 +extrema-descending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.88994,71252,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.65437,2.6702,2.65437,2.6521,2.64875,2.65566 +extrema-descending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.55894,103636,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.48889,2.72877,2.50315,2.13866,2.48889,2.23612 +extrema-descending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.93711,81568,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,1.74822,1.86407,1.90077,1.74822,1.74726,1.74366 +extrema-descending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.84996,169544,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.74306,3.02791,3.64948,2.74306,2.17979,1.92528 +extrema-descending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.42297,102016,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,2.27383,2.40708,2.29707,2.27383,2.26073,2.24513 +extrema-descending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.0775,300700,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,3.49574,4.50177,2.95525,4.14019,3.49574,2.96815 +extrema-descending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.58464,142964,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,1.51183,1.49989,1.67879,1.61106,1.51183,1.47249 +extrema-descending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.73004,528416,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,4.77262,4.77262,5.11376,4.40723,4.43642,5.21699 +extrema-descending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.2827,225384,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,1.20927,1.14586,1.16651,1.20927,1.25805,1.29627 +extrema-descending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.46966,795160,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,5.77425,5.22711,6.79331,4.88186,5.77425,7.45287 +extrema-descending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.00278,348232,8823eccd39d81882e79aa8268b5693e0450abcc2f497bddd42ab748b6822269f,0.833244,0.865535,0.871011,0.833244,0.809806,0.802509 +few-groups-indexed,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,7,6.84367,89532,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,5.41288,5.41288,5.41556,5.33701,5.4411,5.35352 +few-groups-indexed,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,7,3.34211,89740,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,2.0718,2.10437,2.0718,2.08549,2.06263,2.06526 +few-groups-indexed,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,7,5.94978,99952,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.47055,4.57416,4.47055,4.58378,4.4265,4.46121 +few-groups-indexed,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,7,3.0404,100248,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,1.9267,1.9267,2.12249,1.83183,1.99979,1.87575 +few-groups-indexed,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,7,6.04504,120788,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.42246,4.44058,4.38847,4.42246,4.40874,5.56656 +few-groups-indexed,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,7,2.87696,118756,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,1.40505,1.79292,1.4228,1.31797,1.40505,1.25194 +few-groups-indexed,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,7,6.01299,161644,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.56469,4.67961,4.93192,4.54708,4.56469,4.47347 +few-groups-indexed,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,7,1.94939,159820,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,0.856779,0.893359,0.856779,0.85592,0.890725,0.717481 +few-groups-indexed,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,7,6.76127,243500,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,5.22532,5.22532,5.38228,5.27642,5.10303,5.16409 +few-groups-indexed,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,7,1.88924,241540,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,0.568243,0.675668,0.571931,0.566715,0.554668,0.568243 +few-groups-indexed,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,7,5.98013,366792,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,4.47183,4.59868,4.45964,4.44072,4.47183,4.53767 +few-groups-indexed,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,7,2.35035,364828,8c33a6dcf0d3b3a7fa0707f4c6875198e02283d54ed3cd8b601dabd37c1b6089,0.520776,0.647453,0.508333,0.520776,0.508656,0.532168 +hot-top-ascending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,6.50462,87496,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.51606,6.34505,5.51606,5.50407,5.72688,5.50625 +hot-top-ascending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,6.60793,87692,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.41042,5.43962,5.3447,5.41042,5.67958,5.36219 +hot-top-ascending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,6.58289,97588,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.57461,5.55525,5.55442,5.57461,6.17976,5.7106 +hot-top-ascending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,4.43768,97968,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,3.36497,3.32727,3.3839,3.37591,3.32785,3.36497 +hot-top-ascending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,6.65191,118516,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.54086,5.54086,5.55568,5.53976,5.52223,5.55147 +hot-top-ascending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,1.98517,102336,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,1.89589,1.89972,2.00284,1.88709,1.87955,1.89589 +hot-top-ascending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,6.48206,159076,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.47062,5.50669,5.44115,5.45209,5.47343,5.47062 +hot-top-ascending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,2.14297,143148,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,1.54634,1.84876,1.6776,1.54444,1.54634,1.54578 +hot-top-ascending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,6.48847,241240,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.3548,5.38272,5.3548,5.40381,5.34648,5.33423 +hot-top-ascending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,0.98001,225348,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,0.872596,0.899507,0.837451,0.872596,0.81659,1.05281 +hot-top-ascending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,6.57208,364972,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,5.36753,5.37541,5.35947,5.34446,5.44367,5.36753 +hot-top-ascending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,1.00641,348384,ab79207dda9ab4fca52a174ffd0453069fce61e5e2a685500719451adb7c222c,0.704886,0.7774,0.747837,0.704886,0.687295,0.691994 +guid-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,12.1201,122488,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.7139,10.7139,10.6319,10.4036,10.7856,11.4533 +guid-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,11.9924,122708,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.3959,10.2984,10.4393,10.211,10.3959,10.4538 +guid-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,12.2146,132680,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.51,10.6639,10.3861,10.6667,10.5067,10.51 +guid-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,7.86898,133032,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,6.41516,6.42597,6.41508,6.33542,6.41516,6.42597 +guid-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,12.7186,153584,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.568,10.5941,10.568,10.5582,10.3777,10.5922 +guid-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,7.34279,153580,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,5.7121,5.80739,5.93319,5.7121,4.99151,3.96915 +guid-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,12.3764,194192,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.477,10.4995,10.477,10.4671,10.3743,10.5068 +guid-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,4.43581,194372,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,2.94823,2.94823,3.03902,2.96755,2.90053,2.90258 +guid-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,12.0775,276444,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.4534,10.601,10.4221,10.4534,10.9542,10.4351 +guid-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,4.59557,276568,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,2.50603,2.49611,2.51056,2.53983,2.48341,2.50603 +guid-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,12.3073,389200,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,10.6126,10.6423,10.6742,10.5523,10.6126,10.5981 +guid-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,6.41462,399368,a668c7d333b9223cc6de56bfb525fc6aca0f62d59b8b3847add3387053d3ebf2,2.52894,2.90126,2.35967,2.48536,2.52894,2.68828 +list-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,30.8402,102312,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.8837,27.8124,27.8837,28.0271,27.8296,28.0123 +list-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,17.515,102020,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,16.4494,16.4494,16.348,16.5147,16.3336,16.4881 +list-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,28.637,112032,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.6334,27.5809,27.6334,27.7124,27.5104,28.3464 +list-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,9.8057,106288,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,9.37827,9.33958,9.36917,9.37827,9.46473,9.66281 +list-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,28.6632,132848,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.8146,27.8146,27.8142,27.5242,29.5804,28.092 +list-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,6.29784,126904,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,5.4174,5.45627,5.4174,5.42994,5.40743,5.4146 +list-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,28.5161,174168,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.7738,27.7086,27.7711,27.7738,28.717,27.9386 +list-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,5.15358,168040,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,3.84177,4.48136,4.16,3.82592,3.84177,3.78987 +list-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,28.5558,255928,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.6487,27.6179,27.5705,27.6487,28.9974,27.9405 +list-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,4.81875,249584,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,3.4438,3.4438,3.69746,3.62716,3.15731,3.05711 +list-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,28.82,379112,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,27.7497,27.7497,28.4569,27.8118,27.6702,27.4984 +list-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,4.44572,372636,30dedc4a21526a9e9e4b2981d9e3631d2208c425da7c2f52df422e9022799cb3,3.44517,3.45566,4.61484,3.44517,3.13675,3.03221 +unique-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1000000,29.5997,136764,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,27.0494,27.8856,27.2334,27.0494,27.0013,26.8749 +unique-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1000000,10.3205,136808,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,6.91218,7.2984,6.86577,7.04132,6.91218,6.86806 +unique-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1000000,45.1406,202428,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,40.1672,40.1672,38.84,40.3137,40.024,40.3064 +unique-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1000000,9.45652,147108,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,6.80232,7.66034,6.9417,6.76837,6.79795,6.80232 +unique-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1000000,26.107,218868,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,21.9424,24.5709,21.9424,22.0737,21.742,21.7209 +unique-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1000000,7.35812,168096,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,3.98592,4.8303,3.98592,3.89097,4.05385,3.86229 +unique-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1000000,15.6404,351944,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,13.7647,15.5537,14.3048,13.6181,13.7647,13.6979 +unique-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1000000,5.92091,209024,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,3.00798,3.51006,3.08112,2.94713,3.00798,2.69542 +unique-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1000000,11.7624,595820,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,10.4696,11.1209,10.4696,11.1294,10.1512,10.4286 +unique-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1000000,6.56769,290728,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,1.94838,3.36398,2.00351,1.92575,1.92417,1.94838 +unique-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1000000,12.7273,952380,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,10.2449,11.3989,9.50195,10.2209,11.0437,10.2449 +unique-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1000000,11.4403,413996,8b42cbc90df24689fd21364ab1959a903614e6dc2e030c592d294fe2a84bde68,1.60204,4.9183,1.7971,1.60204,1.59134,1.59645 +nonnull,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,4.76972,70932,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,4.52293,4.52293,4.45773,4.5704,4.44197,4.59203 +nonnull,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.23495,71040,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,3.01337,3.01665,3.01853,3.01337,3.00387,3.00574 +nonnull,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.12705,97948,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.85727,2.99724,2.75393,2.85727,3.11546,2.7965 +nonnull,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.84759,96000,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,1.89007,2.80266,1.89754,1.89007,1.88891,1.87504 +nonnull,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.43717,151128,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.47772,2.79494,2.47772,2.1025,2.50437,1.78433 +nonnull,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.43143,120552,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.20135,2.20135,2.2266,2.21647,2.18196,1.29104 +nonnull,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.06895,257728,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,3.35847,3.35847,3.75159,3.34834,2.95123,3.95893 +nonnull,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.74519,165724,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.49842,2.66767,2.48157,2.56709,2.49842,1.89527 +nonnull,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,8.52701,575748,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,8.33067,8.33182,8.09318,8.37179,8.33067,7.87389 +nonnull,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.69504,247836,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,1.26607,1.47887,1.26607,1.21052,1.25881,1.2939 +nonnull,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.65332,753720,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,7.88474,8.15443,7.88474,6.15197,7.08399,9.528 +nonnull,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.34422,370460,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,0.882432,1.52896,0.92483,0.871726,0.882432,0.843192 +hot-min-ascending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,2.04301,71216,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.90073,1.906,1.90073,1.89452,1.93049,1.89712 +hot-min-ascending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,1.93873,71288,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.79139,1.82879,1.79542,1.7819,1.79139,1.7909 +hot-min-ascending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,2.39975,97964,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.40995,1.53091,1.2083,1.30808,1.61621,1.40995 +hot-min-ascending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,1.03238,81512,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.14636,1.18926,0.997919,0.977527,1.14636,1.17653 +hot-min-ascending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,1.32057,151572,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.05559,0.880155,1.31595,1.05559,1.52861,0.905817 +hot-min-ascending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,0.820738,102104,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.595508,0.735411,0.595508,0.600462,0.577361,0.581513 +hot-min-ascending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,1.53913,257752,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,1.88695,1.58933,2.20739,1.88695,1.31398,2.5149 +hot-min-ascending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,0.629959,142884,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.637016,0.730716,0.698162,0.637016,0.633052,0.567739 +hot-min-ascending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,7.7327,546900,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,9.68189,8.10891,9.35678,9.70779,9.68189,10.7494 +hot-min-ascending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,1.11554,224932,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.791314,0.791314,0.820971,0.751553,0.885302,0.721219 +hot-min-ascending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,9.37795,876596,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,10.5928,9.23971,10.731,10.5928,11.0796,9.92517 +hot-min-ascending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,0.977415,348632,0d27a2491b3d086a3fefea69ca4e109e00f61956536cc6791192c467a61a68b4,0.872466,0.973099,0.773417,0.872466,0.869908,0.882685 +hot-min-descending,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,2.02704,70988,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.89756,1.90284,1.8954,1.88977,1.89756,1.92848 +hot-min-descending,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,1.313,71348,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.19278,1.20433,1.18781,1.18994,1.24931,1.19278 +hot-min-descending,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,2.43726,97508,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,2.30058,2.58152,2.11917,2.26614,2.72424,2.30058 +hot-min-descending,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,0.896815,81816,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.771376,0.792912,0.771376,0.770701,0.773401,0.770436 +hot-min-descending,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,1.34671,151060,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.03842,0.894705,1.25891,1.03842,1.5569,0.889235 +hot-min-descending,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,1.05258,102324,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.04771,0.993924,1.05101,1.04771,1.0386,1.07474 +hot-min-descending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,1.44626,258032,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.76865,1.673,2.17369,1.76865,1.29929,2.52298 +hot-min-descending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,2.27623,161680,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.09401,1.22796,1.19354,1.06994,1.09186,1.09401 +hot-min-descending,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,12.7164,545024,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,8.47318,8.00646,8.49238,8.08031,13.3842,8.47318 +hot-min-descending,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,1.1947,225336,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.11674,1.1149,1.08468,1.11674,1.19949,1.13006 +hot-min-descending,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,8.00452,866660,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,10.5774,9.1783,11.5626,10.5774,10.7031,9.7452 +hot-min-descending,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,0.902368,348164,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.776415,0.774401,0.749778,0.776415,0.892187,0.777826 +all-null-key,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,4.66494,71020,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,4.51564,4.50738,4.52704,4.51564,4.56275,4.51016 +all-null-key,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,3.31146,71008,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,3.25577,3.26592,3.25132,3.54142,3.25577,3.25285 +all-null-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,2.64689,81540,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,2.47353,2.46815,2.45971,2.48484,2.53443,2.47353 +all-null-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,1.71983,81816,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,1.6408,1.65901,1.64367,1.64007,1.6408,1.64079 +all-null-key,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,1.64516,102084,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,1.50859,1.50859,2.07806,1.43971,1.4425,1.53588 +all-null-key,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,1.0084,101816,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.838543,0.874123,0.864367,0.83807,0.838536,0.838543 +all-null-key,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,1.52247,143224,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,1.44759,1.44759,1.442,1.45735,1.73061,1.42689 +all-null-key,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,0.735873,142864,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.656247,0.656247,0.635621,0.678933,0.611818,0.68501 +all-null-key,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,1.12723,224836,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,1.01137,1.07478,1.02578,0.999083,0.994545,1.01137 +all-null-key,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,0.502272,225108,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.409256,0.413718,0.426079,0.409256,0.387698,0.385278 +all-null-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,1.10126,348368,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.887437,0.870734,0.986112,0.887437,1.07622,0.855952 +all-null-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,0.475572,348112,6ba0bd989322c14d9f8f5035cce5167445580360b19874f70123588f5d18cc14,0.313103,0.344911,0.317094,0.312195,0.313103,0.305671 +distinct-skew,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,43.3451,200016,65c9530f85952e51e2e328375168e406e8f1b73d7aa2d83845ac77496ca9365b,45.77,45.77,43.2008,44.2954,46.2477,45.8795 +distinct-skew,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,36.2055,200536,65c9530f85952e51e2e328375168e406e8f1b73d7aa2d83845ac77496ca9365b,32.3919,28.9294,33.0468,32.3919,32.5854,30.2788 +distinct-skew,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,22.0884,289956,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,17.4656,17.4656,18.0389,17.938,16.6982,16.542 +distinct-skew,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,13.2628,147352,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,11.2641,11.5032,11.6495,11.2641,11.1395,10.9727 +distinct-skew,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,17.2376,343140,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,14.2565,14.6296,14.2565,14.3197,13.1216,14.2495 +distinct-skew,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,7.20331,213316,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,6.03605,6.03605,6.0313,6.15005,5.94194,6.12258 +distinct-skew,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,15.5717,443672,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,13.3417,13.4856,15.943,13.3417,11.1865,11.0103 +distinct-skew,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,4.99766,278408,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,3.98529,3.97006,3.9877,3.98529,4.42396,3.88231 +distinct-skew,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,14.6078,679336,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,14.5776,14.5776,15.3664,12.7196,13.7007,16.0151 +distinct-skew,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,3.47312,327300,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,3.18768,2.83678,3.02125,3.20774,3.4358,3.18768 +distinct-skew,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65537,14.9346,972548,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,13.4073,12.913,13.4073,12.9605,16.0608,13.683 +distinct-skew,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65537,3.46355,447572,34d140f6bf047da72ec0412447092d7f5468f27b737fc9e59c921955aedf4811,2.77614,2.76815,2.69709,2.77614,3.19216,3.50252 +distinct-correlated,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,19.0813,121956,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,16.4069,16.4069,16.3103,16.4964,16.6437,16.2742 +distinct-correlated,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,17.6032,122420,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,14.8791,14.8791,14.7502,14.6791,16.2345,16.0035 +distinct-correlated,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,14.3082,181212,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,10.9893,13.2084,11.0434,10.9625,10.9893,10.9849 +distinct-correlated,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,11.8739,120636,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,9.97651,7.29617,9.99486,10.2529,9.97651,7.06912 +distinct-correlated,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,11.1748,195520,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,9.06464,10.5422,9.15505,8.96417,9.06464,8.99447 +distinct-correlated,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,9.25464,139420,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,3.97343,5.47936,4.15106,3.97343,3.965,3.83123 +distinct-correlated,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,11.2042,318312,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,8.59391,10.4007,9.44507,8.59391,8.3217,8.53148 +distinct-correlated,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.02472,179904,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,3.11199,3.42342,3.08589,3.23045,3.11199,3.10807 +distinct-correlated,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,10.145,574668,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,10.3006,10.811,10.3006,11.8261,9.82284,9.90145 +distinct-correlated,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.82435,254196,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,2.32191,2.24309,2.38863,2.31381,2.32191,2.34918 +distinct-correlated,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,9.68557,736772,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,11.5966,17.3853,11.5966,12.0086,8.87606,10.3153 +distinct-correlated,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.90345,377704,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,2.99444,2.99444,3.52917,3.08714,2.88026,2.80179 +symbol-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,40.6461,97924,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.1318,38.912,39.6432,38.9173,39.4886,39.1318 +symbol-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,34.452,97856,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,33.0126,33.2837,33.5151,32.9488,33.0126,32.7283 +symbol-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,39.756,108212,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.6291,40.4898,39.5841,39.6291,39.8212,39.2058 +symbol-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,19.6253,108448,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,19.1948,19.1948,20.1086,18.8996,18.856,19.2226 +symbol-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,40.0012,128500,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.1821,39.2897,38.8566,39.4833,39.1354,39.1821 +symbol-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,12.4997,128760,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,10.9637,11.3811,10.9219,11.9462,10.9637,10.9542 +symbol-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,39.606,169736,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.462,39.462,39.0183,39.4596,39.7479,39.7878 +symbol-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,8.10291,170064,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,6.82171,7.30725,6.58662,6.82171,6.59769,6.92225 +symbol-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,39.9599,251780,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.3889,39.0399,39.3889,39.1498,39.5426,40.4987 +symbol-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.95017,251624,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,4.69179,4.62423,4.69179,4.74952,4.46907,4.89972 +symbol-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,39.699,375016,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,39.7111,39.6371,39.7111,39.8771,39.7778,39.7051 +symbol-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.68657,374868,bd16861e4bfb2bf9eecc83018b5d98157e9101b4c9a8ce57538fd0cacbb691e5,3.46445,3.46445,3.40117,3.3726,3.68847,3.65688 +symbol-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,44.6678,116092,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,43.6789,44.7378,43.5517,43.6789,43.6233,44.0218 +symbol-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,40.6969,116512,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,40.1995,41.409,40.1995,40.2323,40.159,39.5547 +symbol-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,45.4442,126876,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,44.6391,45.7203,44.6403,44.5808,44.5516,44.6391 +symbol-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,26.1569,136888,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,25.411,25.663,25.0583,25.411,25.0813,25.424 +symbol-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,45.4125,147436,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,44.9294,45.6822,44.9294,44.7057,44.4337,45.0476 +symbol-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,14.3901,157704,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,13.9429,13.6836,14.0347,13.9429,13.8077,14.0858 +symbol-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,45.7011,188516,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,44.8224,46.2479,44.8224,44.8146,44.9039,44.8207 +symbol-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,10.0626,184388,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,8.26782,9.40663,8.70308,7.87416,8.26782,7.8596 +symbol-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,44.7708,270044,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,44.2343,45.1786,44.2343,44.368,43.9673,44.1524 +symbol-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.3659,262068,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,5.56268,5.52742,5.6217,5.6017,5.56268,5.54675 +symbol-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,45.2067,392788,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,43.9132,45.0753,43.9437,43.8603,43.623,43.9132 +symbol-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.88677,375100,f8b8ccc418982414bfa68c9de1d005671cf24403b6c459d2f4438324c017bcce,4.2073,4.33399,4.06849,4.09939,4.28489,4.2073 +distinct-string-values,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,23.5843,122504,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,22.3809,22.854,22.4309,22.0275,22.2117,22.3809 +distinct-string-values,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,16.873,139160,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,15.6609,15.8306,15.6609,15.5762,15.4484,15.8338 +distinct-string-values,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,12.2797,159784,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,9.61408,9.59952,9.62454,9.61408,9.58506,9.74516 +distinct-string-values,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.60752,200480,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,5.47442,5.47442,5.51969,5.72416,5.25558,5.18478 +distinct-string-values,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.85773,282372,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,4.42819,4.73658,4.42315,4.42819,4.38917,4.6736 +distinct-string-values,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.62121,405772,9b7eb3754cd7ed308bf0f8be7348679914b4acc67b61adcba0d6bfba1217ba81,3.85778,4.06374,3.80759,3.87957,3.75663,3.85778 +distinct-string-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,42.2942,118644,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,40.7126,40.7415,40.6827,40.8672,40.689,40.7126 +distinct-string-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,18.376,122100,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,16.8907,17.1599,17.083,16.8907,16.6553,16.8457 +distinct-string-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,24.819,200272,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,22.1579,22.8415,22.1579,22.2581,22.1277,22.0702 +distinct-string-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,10.4952,138912,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,9.14033,9.14033,9.09575,9.11566,9.33976,9.17497 +distinct-string-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,13.5332,220756,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,12.2844,12.2844,11.9366,12.6588,14.1221,12.1093 +distinct-string-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,6.85973,159664,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,4.79168,5.21725,4.87586,4.79168,4.7574,4.74845 +distinct-string-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,11.8566,349908,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,8.9782,9.66101,8.9782,8.22321,7.70986,9.39101 +distinct-string-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,4.18628,200428,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,2.76323,2.9139,2.75212,2.76323,2.76187,2.96728 +distinct-string-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,8.33302,606472,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,8.77697,8.77697,7.40438,9.8212,9.04806,7.44179 +distinct-string-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,3.79577,282488,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,1.94697,2.04478,2.03337,1.93737,1.8978,1.94697 +distinct-string-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,9.71789,700812,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,7.36271,9.15346,7.36271,8.54931,6.91907,6.78002 +distinct-string-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,5.43854,405528,ac3f509a27fd3b0822cc8bdda118b589ce75ecf1593f6ebb10af51c0966a8c22,2.68161,3.04788,2.70449,2.68161,2.57431,2.48144 +mixed-distinct-string-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,46.2738,118120,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,44.5976,44.6679,44.8813,44.5004,44.3993,44.5976 +mixed-distinct-string-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,34.3736,128652,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,33.3654,33.1587,33.091,33.3654,33.5157,33.859 +mixed-distinct-string-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,27.7638,208572,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,25.3066,26.33,25.4884,25.2764,25.1846,25.3066 +mixed-distinct-string-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,18.31,142988,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,16.5676,16.5676,16.5623,16.4917,16.6908,16.6195 +mixed-distinct-string-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,19.1732,225508,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,13.6008,13.966,13.6395,13.6008,13.502,13.5346 +mixed-distinct-string-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,10.6502,171692,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,9.04792,9.1579,9.04792,9.02014,9.03269,9.29699 +mixed-distinct-string-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,14.7082,354512,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,12.0948,13.6691,12.1308,12.0948,11.1914,9.09018 +mixed-distinct-string-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,7.47664,229416,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,5.52492,5.85212,5.85567,5.46775,5.38768,5.52492 +mixed-distinct-string-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,11.2509,579688,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,10.0707,10.1891,9.15248,10.0707,10.4152,9.31486 +mixed-distinct-string-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,6.4119,335692,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,4.39218,5.10767,4.39218,4.25648,4.20024,4.86279 +mixed-distinct-string-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,10.232,665984,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,9.32093,11.4599,7.36736,8.9444,10.7233,9.32093 +mixed-distinct-string-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,7.36882,479796,61232620dcfbf5bc76482baa4c6693ac891191c888c9bc3db3d330a13083b072,3.95088,3.99099,3.69018,3.953,3.95088,3.36873 +distinct-guid-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,33.5498,122460,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,32.1282,32.3032,32.1282,32.0503,32.1958,31.9894 +distinct-guid-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,10.0406,116360,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,9.05553,10.3248,9.09496,8.67428,9.05553,8.53054 +distinct-guid-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,29.9497,136592,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.5032,28.685,28.382,28.5673,28.3648,28.5032 +distinct-guid-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,8.69829,132784,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,5.486,5.48947,5.39184,5.486,5.52258,5.44106 +distinct-guid-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,30.347,165292,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.4392,28.8544,28.1612,28.3683,28.4572,28.4392 +distinct-guid-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,4.04613,153276,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,2.94545,2.99856,3.68762,2.94545,2.88804,2.89513 +distinct-guid-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,29.4701,214804,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.2079,28.817,27.9486,28.4891,28.0745,28.2079 +distinct-guid-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,3.41054,194244,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,2.13048,2.20275,2.13048,2.17082,1.89479,1.71376 +distinct-guid-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,29.6535,313128,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.1036,28.7706,27.9996,28.765,28.1036,27.9334 +distinct-guid-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,2.6449,276500,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,1.308,1.43017,1.4143,1.308,1.29867,1.25598 +distinct-guid-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,30.0605,468960,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,28.4582,28.4582,28.5236,28.9009,28.0921,27.9837 +distinct-guid-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,4.78535,399036,d9309af93908251cc2530fcab1577eb47724fdb555fb52140e0cf29d69e3e69d,1.58621,1.8072,1.78234,1.5821,1.58621,1.54195 +mixed-distinct-guid-keys,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,37.9974,122752,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,36.7185,36.6946,36.7185,36.7782,36.6916,36.9499 +mixed-distinct-guid-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,38.6208,122720,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,36.9051,37.4453,37.0321,36.8065,36.9051,36.8532 +mixed-distinct-guid-keys,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,33.6579,136764,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,32.2959,32.4226,32.2959,32.3529,32.2514,32.1238 +mixed-distinct-guid-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,34.2888,136868,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,32.4765,32.4765,32.5497,32.5664,32.3551,32.4141 +mixed-distinct-guid-keys,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,32.9669,165384,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.3566,31.4221,31.4604,31.3196,31.1715,31.3566 +mixed-distinct-guid-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,8.22456,196800,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,6.97931,7.09172,6.78158,7.22507,6.70829,6.97931 +mixed-distinct-guid-keys,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,33.0636,215016,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.3789,31.332,31.2509,31.3789,32.1711,31.4968 +mixed-distinct-guid-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,7.37446,237480,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,6.17918,6.22138,6.43906,6.1381,6.17918,6.16016 +mixed-distinct-guid-keys,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,32.8723,313052,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.1977,31.4864,31.1977,31.8982,30.7414,30.9124 +mixed-distinct-guid-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,8.05643,344160,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,6.38474,6.44113,6.6687,6.22451,6.30697,6.38474 +mixed-distinct-guid-keys,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,32.9644,460768,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,31.6063,31.6063,31.8326,31.6674,31.1559,31.1967 +mixed-distinct-guid-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,8.15376,492000,0b242a4715a18e98585d2c495072e013dd56ee015a0185ab603539decca3d4fd,6.37937,6.64872,5.98582,6.37937,6.44357,6.1248 +distinct-list-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,14.9309,96120,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,14.9916,14.7019,14.4108,14.9964,14.9916,15.1152 +distinct-list-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,8.54603,106116,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,8.26397,8.27193,8.26397,8.14925,8.13955,8.291 +distinct-list-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,4.54288,126680,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,4.32202,4.34915,4.32202,4.26216,4.32254,4.24238 +distinct-list-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,3.00852,167780,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,2.91732,3.00356,3.12923,2.91732,2.35421,2.42563 +distinct-list-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,2.02472,249884,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,1.73499,1.74137,1.79243,1.72384,1.72066,1.73499 +distinct-list-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,2.51101,372704,22ba9bd6ec666041eea9f90edb8e32c5c3c9128b3a28900fc9cf9c171c54d8b1,1.99379,1.99379,1.93091,1.92499,2.01903,2.04899 +mixed-distinct-list-keys,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,45.312,97660,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,38.9243,40.7514,38.7903,38.9699,38.9243,38.8444 +mixed-distinct-list-keys,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,34.7059,110472,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,34.0187,33.979,33.8764,34.0939,34.0187,34.139 +mixed-distinct-list-keys,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,8.71936,169944,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,8.43694,8.43694,8.42667,8.72407,8.37857,8.82476 +mixed-distinct-list-keys,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,9.15951,221220,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,7.81901,7.82939,7.91059,7.81901,7.16896,7.34283 +mixed-distinct-list-keys,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,7.37227,327736,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,7.14127,7.14127,7.23764,7.11503,7.16942,6.94144 +mixed-distinct-list-keys,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,7.56513,452592,bda38af97f50bcedd7789b55d9625aa5bdb902b2192d7e15629a9ab65afc5d9e,6.78299,6.85694,6.46626,6.58446,6.90297,6.78299 +mixed-distinct-string-values,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,26.8965,118376,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,25.8613,25.6052,25.9538,26.0088,25.8613,25.8445 +mixed-distinct-string-values,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,66.0719,256976,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,41.074,45.4046,41.7349,40.9495,41.074,40.6111 +mixed-distinct-string-values,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,60.4176,275516,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,35.8191,37.7516,34.2299,35.4459,36.7712,35.8191 +mixed-distinct-string-values,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,57.1645,324404,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,34.1591,36.1132,32.9522,32.4517,34.1591,35.1452 +mixed-distinct-string-values,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,57.979,408780,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,33.8689,33.7287,34.1008,32.6319,35.1563,33.8689 +mixed-distinct-string-values,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,58.3992,531676,834e03a3652cc22a097f0d908c610aef785658fc9bf8878f212fedd25237610c,30.7777,30.6325,30.7777,31.7086,30.522,47.0785 +hot-first-last,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.10089,97456,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.42729,6.43501,6.4373,6.39598,6.42729,6.40816 +hot-first-last,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,4.93863,98000,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,4.247,4.32594,4.20381,4.22799,4.247,4.3043 +hot-first-last,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.33966,107908,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.42094,6.43916,6.41259,6.42921,6.39983,6.42094 +hot-first-last,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,3.4625,108468,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,2.65671,2.62816,2.70298,2.82103,2.64195,2.65671 +hot-first-last,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.10706,128484,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.42485,6.42485,6.39681,6.62589,6.557,6.42275 +hot-first-last,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,1.99187,120432,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,1.59884,1.6015,1.61836,1.56471,1.59884,1.59007 +hot-first-last,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.1335,169724,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.46108,6.46108,6.42678,6.72623,6.47827,6.43422 +hot-first-last,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,1.35087,161312,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,0.988419,1.00017,0.907995,0.978095,0.996572,0.988419 +hot-first-last,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.17832,251256,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.44796,6.51339,6.44796,6.45197,6.44285,6.44794 +hot-first-last,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,1.46635,243288,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,0.622997,0.663837,0.622997,0.605296,0.642093,0.591122 +hot-first-last,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.04062,374732,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,6.3785,6.41104,7.09526,6.3785,6.36782,6.36188 +hot-first-last,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,1.98415,366292,de1c7aee90df728f2e380959e7d5f2288b6fdfb6774d1d5c58b4046a002c902c,0.534353,0.674729,0.535052,0.534353,0.532674,0.531955 +hot-symbol-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,34.9612,97628,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,35.0616,35.5271,34.5227,35.1243,34.6973,35.0616 +hot-symbol-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,34.6073,97928,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,32.9806,32.9806,33.8844,33.7369,32.721,32.9311 +hot-symbol-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,35.6489,108452,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,34.9845,36.0528,34.938,34.9845,35.0906,34.9516 +hot-symbol-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,18.4682,107992,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,17.969,17.969,18.0014,17.9599,17.9664,18.6459 +hot-symbol-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,37.0128,128612,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,36.9002,36.8419,37.5745,36.9477,36.5257,36.9002 +hot-symbol-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,10.5134,120536,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,9.64222,9.7451,9.46317,9.64222,8.95757,10.4715 +hot-symbol-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,37.7959,169620,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,37.6232,37.6996,37.6232,37.8422,35.5639,37.1516 +hot-symbol-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,6.7901,161376,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,5.76115,6.44641,5.96174,5.76115,4.84546,4.73731 +hot-symbol-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,36.7731,251704,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,37.6562,38.107,37.3799,37.1449,37.6562,38.0673 +hot-symbol-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,3.37399,243824,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,3.21096,3.22061,3.226,3.21096,3.02724,3.05445 +hot-symbol-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,36.2922,374872,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,36.4221,36.1069,36.4221,36.9108,36.8284,35.6158 +hot-symbol-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,2.60561,366844,db5c38a9f7a9a6060d996feab546074359840f34b5ed13fa6a873da74c958575,2.12837,2.12837,2.2313,2.13279,2.10055,2.12195 +hot-wide-extrema,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,20.694,117896,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,20.1218,20.0398,20.3365,20.4638,19.6031,20.1218 +hot-wide-extrema,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,21.5911,118136,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,20.2448,20.0001,20.285,19.438,20.2448,20.7405 +hot-wide-extrema,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,20.6201,128680,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.8962,20.2238,19.8186,19.8962,21.7099,19.1527 +hot-wide-extrema,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,15.2988,128668,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,11.5005,11.4481,11.5005,11.5738,11.5902,11.4607 +hot-wide-extrema,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,20.6815,149192,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,20.4596,19.8373,20.4596,20.66,19.8875,20.5253 +hot-wide-extrema,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,6.54052,133048,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,5.85769,6.28834,5.85769,5.79466,5.88911,5.77262 +hot-wide-extrema,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,20.7873,190032,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.9389,19.9389,19.7643,19.9784,20.0011,19.6341 +hot-wide-extrema,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,5.12004,173916,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,3.2098,4.6838,4.46046,3.2098,3.0083,3.05384 +hot-wide-extrema,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,20.4983,271896,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.8867,20.1361,19.946,19.8823,19.3451,19.8867 +hot-wide-extrema,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,2.92579,255592,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,2.27206,2.56623,2.15232,2.27585,2.27206,2.17499 +hot-wide-extrema,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,20.6146,396004,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,19.2109,19.0063,19.2109,19.206,19.6767,19.5286 +hot-wide-extrema,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,2.09843,379092,f433b82646d94aafcddc53b00cffa02530ada75b5c1ba5fa5b40e8dbda6cf1f2,1.70235,1.66595,1.66969,1.71751,1.70235,1.74146 +hot-mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,29.6403,171232,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.2878,11.2878,11.3226,11.2481,11.2792,11.3091 +hot-mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,29.2165,171508,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.5144,11.6239,11.554,11.5014,11.5135,11.5144 +hot-mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,29.2914,182024,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.4704,11.4704,11.496,11.4749,11.3665,11.2983 +hot-mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,6.3393,100240,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,5.34376,5.37758,5.30994,5.29725,5.36546,5.34376 +hot-mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,29.5769,202576,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.5597,11.6927,11.6207,11.533,11.446,11.5597 +hot-mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,5.30753,118484,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,2.93627,3.60308,3.4208,2.86909,2.93627,2.86832 +hot-mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,29.6096,243648,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.2924,11.3912,11.2924,11.1951,11.3342,11.2782 +hot-mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,3.32202,159784,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,2.04185,2.04185,2.03578,2.06701,2.04211,1.96361 +hot-mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,29.6737,325580,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.3577,11.3064,11.3775,11.3577,11.3679,11.2599 +hot-mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,2.90348,241720,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,1.12254,1.22812,1.12254,1.12035,1.23074,1.10594 +hot-mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,29.6428,448544,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,11.279,11.402,11.3328,11.2032,11.279,11.2074 +hot-mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,4.09177,364804,b64d0f9376d31c65a2c29100e20b1eaa9a81ceb87b9537b6ee98bf91bb511a5a,1.05377,1.09473,1.14274,1.02234,1.05377,1.05033 +hot-wide-mode,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,32.7029,199908,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4321,14.3589,14.4321,14.4596,14.4449,14.3018 +hot-wide-mode,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,32.667,200096,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4969,14.5635,14.5171,14.4969,14.4587,14.4721 +hot-wide-mode,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,32.2729,210700,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4035,14.5537,14.4049,14.389,14.4035,14.3907 +hot-wide-mode,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,8.90378,128744,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,7.46829,7.44826,7.45853,7.49488,7.46829,7.51285 +hot-wide-mode,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,32.6595,230748,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4571,14.3948,14.5188,14.5178,14.392,14.4571 +hot-wide-mode,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,5.56985,132836,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,5.0221,5.51668,5.25282,5.0221,4.79772,3.97507 +hot-wide-mode,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,32.4765,271816,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4956,14.5076,14.521,14.4074,14.4956,14.454 +hot-wide-mode,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,3.00188,174000,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,2.81793,2.89239,2.81793,2.81874,2.80569,2.65586 +hot-wide-mode,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,32.41,353628,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4524,14.4803,14.3535,14.4844,14.4275,14.4524 +hot-wide-mode,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,1.82127,255648,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,1.54677,1.68318,1.55594,1.54524,1.54521,1.54677 +hot-wide-mode,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,32.4526,477004,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,14.4102,14.3694,14.3851,14.5428,14.4102,14.5034 +hot-wide-mode,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,1.80628,379048,07cb29779cdb3eae170efb2663e4cba81e63e44f2788c36bd3f8b5a276a0ccd3,1.271,1.271,1.25462,1.27251,1.25004,1.34612 +hot-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.08298,89744,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.88321,5.90426,5.88321,5.88228,5.87999,5.89515 +hot-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,7.10101,89468,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.84672,5.88836,5.872,5.84672,5.83724,5.83902 +hot-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.0241,99824,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.78022,5.78022,5.74664,5.73915,5.7812,5.79134 +hot-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,4.30821,99984,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,3.10691,3.0903,3.15792,3.15777,3.10691,3.07746 +hot-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,6.96259,120348,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.83136,5.83136,5.85665,5.90132,5.82835,5.81463 +hot-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,3.87523,118492,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,1.88272,2.63826,2.5572,1.88272,1.79813,1.80553 +hot-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.02172,161352,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.87601,5.91026,5.87916,5.87362,5.84668,5.87601 +hot-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,3.01941,159820,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,1.28442,1.36739,1.36762,1.28442,1.21849,1.14865 +hot-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.04974,243496,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.7892,5.84847,5.81912,5.7892,5.77835,5.7729 +hot-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,2.74825,241532,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,0.745631,0.821757,0.758318,0.745631,0.735026,0.740769 +hot-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,6.97963,366564,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,5.84257,5.85456,5.83253,5.84257,5.85639,5.80202 +hot-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,3.75484,364796,0deae406d093a497ed430a8dc03bcd2e046b57e6946851149c17ebb8f8a2a178,0.60594,0.728736,0.632634,0.60594,0.577354,0.596893 +hot-wide-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,19.9263,118400,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,18.8677,19.5352,18.8677,18.6272,18.7407,19.515 +hot-wide-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,19.5688,118408,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,18.6464,18.4567,19.1148,18.9272,18.5443,18.6464 +hot-wide-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,21.1308,128664,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.4594,20.0434,19.2795,19.2497,19.4594,19.7897 +hot-wide-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,14.4274,128884,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,12.6936,12.6936,13.0649,13.7247,12.2151,12.3621 +hot-wide-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,19.6826,149428,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,18.9387,18.7658,19.0108,18.4964,19.1724,18.9387 +hot-wide-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,6.7387,133096,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,6.61764,6.59598,6.61764,6.65744,6.66092,6.58753 +hot-wide-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,20.4273,189820,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.0817,19.094,18.6159,18.9116,19.0817,19.1216 +hot-wide-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,3.66835,173916,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,4.06755,3.71608,4.12217,3.72027,4.21626,4.06755 +hot-wide-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,20.22,272400,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,18.8556,18.7548,18.9857,19.1333,18.7821,18.8556 +hot-wide-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,2.56538,255836,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,2.23039,2.31741,2.29427,2.23039,2.19903,2.19728 +hot-wide-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,20.1625,395544,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,19.0481,19.0481,18.9425,19.4186,18.8304,20.3832 +hot-wide-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,2.21912,379340,ad9fb3d60360f76b62f7ea7a2deb78bae4a64a5aa605bc8e6ec0ba68d55bba06,1.95512,1.88887,1.93051,1.95512,1.97215,2.04793 +hot-symbol-top-bottom,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,35.2429,97364,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,35.4424,35.4424,34.7906,35.5772,35.2119,35.531 +hot-symbol-top-bottom,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,34.1941,97836,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,33.9562,33.9562,33.7809,33.6279,34.13,34.1578 +hot-symbol-top-bottom,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,35.5935,108204,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,34.9613,34.9011,35.336,34.6649,34.9613,35.1195 +hot-symbol-top-bottom,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,21.6777,108440,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,20.5566,21.0289,20.3404,20.3897,21.0781,20.5566 +hot-symbol-top-bottom,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,35.9773,129008,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,35.0072,35.0072,34.677,34.6805,35.0855,35.0944 +hot-symbol-top-bottom,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,13.3514,120804,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,10.695,12.2672,11.6457,10.6108,10.695,10.45 +hot-symbol-top-bottom,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,35.9618,169088,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,34.8364,34.8788,35.4929,34.6736,34.8364,34.8182 +hot-symbol-top-bottom,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,6.48601,161120,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,6.07112,6.12245,5.99534,6.14002,6.07112,5.58467 +hot-symbol-top-bottom,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,34.7294,251692,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,34.6876,35.1222,34.6876,35.0833,34.6206,34.4978 +hot-symbol-top-bottom,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,3.67376,243864,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,3.48573,3.51139,3.468,3.48573,3.49338,3.4731 +hot-symbol-top-bottom,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,35.3818,375272,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,34.8644,34.8185,34.9852,34.4693,34.8644,34.8991 +hot-symbol-top-bottom,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,3.09585,367060,06a7912a56088c66cee0f2d2165ba95a5c4fd7a8f64efce4381781d6309d312b,2.48572,2.48572,2.55321,2.49722,2.46821,2.46494 +hot-max-k,1,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.40069,89476,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.22762,6.2146,6.212,7.26107,6.55807,6.22762 +hot-max-k,1,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,7.44743,89488,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.26316,6.28222,6.26241,6.23124,6.29532,6.26316 +hot-max-k,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.59539,99796,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.26401,6.26628,6.26401,6.52516,6.15145,6.19065 +hot-max-k,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,4.69163,99984,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,3.42091,3.4128,3.48911,3.45059,3.42091,3.38505 +hot-max-k,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.42052,120824,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.21471,6.18564,6.25544,6.34384,6.21471,6.19023 +hot-max-k,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,4.14981,118760,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,2.60527,2.7035,2.63236,2.60527,2.22703,2.10047 +hot-max-k,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.43164,161632,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.24115,6.24115,6.23406,6.42683,6.24096,6.24547 +hot-max-k,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,2.98685,159360,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,1.69518,1.79051,1.79706,1.69518,1.68616,1.68608 +hot-max-k,16,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.3535,243832,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.22784,6.20753,6.24513,6.22784,6.27611,6.21387 +hot-max-k,16,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,2.99184,241620,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,1.22919,1.3439,1.22289,1.22713,1.22919,1.27677 +hot-max-k,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,1,7.34957,367056,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,6.1794,6.17579,6.20441,6.15086,6.1794,6.65367 +hot-max-k,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,1,4.85503,364676,ff1bd50e8a74ef503fc27771e6f3ebba13e6b12e3e5070f643274620ae076e32,1.25868,1.31202,1.23995,1.25868,1.22651,1.27792 diff --git a/bench/groupby_shapes/results/2026-09-15/regression-repeat.csv b/bench/groupby_shapes/results/2026-09-15/regression-repeat.csv new file mode 100644 index 000000000..261b02d26 --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-15/regression-repeat.csv @@ -0,0 +1,131 @@ +case,workers,binary,sha256,round,rows,groups,cold_ms,peak_rss_kib,result_sha256,warm_median_ms,warm_1_ms,warm_2_ms,warm_3_ms,warm_4_ms,warm_5_ms +extrema-f32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,4.08687,169500,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.71389,3.17581,3.11941,2.38528,2.71389,2.49646 +extrema-f32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.85936,124636,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.25576,2.4247,2.25576,2.2805,1.68174,1.25328 +extrema-f32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.57255,124724,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.5967,1.39352,1.5967,2.45895,2.06551,1.39533 +extrema-f32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.72329,169632,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.67709,2.79101,3.1111,2.43715,2.67709,2.46394 +extrema-f32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.61794,169424,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.66805,2.66805,3.00664,2.42741,2.68218,2.39697 +extrema-f32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.52806,124912,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.25663,1.29678,1.32359,1.25663,1.24611,1.25231 +extrema-f32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,65536,3.83992,124668,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.40455,1.40455,1.25684,1.25873,2.45281,2.29454 +extrema-f32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,65536,2.5715,169256,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.6503,2.70382,3.06431,2.3438,2.6503,2.41981 +extrema-f32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,65536,4.04356,169736,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.81038,3.85865,4.81553,2.62366,2.81038,2.61455 +extrema-f32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,65536,2.54846,124572,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.27188,1.55926,2.35086,2.29617,2.27188,2.12225 +clustered,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,125000,2.43691,126516,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,2.39013,2.61288,2.39013,2.91749,2.07502,1.96673 +clustered,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,2.39202,118732,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.772818,1.05453,0.772818,0.775717,0.758643,0.760942 +clustered,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,1.70937,118736,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.777371,0.82431,0.790707,0.777371,0.761128,0.759089 +clustered,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,125000,2.24235,126684,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,2.31247,2.31247,2.07108,2.61803,2.12912,2.38032 +clustered,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,125000,2.24839,126748,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,2.16826,2.46362,2.16826,2.70133,2.08734,2.15422 +clustered,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,1.69989,118748,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.783317,0.815044,0.795311,0.783317,0.780613,0.779091 +clustered,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,125000,1.69865,118440,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.775143,0.8058,0.775143,0.761088,0.75241,0.779549 +clustered,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,125000,1.71598,126524,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.74296,1.74296,1.58182,1.9218,1.5443,1.75363 +clustered,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,125000,2.67731,126440,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,2.12605,2.03628,2.27549,2.92858,1.77991,2.12605 +clustered,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,125000,1.86782,118256,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.818303,0.870606,0.880624,0.818303,0.813468,0.813226 +symbol-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,4,0.749674,97936,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.673346,0.750168,0.699861,0.673346,0.649155,0.609575 +symbol-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,0.817371,98188,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.568779,0.677701,0.568779,0.561309,0.564653,0.569192 +symbol-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,0.702476,97988,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.540752,0.643312,0.551588,0.540752,0.538883,0.537548 +symbol-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,4,1.41011,97696,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.671174,1.09533,1.08926,0.671174,0.64696,0.591563 +symbol-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,4,0.660327,97484,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.512467,0.579346,0.530334,0.509724,0.500894,0.512467 +symbol-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,0.705912,98188,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.559919,0.635708,0.559919,0.55749,0.552595,0.576767 +symbol-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,4,0.710412,97696,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.555557,0.650008,0.555557,0.547029,0.542895,0.556834 +symbol-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,4,1.29045,97656,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.17719,1.23678,1.20919,1.17719,1.16874,0.979231 +symbol-key,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,4,0.68534,97848,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.503398,0.627978,0.516865,0.503398,0.488008,0.491581 +symbol-key,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,4,0.805991,97688,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.55943,0.692711,0.55943,0.541434,0.577861,0.548645 +hot-min-descending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,32769,1.49809,257848,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.75516,1.69009,2.09445,1.75516,1.24746,2.52095 +hot-min-descending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,32769,2.0985,161368,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.923214,1.03553,0.993843,0.923214,0.908375,0.897479 +hot-min-descending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,32769,2.10608,161372,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.908615,1.01658,0.897536,0.920216,0.906227,0.908615 +hot-min-descending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,32769,1.33165,257768,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.53628,1.57657,1.53628,1.21111,1.28022,2.40623 +hot-min-descending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,32769,1.66355,257860,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.88494,1.79823,2.32189,1.88494,1.35268,2.53363 +hot-min-descending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,32769,1.89255,161404,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.8492,0.917871,0.856067,0.8492,0.813047,0.80092 +hot-min-descending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,32769,2.2535,161616,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.10402,1.14838,1.12315,1.10402,0.9098,0.85995 +hot-min-descending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,32769,1.40893,257528,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.69136,1.62931,2.24501,1.69136,1.35397,2.83948 +hot-min-descending,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,32769,1.03827,257564,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,1.57683,1.38631,2.06819,1.57683,1.24911,1.8203 +hot-min-descending,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,32769,1.88539,161480,945768a99a38df53236cda69351f9fef91a23d923f3abf3a8049970cfe62ab6e,0.813905,0.857187,0.813905,0.802349,0.829783,0.710638 +sum-f64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.62546,206824,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,3.88479,3.94292,3.88479,4.27081,3.01971,2.96594 +sum-f64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.97627,161724,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.754,1.46256,1.3794,1.754,1.78727,1.77378 +sum-f64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.70244,161564,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.4527,1.66727,1.4527,1.41103,1.40874,1.48535 +sum-f64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.75409,200084,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.94859,2.94859,2.65956,3.22853,2.68948,3.46272 +sum-f64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.66167,200800,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.42944,2.83554,2.42944,2.81092,1.35121,1.59207 +sum-f64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.22372,161544,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.0464,1.13346,1.05327,1.0464,1.04041,1.03656 +sum-f64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,65536,2.24001,161676,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.23161,1.23161,1.23034,1.23924,1.25304,1.16787 +sum-f64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,65536,1.6064,200320,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.04122,1.61502,1.41343,2.04122,2.52466,2.79378 +sum-f64,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,65536,2.59907,200208,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.80062,2.80062,2.47282,3.36509,2.51328,2.8145 +sum-f64,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,65536,2.88599,161564,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.78086,1.83088,1.81117,1.78086,1.77669,1.74543 +float-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.92142,819804,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.15878,5.63066,6.3294,4.55934,6.15878,8.58149 +float-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.84049,374976,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.18987,2.58385,2.164,2.18987,2.18824,2.2467 +float-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.15777,374836,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.2822,2.2965,2.2822,2.24954,2.22045,2.33349 +float-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,4.161,832132,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.15968,5.75418,6.15968,6.85909,5.3009,7.04579 +float-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.85572,813140,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,7.0949,5.65434,7.18281,5.23497,7.0949,7.56902 +float-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.04019,375004,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.25022,2.31227,2.30432,2.23589,2.16254,2.25022 +float-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,65536,5.27923,375196,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.33997,2.48931,2.23428,2.23047,2.33997,2.53461 +float-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,65536,4.72359,826196,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,7.04304,6.36825,8.076,6.35272,7.93669,7.04304 +float-key,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,65536,3.70525,809572,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,6.48782,5.41723,6.48782,4.7066,7.5643,8.50841 +float-key,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,65536,4.84094,374852,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,2.21645,2.25807,2.20935,2.21645,2.26374,2.21285 +clustered,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,125000,1.93778,200588,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,2.02544,2.02544,1.56233,2.37905,1.65232,2.05646 +clustered,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,2.58387,163440,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.758322,0.912167,0.826152,0.756079,0.758322,0.734024 +clustered,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,2.39215,163664,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.06646,1.18389,1.20364,1.06646,0.98011,1.063 +clustered,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,125000,1.76735,200808,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.96428,2.12959,1.90693,3.28938,1.57154,1.96428 +clustered,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,125000,2.2915,199660,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.55818,2.13285,1.33281,1.84089,1.32127,1.55818 +clustered,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,2.50632,163628,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.20434,1.19723,1.24949,1.06005,1.24485,1.20434 +clustered,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,125000,2.86219,163748,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.29132,1.24258,1.30206,1.27134,1.40125,1.29132 +clustered,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,125000,2.03672,200516,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,2.21067,2.43349,1.90843,2.80911,1.80601,2.21067 +clustered,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,125000,1.91602,200580,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,2.23246,2.23246,1.81998,2.66468,1.67275,2.34898 +clustered,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,125000,2.20893,163964,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.945608,1.48,1.03119,0.945608,0.940728,0.844593 +sparse,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,5.85339,770704,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,6.19572,6.48557,5.87007,6.93711,6.19252,6.19572 +sparse,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.02654,731648,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,6.17207,6.56527,6.17207,4.97523,5.35233,8.33468 +sparse,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.48535,764904,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.8902,5.8902,6.7032,6.63667,5.40733,5.47557 +sparse,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,5.77954,762444,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,7.2753,6.71253,7.08264,8.15809,7.2753,7.94827 +sparse,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,6.14774,762472,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,6.41961,5.78266,5.3711,7.13558,6.41961,6.93821 +sparse,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.64653,758428,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.65306,5.45036,5.2141,7.59995,5.65306,5.76106 +sparse,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,65536,6.21397,754684,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,6.30986,7.3652,5.63024,7.58338,6.30986,5.56642 +sparse,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,65536,5.64822,764568,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,6.09349,5.86747,6.09349,6.88886,5.80965,6.83491 +sparse,default,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,65536,5.92019,758648,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,6.12955,6.01398,6.12955,7.4184,5.37778,7.03939 +sparse,default,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,65536,6.64159,764220,01972ab0fd51c783c88722805ff688e4e3cedb929ff314dc14dcdc03d2d1c1c8,5.66828,5.66828,5.10862,6.13426,5.19184,7.35541 +truth,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.8932,103916,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,3.73739,3.75188,3.71917,3.25946,3.81414,3.73739 +truth,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.797,93900,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,2.63265,2.70349,2.58027,2.63265,2.65063,2.58346 +truth,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.21388,93572,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,2.29946,2.30607,2.29946,2.27144,2.26794,2.30962 +truth,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.99588,104120,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,4.01798,4.53803,4.15629,3.78434,4.01798,3.80221 +truth,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,3.79856,104132,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,3.61375,3.74329,3.66195,3.26275,3.61375,3.51488 +truth,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.32875,93720,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,2.24444,2.32779,2.28947,2.24444,2.22725,2.21543 +truth,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,65536,3.16824,93896,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,2.26093,2.28994,2.26093,2.22803,2.24465,2.26638 +truth,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,65536,5.99338,104076,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,4.1834,6.4285,5.30611,3.78825,4.1834,3.97418 +truth,2,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,65536,5.79375,103856,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,3.8123,5.70675,3.65702,3.55608,3.82494,3.8123 +truth,2,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,65536,3.3341,93808,11d9f81debed2ffce7195bff361d2a6ba11cfaefa8a80b7c103e03b96dd7f669,2.52676,2.72029,2.60725,2.52676,2.50474,2.49543 +nonnull,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.9715,257984,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,3.372,3.372,3.64349,2.2009,1.96843,3.64738 +nonnull,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.37439,165772,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.16021,2.46474,2.16021,2.2132,2.06917,1.99739 +nonnull,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.99883,165648,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,1.78269,1.92522,1.70567,1.78269,1.84131,1.37642 +nonnull,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,3.73568,257152,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.466,3.62276,2.95126,1.97798,1.68904,2.466 +nonnull,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.45408,257376,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,3.06972,3.06972,3.28028,2.82414,2.31564,3.19257 +nonnull,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.42815,165980,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.11112,2.19033,2.003,1.97775,2.11112,2.12308 +nonnull,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,65536,2.7361,165784,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,1.43376,1.5632,1.44919,1.43376,1.42478,1.42696 +nonnull,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,65536,1.90351,257728,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,3.63881,2.16604,3.63881,3.64291,2.61655,3.78351 +nonnull,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,65536,2.49346,257504,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,2.9307,2.9307,3.44244,2.76821,2.43861,4.28556 +nonnull,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,65536,3.19139,165640,37029694adc4216655775cf92fb184871eb4c04cc53f24d59c7cb8e89b05a218,1.90327,1.68894,1.87875,1.91946,1.90937,1.90327 +product,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.26292,257960,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.47757,2.42342,2.9043,2.47757,2.19852,3.20397 +product,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.48579,165736,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.33545,1.86958,1.78937,1.33545,1.27942,1.19649 +product,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.57002,165460,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.38894,1.40483,1.38894,1.39516,1.17665,1.17866 +product,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.26037,257784,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.61814,2.55756,2.92443,2.61814,2.15343,3.29075 +product,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.21338,257552,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.49302,2.49904,2.89639,2.49302,1.5717,2.18305 +product,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.26076,165684,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,0.764137,1.07832,0.812049,0.764137,0.603463,0.600869 +product,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,65536,3.0516,165304,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.44947,1.6215,1.41767,1.4057,1.44947,1.47902 +product,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,65536,2.07943,258164,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.34117,2.34117,2.7796,2.31875,1.88071,2.92193 +product,8,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,65536,2.32638,258052,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,2.58939,2.58939,3.01472,2.34799,1.97965,3.33955 +product,8,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,65536,2.60013,165700,05fe562cb8d0b362f52c0f2a54e5d5ab5914be8889a4c4a09cfef0e7b4573be2,1.80365,1.38883,1.14425,1.83173,1.80585,1.80365 +extrema-i32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,3.3278,169472,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.66431,2.97618,2.77241,2.33779,2.66431,2.24087 +extrema-i32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.59245,102368,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.35272,2.38317,2.35272,2.36495,2.28912,2.28521 +extrema-i32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.40951,102084,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.31057,1.30275,1.32528,1.2376,1.31057,1.3289 +extrema-i32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.47227,169744,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.36325,2.36325,2.67339,2.02333,2.43273,2.14965 +extrema-i32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.34002,169988,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.7374,2.37401,2.7374,2.38366,2.83777,2.92853 +extrema-i32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.4484,102064,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.2846,1.3073,1.27509,1.40095,1.2846,1.28247 +extrema-i32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,65536,1.35436,102076,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.23612,1.25083,1.2476,1.22821,1.21616,1.23612 +extrema-i32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,65536,2.23232,169488,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.2875,2.2875,2.66767,1.98897,2.38214,2.10183 +extrema-i32,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,65536,2.32167,169448,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,2.42232,2.53019,2.74233,2.13634,2.42232,2.13963 +extrema-i32,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,65536,2.43468,102560,4898738f30509da2f14fee436fc21c3382e34c12d831b3de2f193a08af899061,1.49972,2.35913,2.3642,1.49972,1.27902,1.30112 +sum-time,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,1,1000000,65536,2.1833,159304,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.38928,1.76482,1.20771,1.56328,1.20935,1.38928 +sum-time,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.54088,135048,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.41612,1.36205,1.3354,1.41629,1.41658,1.41612 +sum-time,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.910958,135092,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,0.741838,0.760374,0.741008,0.742714,0.736118,0.741838 +sum-time,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,2,1000000,65536,2.47689,159224,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,2.19795,2.33496,2.13489,2.95563,1.95738,2.19795 +sum-time,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,3,1000000,65536,2.24126,159064,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.47506,1.58327,1.36165,1.51591,1.47506,1.35736 +sum-time,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.08401,134608,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,0.73645,0.760754,0.741068,0.73461,0.736344,0.73645 +sum-time,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,65536,1.70144,134856,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.4315,1.45104,1.4315,1.42517,1.43007,1.44562 +sum-time,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,4,1000000,65536,2.20476,159372,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.83723,2.29121,2.00021,1.83723,1.24612,1.39828 +sum-time,4,baseline,f9ed90d41e7bff92707a796e4677772351a23e53a6e148cf831a3feb92ae880e,5,1000000,65536,2.10588,159244,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,1.60546,1.98045,1.22188,1.60905,1.22602,1.60546 +sum-time,4,current,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,65536,1.07851,134668,14d770a6cddb0512cc7e5c17ff3d2f6ddd8f7583fb91b0ba6f44dcc446428f0d,0.795549,0.78511,0.889358,0.795549,0.733332,0.820722 diff --git a/bench/groupby_shapes/results/2026-09-15/topk.csv b/bench/groupby_shapes/results/2026-09-15/topk.csv new file mode 100644 index 000000000..f619eebb7 --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-15/topk.csv @@ -0,0 +1,31 @@ +case,workers,binary,processes,cold_median_ms,cold_min_ms,cold_max_ms,warm_median_ms,warm_min_ms,warm_max_ms,peak_rss_kib +topk-k1024,1,baseline,3,12.300058,10.846972,13.746716,11.284429,10.802132,11.316363,75576 +topk-k1024,1,current,3,11.411387,11.049837,12.989153,11.205686,11.064206,11.597321,75568 +topk-k1024,2,baseline,3,10.89963,10.865237,11.088383,10.931895,10.919874,11.268303,75588 +topk-k1024,2,current,3,6.340218,6.10268,7.639043,5.961478,5.002112,6.09708,75616 +topk-k1024,4,baseline,3,10.966465,10.861603,13.783205,11.784867,11.196765,13.754593,75584 +topk-k1024,4,current,3,4.277831,3.309514,4.590614,3.068007,2.925685,3.28973,75848 +topk-k1024,8,baseline,3,10.892762,10.8871,13.620971,10.957506,10.843468,13.651051,75588 +topk-k1024,8,current,3,2.986781,2.515783,3.831533,2.147878,2.135349,2.176372,76028 +topk-k1024,16,baseline,3,10.873852,10.857507,11.201275,10.889095,10.841436,11.364596,75564 +topk-k1024,16,current,3,2.379032,2.347969,2.390542,1.917026,1.819151,2.595744,76024 +topk-k1024,default,baseline,3,10.865945,10.85488,11.075247,10.962551,10.827867,11.238119,76028 +topk-k1024,default,current,3,2.429796,2.368251,2.695446,1.795333,1.638305,2.398588,76504 +topk-k65536,1,baseline,3,53.531554,53.35107,53.802673,53.544175,53.365793,54.0194,75580 +topk-k65536,1,current,3,53.815967,53.58734,53.824275,53.570852,53.549891,53.627212,75528 +topk-k65536,2,baseline,3,53.470171,53.36737,53.513807,53.409142,53.391146,53.49992,75552 +topk-k65536,2,current,3,49.862001,46.609331,52.77388,39.448359,38.514748,46.753366,137868 +topk-k65536,4,baseline,3,53.605443,53.429992,55.701292,53.570112,53.341266,55.724197,75592 +topk-k65536,4,current,3,37.417574,33.518196,38.706513,25.533153,21.779494,25.65863,137640 +topk-k65536,8,baseline,3,53.523274,53.511723,55.581343,53.396983,53.338453,54.500503,75604 +topk-k65536,8,current,3,25.729305,25.46986,29.578336,20.432292,16.486803,23.705901,137184 +topk-k65536,16,baseline,3,53.486883,53.45758,53.633105,53.516659,53.336914,53.579611,75792 +topk-k65536,16,current,3,19.445869,18.844174,19.45691,18.055834,17.390005,18.334977,137024 +topk-k65536,default,baseline,3,53.779995,53.323816,54.264221,53.400857,53.380389,53.521439,76020 +topk-k65536,default,current,3,16.618184,16.022741,16.943637,17.512047,15.856185,19.666095,135420 +topk-k4000003,1,current,3,785.378454,775.297452,854.277818,772.736533,753.571619,790.445878,168052 +topk-k4000003,2,current,3,607.185955,604.851981,635.229334,600.899441,592.557223,601.927905,199696 +topk-k4000003,4,current,3,291.998585,286.094661,292.375116,278.550761,273.874177,278.77021,199008 +topk-k4000003,8,current,3,164.679004,160.720852,172.817135,159.593952,150.183166,160.599564,198596 +topk-k4000003,16,current,3,131.154004,129.682969,135.12972,132.267032,125.633564,135.057423,198600 +topk-k4000003,default,current,3,97.46706,96.526381,104.909471,89.194126,88.078586,101.335575,197912 diff --git a/bench/groupby_shapes/results/2026-09-16-audit/environment.json b/bench/groupby_shapes/results/2026-09-16-audit/environment.json new file mode 100644 index 000000000..fc143f8d6 --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-16-audit/environment.json @@ -0,0 +1,39 @@ +{ + "kernel": "Linux-6.17.9-76061709-generic-x86_64-with-glibc2.35", + "cpu": { + "Architecture": "x86_64", + "CPU(s)": "28", + "On-line CPU(s) list": "0-27", + "Model name": "Intel(R) Core(TM) i7-14700", + "Thread(s) per core": "2", + "Core(s) per socket": "20", + "Socket(s)": "1", + "L2 cache": "28 MiB (11 instances)", + "L3 cache": "33 MiB (1 instance)" + }, + "compiler": "cc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0", + "build_flags": "-O3 -march=native -funroll-loops -fomit-frame-pointer -fno-math-errno -falign-functions=64 -fassociative-math -ffp-contract=fast -fno-signed-zeros -fno-trapping-math", + "governor": "powersave", + "scaling_driver": "intel_pstate", + "candidate_revision": "fdf365f3a52af3ceeb0814c59ba7be272d8b419f", + "baseline_revision": "f61a4eda", + "candidate_sha256": "ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b", + "baseline_sha256": "1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353", + "workers": [ + "1", + "8", + "default" + ], + "fresh_processes_per_case": 3, + "warm_executions_per_process": 5, + "typed_result_comparisons": 144, + "asan_ubsan_tests": 3832, + "extra_query_fixture_total_threads": 28, + "repeat_typed_result_comparisons": 30, + "repeat_cases": [ + "binary", + "clustered", + "symbol-key" + ], + "repeat_processes_per_binary_case": 5 +} diff --git a/bench/groupby_shapes/results/2026-09-16-audit/grouping.csv b/bench/groupby_shapes/results/2026-09-16-audit/grouping.csv new file mode 100644 index 000000000..c4ca73814 --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-16-audit/grouping.csv @@ -0,0 +1,49 @@ +case,workers,binary,processes,cold_median_ms,cold_min_ms,cold_max_ms,warm_median_ms,warm_min_ms,warm_max_ms,peak_rss_kib +sum-i64,1,baseline,3,1.9172,1.90816,2.13185,1.73701,1.69312,1.82658,71480 +sum-i64,1,current,3,1.8382,1.83153,1.99528,1.68742,1.67677,1.71362,71292 +sum-i64,8,baseline,3,2.49397,2.49152,2.94683,1.39298,1.12134,1.72383,161464 +sum-i64,8,current,3,2.62528,2.34893,2.85765,1.39838,1.38255,1.71561,161980 +sum-i64,default,baseline,3,2.97725,2.93811,3.06305,0.733329,0.713338,0.762998,370924 +sum-i64,default,current,3,3.2502,2.98288,3.30766,0.724268,0.674241,0.730773,371148 +count,1,baseline,3,1.8556,1.85015,1.87158,1.67401,1.65619,1.68246,71300 +count,1,current,3,1.87066,1.84748,1.87457,1.66649,1.66344,1.66681,71284 +count,8,baseline,3,1.23593,1.21399,1.50363,1.09761,1.08494,1.24993,143400 +count,8,current,3,1.27361,1.20776,1.47122,1.14493,1.10638,1.3881,143416 +count,default,baseline,3,0.748798,0.741953,0.756636,0.599537,0.581825,0.650663,348652 +count,default,current,3,0.732917,0.721196,0.775408,0.573866,0.572316,0.592809,348360 +statistics,1,baseline,3,6.10103,6.05098,6.25015,4.8946,4.84671,5.35281,85404 +statistics,1,current,3,6.13461,6.13212,6.20899,5.02677,4.66411,5.06692,85476 +statistics,8,baseline,3,3.85259,3.48161,3.95084,2.58888,2.03801,2.96305,170024 +statistics,8,current,3,3.92909,3.76509,4.37152,2.45131,2.41979,2.56875,170120 +statistics,default,baseline,3,4.33439,4.23464,4.67399,1.32619,1.3233,1.39404,375096 +statistics,default,current,3,4.1326,4.12224,4.33775,1.31638,1.30423,1.37156,374872 +binary,1,baseline,3,15.4814,15.0889,16.5799,15.6362,12.4528,15.6889,116372 +binary,1,current,3,15.4497,14.9873,19.157,12.86,12.789,16.5981,116568 +binary,8,baseline,3,6.51876,4.60469,7.27592,3.71318,3.2148,4.16533,186888 +binary,8,current,3,6.00828,5.24393,6.75521,3.34102,3.31073,3.78333,186752 +binary,default,baseline,3,5.6956,5.38717,5.71288,2.43092,2.3585,2.43258,392012 +binary,default,current,3,6.0178,5.97952,7.85846,3.03359,2.55737,5.49806,391940 +composite,1,baseline,3,15.8327,15.1398,16.4185,11.6953,11.2643,12.3051,137388 +composite,1,current,3,15.2347,15.0857,21.8541,11.3547,11.3266,17.7249,137128 +composite,8,baseline,3,7.28061,6.37715,7.83999,4.84998,4.28946,4.93538,202660 +composite,8,current,3,7.0761,7.03014,7.68335,4.34676,3.69107,4.45247,202880 +composite,default,baseline,3,8.48006,7.90511,8.67093,2.25607,1.98612,2.30067,407868 +composite,default,current,3,8.30413,7.92899,8.6468,1.99139,1.92586,1.99142,407984 +selected,1,baseline,3,3.77469,3.7042,4.02929,3.37948,3.36218,3.62671,71252 +selected,1,current,3,3.77824,3.73307,3.93776,3.3907,3.35379,3.49299,71524 +selected,8,baseline,3,1.41062,1.37205,1.60384,1.14126,1.13312,1.22663,153428 +selected,8,current,3,1.40048,1.39119,1.93655,1.18404,1.08854,1.35526,153428 +selected,default,baseline,3,1.35941,1.24316,1.36517,0.901919,0.861955,0.96265,358884 +selected,default,current,3,1.37131,1.35175,1.44684,0.903514,0.871609,0.918037,358884 +clustered,1,baseline,3,1.78628,1.76608,1.90334,1.59449,1.57996,1.64408,71536 +clustered,1,current,3,1.79807,1.79151,1.80955,1.59415,1.59224,1.67785,71452 +clustered,8,baseline,3,2.66395,1.98327,2.7436,1.32793,1.0135,1.34301,163800 +clustered,8,current,3,2.51941,2.28807,2.58137,1.05501,0.949648,1.09832,163628 +clustered,default,baseline,3,2.87836,2.74468,2.88501,0.629884,0.605832,0.68483,368916 +clustered,default,current,3,3.012,2.94033,3.33202,0.73048,0.630971,0.954527,369036 +symbol-key,1,baseline,3,1.29807,1.26205,1.31118,1.08469,1.04314,1.17314,87848 +symbol-key,1,current,3,1.30281,1.28222,1.36459,1.08693,1.04942,1.09749,87660 +symbol-key,8,baseline,3,0.370056,0.322495,0.546335,0.232323,0.229067,0.237255,159512 +symbol-key,8,current,3,0.334127,0.320804,0.379759,0.207622,0.206635,0.262759,159468 +symbol-key,default,baseline,3,0.594801,0.583727,0.598412,0.23216,0.223414,0.246635,365020 +symbol-key,default,current,3,0.588633,0.55326,0.600995,0.249214,0.23969,0.26386,365028 diff --git a/bench/groupby_shapes/results/2026-09-16-audit/processes.csv b/bench/groupby_shapes/results/2026-09-16-audit/processes.csv new file mode 100644 index 000000000..5f11d1165 --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-16-audit/processes.csv @@ -0,0 +1,145 @@ +case,workers,binary,sha256,round,rows,groups,cold_ms,peak_rss_kib,result_sha256,warm_median_ms,warm_1_ms,warm_2_ms,warm_3_ms,warm_4_ms,warm_5_ms +sum-i64,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.90816,71480,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.69312,1.71842,1.69312,1.67756,1.67458,1.69458 +sum-i64,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,1.8382,71216,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.68742,1.69406,1.69003,1.68742,1.68618,1.68673 +sum-i64,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.49397,161200,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.39298,1.38991,1.39298,1.4247,1.41505,1.38664 +sum-i64,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,2.62528,161644,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.39838,1.4569,1.41024,1.38831,1.36211,1.39838 +sum-i64,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,2.97725,370444,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.762998,1.39351,0.762998,0.797098,0.710806,0.650983 +sum-i64,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,3.30766,370936,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.730773,1.34146,0.784263,0.679031,0.716119,0.730773 +count,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.85015,71256,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.67401,1.67294,1.6687,1.67401,1.69201,1.6806 +count,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,1.87457,71032,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.66344,1.6815,1.66241,1.6588,1.66622,1.66344 +count,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.50363,143400,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.24993,1.38624,1.23418,1.19813,1.24993,1.29644 +count,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,1.20776,142908,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.10638,1.0887,1.10638,1.13922,1.10464,1.14807 +count,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,0.756636,348356,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.581825,0.581825,0.827805,0.579464,0.572679,0.61463 +count,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,0.775408,348124,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.592809,0.630258,0.564709,0.603293,0.592809,0.547878 +statistics,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.05098,85404,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,4.8946,4.93337,4.87528,4.95533,4.85675,4.8946 +statistics,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,6.13212,85332,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,5.06692,5.09358,5.15796,5.06692,5.00828,5.01536 +statistics,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.95084,169588,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.96305,2.72829,2.35618,2.96305,3.05667,2.97315 +statistics,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,4.37152,169588,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.45131,3.09115,2.76781,2.45131,2.40983,2.31863 +statistics,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,4.23464,375096,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,1.32619,1.83204,1.29948,1.33133,1.29207,1.32619 +statistics,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,4.33775,374812,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,1.37156,1.88812,1.3624,1.37897,1.37156,1.35678 +binary,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,15.4814,115712,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,15.6362,15.8881,13.5585,16.0086,13.0179,15.6362 +binary,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,15.4497,116568,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,12.789,12.4526,12.7914,12.789,12.6875,12.9293 +binary,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,6.51876,186348,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,4.16533,4.14007,4.36978,4.16533,4.61431,3.99701 +binary,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,5.24393,186268,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,3.31073,3.38609,3.17129,2.98721,3.31073,3.99599 +binary,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.6956,391456,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.3585,3.07011,2.40673,2.3585,2.3284,2.27441 +binary,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,6.0178,391548,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,3.03359,3.25422,3.03359,2.62223,2.63322,3.05871 +composite,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,458752,16.4185,136852,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,11.6953,12.7318,11.4364,11.6953,11.8614,11.5883 +composite,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,458752,15.0857,136908,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,11.3266,12.1269,11.3266,11.5633,11.2266,11.3061 +composite,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,458752,6.37715,202324,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,4.28946,5.22028,4.28946,4.33272,3.93803,3.64174 +composite,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,458752,7.0761,202640,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,3.69107,5.53684,3.94404,3.42447,3.65586,3.69107 +composite,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,458752,8.48006,407864,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,1.98612,4.10235,2.25729,1.92714,1.98612,1.88447 +composite,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,458752,8.30413,407664,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,1.92586,3.92664,2.23381,1.89995,1.92586,1.89401 +selected,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,3.7042,71056,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.36218,3.40924,3.37043,3.36218,3.35886,3.36212 +selected,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,3.77824,71524,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.3907,3.41372,3.37881,3.3907,3.36801,3.3996 +selected,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.60384,153188,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.22663,1.29279,1.23567,1.22663,1.2216,1.21741 +selected,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,1.93655,153428,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.35526,1.49935,1.35196,1.35526,1.38094,1.2555 +selected,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,1.35941,358364,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,0.861955,1.00141,0.910292,0.849524,0.861955,0.852379 +selected,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,1.44684,358460,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,0.903514,1.00625,1.21224,0.903514,0.893646,0.892664 +clustered,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,1.76608,71216,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.59449,1.68386,1.59449,1.59433,1.60348,1.59361 +clustered,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,125000,1.79151,71320,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.59415,1.70593,1.60185,1.59415,1.58712,1.58741 +clustered,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,2.66395,163684,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.34301,1.34301,1.34557,1.33134,1.36516,1.18213 +clustered,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,125000,2.28807,163260,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.949648,0.979337,1.1746,0.946033,0.949648,0.895527 +clustered,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,2.74468,368916,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.605832,0.758195,0.667105,0.605832,0.57478,0.536789 +clustered,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,125000,2.94033,368664,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.630971,0.784948,0.622793,0.582378,0.630971,0.660108 +symbol-key,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,1.26205,87588,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.04314,1.20423,1.05977,1.04314,1.03501,1.03968 +symbol-key,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,4,1.36459,87576,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.09749,1.25693,1.11627,1.08453,1.09749,1.09209 +symbol-key,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,0.546335,159512,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.237255,0.306006,0.241936,0.237255,0.204439,0.206828 +symbol-key,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,4,0.334127,159468,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.206635,0.270852,0.21706,0.206635,0.203918,0.183704 +symbol-key,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,0.594801,364212,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.246635,0.326544,0.349219,0.246635,0.204524,0.208011 +symbol-key,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,4,0.55326,364756,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.23969,0.324704,0.255308,0.23969,0.227527,0.222677 +symbol-key,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,4,0.600995,364468,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.249214,0.326842,0.264427,0.249214,0.234721,0.228507 +symbol-key,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,0.598412,364528,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.223414,0.334957,0.249454,0.223414,0.222419,0.201615 +symbol-key,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,4,0.320804,159432,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.207622,0.266049,0.218276,0.207622,0.201209,0.202719 +symbol-key,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,0.322495,159364,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.229067,0.236743,0.234826,0.229067,0.213374,0.222108 +symbol-key,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,4,1.30281,87660,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.08693,1.24542,1.103,1.08474,1.08693,1.08033 +symbol-key,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,1.29807,87644,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.17314,1.26952,1.1878,1.17266,1.17314,1.15785 +clustered,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,125000,3.012,368372,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.954527,1.14633,1.11615,0.940705,0.760188,0.954527 +clustered,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,2.87836,368892,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.629884,0.846108,0.640956,0.629884,0.606598,0.589273 +clustered,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,125000,2.51941,163448,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.05501,1.1367,1.05501,1.02635,1.07769,0.994302 +clustered,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,1.98327,163800,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.0135,1.03233,1.22827,0.97279,0.949608,1.0135 +clustered,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,125000,1.80955,71452,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.67785,1.73986,1.68422,1.66433,1.66649,1.67785 +clustered,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,1.78628,71300,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.57996,1.67928,1.58792,1.57996,1.57752,1.57224 +selected,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,1.37131,358840,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,0.918037,1.09059,0.928472,0.918037,0.879546,0.878511 +selected,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.24316,358036,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,0.901919,1.00917,0.901919,0.90242,0.901573,0.895891 +selected,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,1.39119,153232,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.18404,1.16976,1.18404,1.1945,1.14787,1.18903 +selected,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.41062,153340,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.13312,1.12816,1.12538,1.13889,1.15024,1.13312 +selected,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,3.73307,71092,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.35379,3.40088,3.35228,3.35379,3.34745,3.5108 +selected,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.77469,71156,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.37948,3.37948,3.37855,3.54872,3.38126,3.3681 +composite,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,458752,7.92899,407728,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,1.99142,3.91897,2.23156,1.90393,1.96778,1.99142 +composite,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,458752,7.90511,407516,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,2.30067,4.3951,2.51793,2.30067,2.24863,2.28542 +composite,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,458752,7.03014,202880,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,4.34676,5.2165,4.5679,4.34676,4.29788,4.23635 +composite,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,458752,7.83999,202660,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,4.93538,5.90538,5.06447,4.8373,4.88332,4.93538 +composite,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,458752,15.2347,137088,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,11.3547,11.6401,11.385,11.2414,11.3547,11.3117 +composite,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,458752,15.8327,136672,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,12.3051,12.3051,12.1421,11.727,12.4241,12.5348 +binary,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,5.97952,391940,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.55737,3.11722,2.55737,2.72491,2.39389,2.3818 +binary,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.38717,392012,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.43258,3.16607,2.43258,2.41467,2.42741,2.5053 +binary,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,6.00828,186752,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,3.78333,4.10222,3.39022,3.41458,3.78333,3.80465 +binary,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,7.27592,186392,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,3.71318,4.959,3.74157,3.71284,3.71318,3.27795 +binary,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,19.157,116036,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,16.5981,17.0383,19.8346,16.4339,16.5981,15.9605 +binary,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,16.5799,116372,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,15.6889,13.4751,15.032,18.299,15.6889,19.6512 +statistics,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,4.1326,374816,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,1.31638,2.04214,1.30617,1.31312,1.34923,1.31638 +statistics,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,4.33439,374708,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,1.39404,1.8965,1.44521,1.36025,1.34017,1.39404 +statistics,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,3.76509,169764,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.56875,2.56875,2.43148,2.52251,3.03466,2.84248 +statistics,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.48161,170024,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.03801,2.39114,2.35618,2.03801,1.85572,1.93681 +statistics,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,6.20899,85352,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,5.02677,5.18242,5.04186,5.00174,4.98467,5.02677 +statistics,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,6.25015,85356,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,5.35281,5.35281,5.11863,4.75898,8.35155,6.58648 +count,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,0.721196,348360,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.572316,0.610747,0.572316,0.568132,0.577914,0.557764 +count,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,0.741953,348088,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.650663,0.650663,0.640136,0.605974,0.666269,0.683038 +count,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,1.27361,143416,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.14493,1.12977,1.14493,1.16852,1.14825,1.13907 +count,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.21399,143380,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.08494,1.15089,1.0584,1.08494,1.10574,0.968577 +count,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,1.84748,71284,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.66681,1.67765,1.65853,1.6694,1.66681,1.66672 +count,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,1.8556,71300,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.65619,1.67879,1.64965,1.66957,1.65338,1.65619 +sum-i64,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,3.2502,371148,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.674241,1.43226,0.747788,0.6682,0.674241,0.639944 +sum-i64,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,3.06305,370768,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.733329,1.4545,0.726211,0.733329,0.703767,0.737311 +sum-i64,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,2.34893,161980,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.38255,1.18787,1.14887,1.47876,1.43512,1.38255 +sum-i64,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.49152,161244,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.12134,1.37977,1.42645,0.972509,1.12134,1.06529 +sum-i64,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,1.99528,71292,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.71362,1.75811,1.70843,1.71562,1.71362,1.6974 +sum-i64,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,2.13185,71248,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.82658,1.82658,1.84477,1.81342,1.81194,1.91477 +sum-i64,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.9172,71076,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.73701,1.7273,1.70429,1.73701,1.79339,1.78548 +sum-i64,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,1.83153,71252,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.67677,1.68738,1.6731,1.68314,1.67283,1.67677 +sum-i64,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.94683,161464,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.72383,1.81282,1.72844,1.72383,1.72122,1.5016 +sum-i64,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,2.85765,161824,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,1.71561,1.70875,1.71561,1.71823,1.73586,1.3496 +sum-i64,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,2.93811,370924,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.713338,1.3803,0.799027,0.650956,0.713338,0.688128 +sum-i64,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,2.98288,370508,67815070ec5e825828d32b5e6b3a37db6aa89f046b6f7390a537494ae110c572,0.724268,1.41633,0.764672,0.668993,0.724268,0.696158 +count,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.87158,71024,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.68246,1.68846,1.68989,1.67067,1.67903,1.68246 +count,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,1.87066,70948,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.66649,1.72081,1.66649,1.6628,1.67025,1.66401 +count,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.23593,142900,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.09761,1.10546,1.11481,1.09761,0.989354,1.06338 +count,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,1.47122,143200,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,1.3881,1.4597,1.34773,1.3881,1.49064,1.3317 +count,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,0.748798,348652,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.599537,0.601315,0.593439,0.588514,0.599537,0.614309 +count,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,0.732917,348180,67457a1d5b29926989e3244d85a4cf3b2f8a21363195d6a2759f4bbe298cb663,0.573866,0.577893,0.577649,0.553719,0.503497,0.573866 +statistics,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,6.10103,85188,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,4.84671,5.67928,4.84671,4.86314,4.70893,4.63656 +statistics,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,6.13461,85476,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,4.66411,5.02251,4.76536,4.66411,4.65116,4.65509 +statistics,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,3.85259,169548,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.58888,2.60694,2.4947,2.58653,2.58888,2.82071 +statistics,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,3.92909,170120,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,2.41979,2.32547,2.68465,2.58336,2.41979,2.20217 +statistics,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.67399,374808,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,1.3233,1.88171,1.3233,1.35482,1.25885,1.28323 +statistics,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,4.12224,374872,ce92f3f44a9c9c78cd8ca1d9813b099e3f33f0d5ed37dd9f3bccd3bbe9164741,1.30423,1.88206,1.28221,1.34454,1.30423,1.29045 +binary,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,15.0889,116312,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,12.4528,12.4064,12.6507,12.3989,12.4528,13.1193 +binary,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,14.9873,116368,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,12.86,12.86,12.8769,12.5127,12.8621,12.5873 +binary,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.60469,186888,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,3.2148,3.56369,3.25083,3.2148,3.18521,3.18875 +binary,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,6.75521,186464,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,3.34102,4.71323,3.18594,2.77891,3.34102,3.78152 +binary,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.71288,391352,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.43092,3.05584,2.3653,2.3339,2.43092,2.60675 +binary,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,7.85846,391700,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,5.49806,5.69647,5.60858,5.49806,4.35673,5.16196 +composite,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,458752,15.1398,137388,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,11.2643,12.0878,11.1916,11.6464,11.1477,11.2643 +composite,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,458752,21.8541,137128,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,17.7249,17.9455,18.0139,17.7249,17.5243,17.413 +composite,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,458752,7.28061,202640,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,4.84998,5.61486,5.50083,4.84998,4.69405,4.4597 +composite,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,458752,7.68335,202604,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,4.45247,5.40654,4.57821,4.45247,4.15639,3.86593 +composite,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,458752,8.67093,407868,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,2.25607,4.12867,2.25607,1.93717,1.94822,2.42756 +composite,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,458752,8.6468,407984,89e28455f2fc78110cb132870bf7c66b4c81609c003a643a742f2a50c43ee976,1.99139,4.07144,2.25547,1.98421,1.99139,1.97732 +selected,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,4.02929,71252,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.62671,3.62671,3.61029,3.73111,3.86989,3.50206 +selected,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,3.93776,71036,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,3.49299,3.69986,3.57178,3.46557,3.49299,3.38035 +selected,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.37205,153428,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.14126,1.23533,1.10501,1.0721,1.15209,1.14126 +selected,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,1.40048,153208,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,1.08854,1.13344,1.10426,1.07618,1.08423,1.08854 +selected,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,1.36517,358884,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,0.96265,0.982459,0.990654,0.96265,0.873074,0.878193 +selected,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,1.35175,358884,8522e3c9de155d55f5248f18156623a96083744742ecf5754c6432bd416e0da2,0.871609,1.00052,0.873026,0.870383,0.871609,0.868258 +clustered,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,1.90334,71536,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.64408,1.74952,1.64612,1.64408,1.63707,1.59937 +clustered,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,125000,1.79807,71292,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.59224,1.67652,1.59224,1.59394,1.58784,1.58416 +clustered,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,2.7436,163536,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.32793,1.32793,1.38547,1.36762,1.15458,1.27843 +clustered,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,125000,2.58137,163628,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,1.09832,1.29729,1.38049,0.973292,0.963873,1.09832 +clustered,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,2.88501,368400,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.68483,0.923784,0.7261,0.676285,0.624032,0.68483 +clustered,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,125000,3.33202,369036,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.73048,0.897862,0.775478,0.73048,0.676375,0.560138 +symbol-key,1,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,1.31118,87848,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.08469,1.24337,1.12177,1.07342,1.08469,1.06846 +symbol-key,1,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,4,1.28222,87628,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,1.04942,1.21433,1.10352,1.04942,1.04094,1.03854 +symbol-key,8,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,0.370056,159456,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.232323,0.243544,0.218495,0.232323,0.232928,0.224379 +symbol-key,8,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,4,0.379759,159456,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.262759,0.271263,0.264593,0.250703,0.262759,0.245388 +symbol-key,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,0.583727,365020,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.23216,0.333511,0.263869,0.23216,0.221192,0.203686 +symbol-key,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,4,0.588633,365028,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.26386,0.341258,0.332693,0.26386,0.227276,0.231342 diff --git a/bench/groupby_shapes/results/2026-09-16-audit/repeat-processes.csv b/bench/groupby_shapes/results/2026-09-16-audit/repeat-processes.csv new file mode 100644 index 000000000..0eedec58d --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-16-audit/repeat-processes.csv @@ -0,0 +1,31 @@ +case,workers,binary,sha256,round,rows,groups,cold_ms,peak_rss_kib,result_sha256,warm_median_ms,warm_1_ms,warm_2_ms,warm_3_ms,warm_4_ms,warm_5_ms +binary,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,65536,5.89175,391120,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.36238,3.25999,2.44531,2.35428,2.33267,2.36238 +binary,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,65536,5.91557,391120,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.29456,2.99477,2.33756,2.25905,2.29456,2.29169 +clustered,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,125000,2.88908,368660,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.645145,0.783886,0.698182,0.645145,0.598365,0.579393 +clustered,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,125000,2.79391,368776,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.603448,0.70631,0.626912,0.554668,0.603448,0.589904 +symbol-key,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,1,1000000,4,0.59698,364636,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.222821,0.378489,0.28276,0.222821,0.206449,0.197566 +symbol-key,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,1,1000000,4,0.602685,364260,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.211118,0.309729,0.247595,0.211118,0.202233,0.200629 +symbol-key,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,4,0.582663,364528,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.229149,0.300791,0.245124,0.229149,0.197264,0.198919 +symbol-key,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,4,0.597973,364932,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.270357,0.379639,0.282162,0.270357,0.255393,0.241143 +clustered,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,125000,2.91088,368920,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.580974,0.785536,0.723045,0.580974,0.562593,0.53865 +clustered,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,125000,2.85804,368688,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.669892,0.789635,0.700387,0.669892,0.567699,0.585841 +binary,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,2,1000000,65536,5.45229,391048,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.31068,2.99883,2.31068,2.30405,2.3275,2.30369 +binary,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,2,1000000,65536,5.91447,391360,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.33232,3.12764,2.33597,2.33232,2.32733,2.27769 +binary,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,65536,5.69237,391472,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.39357,3.17554,2.4016,2.3646,2.35371,2.39357 +binary,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,65536,5.11199,391528,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.36222,3.73585,2.36222,2.53494,2.30777,2.31459 +clustered,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,125000,2.91438,368664,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.619325,0.807253,0.734162,0.619325,0.549856,0.568802 +clustered,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,125000,2.86803,368572,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.610024,0.793735,0.676571,0.596921,0.610024,0.609765 +symbol-key,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,3,1000000,4,0.560579,364508,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.237495,0.322844,0.237495,0.228066,0.236653,0.253669 +symbol-key,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,3,1000000,4,0.588016,364524,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.257872,0.451,0.29146,0.257872,0.243948,0.230008 +symbol-key,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,4,1000000,4,0.586222,364764,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.227315,0.324147,0.251721,0.227315,0.203056,0.200766 +symbol-key,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,4,0.568215,364496,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.233798,0.331092,0.246403,0.233798,0.204844,0.205781 +clustered,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,4,1000000,125000,2.82288,368652,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.605434,0.740494,0.676974,0.605434,0.534103,0.584426 +clustered,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,125000,2.92315,368932,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.626003,0.720621,0.626003,0.630622,0.601544,0.567921 +binary,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,4,1000000,65536,6.49526,391468,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.32828,3.05766,2.32828,2.28743,2.24317,2.35525 +binary,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,4,1000000,65536,6.13455,391476,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.28711,3.09966,2.33503,2.24562,2.28711,2.26761 +binary,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,65536,5.44895,391496,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.34941,3.07027,2.33892,2.40147,2.34941,2.32993 +binary,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,5,1000000,65536,5.78132,391788,a07c1dabc08383bc311d6241d6f8f29ca0845609ef637c571d284a88105dca45,2.31598,3.06165,2.35428,2.26824,2.31598,2.30934 +clustered,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,125000,2.92659,368400,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.58954,0.888407,0.7107,0.58954,0.583594,0.579485 +clustered,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,5,1000000,125000,2.77178,368908,9226fbbc34dc78a0eb0d9731b6075244eb0d8c9daf05af27419f638bf9d95c9c,0.579778,0.712898,0.652861,0.565083,0.541309,0.579778 +symbol-key,default,baseline,1003a83830e8b12ae1cb86bdec98af3d649ae54404b59a1466ec31548b149353,5,1000000,4,0.585992,364756,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.254363,0.343141,0.254363,0.344411,0.216216,0.202344 +symbol-key,default,current,ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b,5,1000000,4,0.565357,364736,f4f0cd4a2b69b2ea50a501980d32272f8f90ca85d2e6c4191191f90689bb19db,0.23108,0.322835,0.271226,0.23108,0.202451,0.206937 diff --git a/bench/groupby_shapes/results/2026-09-16-audit/repeat.csv b/bench/groupby_shapes/results/2026-09-16-audit/repeat.csv new file mode 100644 index 000000000..29f7e7390 --- /dev/null +++ b/bench/groupby_shapes/results/2026-09-16-audit/repeat.csv @@ -0,0 +1,7 @@ +case,workers,binary,processes,cold_median_ms,cold_min_ms,cold_max_ms,warm_median_ms,warm_min_ms,warm_max_ms,peak_rss_kib +binary,default,baseline,5,5.89175,5.44895,6.13455,2.34941,2.28711,2.39357,391496 +binary,default,current,5,5.78132,5.11199,6.49526,2.31598,2.29456,2.36222,391788 +clustered,default,baseline,5,2.91438,2.85804,2.92659,0.626003,0.58954,0.669892,368932 +clustered,default,current,5,2.82288,2.77178,2.91088,0.603448,0.579778,0.610024,368920 +symbol-key,default,baseline,5,0.585992,0.560579,0.597973,0.237495,0.222821,0.270357,364932 +symbol-key,default,current,5,0.586222,0.565357,0.602685,0.229149,0.211118,0.257872,364764 diff --git a/bench/groupby_shapes/scaling_common.py b/bench/groupby_shapes/scaling_common.py new file mode 100644 index 000000000..1e5fc3c17 --- /dev/null +++ b/bench/groupby_shapes/scaling_common.py @@ -0,0 +1,36 @@ +"""Shared comparison and profile parsing for synthetic scaling benchmarks.""" +import csv +import math +import re + + +def profile_phases(log): + return [dict(label=label, ms=float(value)) for label, value in + re.findall(r"✶\s+(.+): ([0-9.]+) ms", log)] + + +def compare_csv(reference, current, schema, key_names=("k",)): + """Exact keys/types/nulls; round-trip floats with explicit tolerances.""" + with schema.open(newline="") as stream: + kinds = {r["column"]: r["kind"] for r in csv.DictReader(stream)} + with reference.open(newline="") as a, current.open(newline="") as b: + left_reader, right_reader = csv.DictReader(a), csv.DictReader(b) + if left_reader.fieldnames != right_reader.fieldnames or left_reader.fieldnames != list(kinds): + raise RuntimeError("Result columns differ") + keys = [name for name in key_names if name in kinds] + key = lambda row: tuple(row[name] for name in keys) + # Canonicalize independently of engine sort/null tie behavior. + left, right = sorted(left_reader, key=key), sorted(right_reader, key=key) + if len(left) != len(right): + raise RuntimeError("Result row counts differ") + for row_number, (row, other) in enumerate(zip(left, right)): + if row_number and (key(row) == key(left[row_number - 1]) or key(other) == key(right[row_number - 1])): + raise RuntimeError("Duplicate output group key") + for name, value in row.items(): + got = other[name] + if value == got: + continue + if name in key_names or kinds[name] not in {"F32", "F64"} or not value or not got: + raise RuntimeError(f"Result differs at row {row_number}, column {name}: {value!r} / {got!r}") + if not math.isclose(float(value), float(got), rel_tol=1e-9, abs_tol=1e-8): + raise RuntimeError(f"Float differs at row {row_number}, column {name}: {value} / {got}") diff --git a/bench/groupby_shapes/summarize_scaling.py b/bench/groupby_shapes/summarize_scaling.py new file mode 100644 index 000000000..9e7809ce1 --- /dev/null +++ b/bench/groupby_shapes/summarize_scaling.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""Summarize fresh-process grouping measurements without discarding outliers.""" +import argparse +import csv +import json +from pathlib import Path +import statistics + + +def summarize(records): + groups = {} + for row in records: + groups.setdefault((row['case'], row['workers'], row['binary']), []).append(row) + for (case, workers, binary), rows in groups.items(): + warm = [statistics.median(row['warm_ms']) for row in rows] + cold = [row['cold_ms'] for row in rows] + yield dict(case=case, workers=workers, binary=binary, processes=len(rows), + cold_median_ms=statistics.median(cold), cold_min_ms=min(cold), cold_max_ms=max(cold), + warm_median_ms=statistics.median(warm), warm_min_ms=min(warm), warm_max_ms=max(warm), + peak_rss_kib=max(row['peak_rss_kib'] for row in rows)) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('input', type=Path) + parser.add_argument('output', type=Path) + parser.add_argument('--process-output', type=Path, + help='also retain each process and all five warm timings') + args = parser.parse_args() + records = json.loads(args.input.read_text()) + rows = list(summarize(records)) + if not rows: + parser.error('no measurements') + with args.output.open('w', newline='') as stream: + writer = csv.DictWriter(stream, fieldnames=list(rows[0]), lineterminator="\n") + writer.writeheader() + writer.writerows(rows) + if args.process_output: + process_rows = [] + for record in records: + row = {key: record[key] for key in ( + 'case', 'workers', 'binary', 'sha256', 'round', 'rows', 'groups', + 'cold_ms', 'peak_rss_kib', 'result_sha256')} + row['warm_median_ms'] = statistics.median(record['warm_ms']) + row.update({f'warm_{i + 1}_ms': value for i, value in enumerate(record['warm_ms'])}) + process_rows.append(row) + with args.process_output.open('w', newline='') as stream: + writer = csv.DictWriter(stream, fieldnames=list(process_rows[0]), lineterminator="\n") + writer.writeheader() + writer.writerows(process_rows) + pairs = {(row['case'], row['workers'], row['binary']): row for row in rows} + for row in rows: + old = pairs.get((row['case'], row['workers'], 'baseline')) + if row['binary'] != 'current' or not old: + continue + ratio = old['warm_median_ms'] / row['warm_median_ms'] + print(f"{row['case']:24} {row['workers']:>7} " + f"{old['warm_median_ms']:9.3f} -> {row['warm_median_ms']:9.3f} ms " + f"({ratio:.2f}x; current process medians {row['warm_min_ms']:.3f}..{row['warm_max_ms']:.3f})") + + +if __name__ == '__main__': + main() diff --git a/bench/groupby_shapes/topk_consumer.c b/bench/groupby_shapes/topk_consumer.c new file mode 100644 index 000000000..e09544c0b --- /dev/null +++ b/bench/groupby_shapes/topk_consumer.c @@ -0,0 +1,79 @@ +/* Direct indexed-consumer benchmark. The query language separately caps K at + * 1024; the internal kernel accepts larger K. Verify every result using an + * independent histogram, outside the timed interval. */ +#define _POSIX_C_SOURCE 200809L +#include +#include "ops/internal.h" +#include "core/pool.h" +#include "core/profile.h" +#include +#include +#include +#include + +static double now_ms(void) { + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return t.tv_sec * 1000.0 + t.tv_nsec / 1000000.0; +} +static bool verify(ray_t* out, const int64_t* histogram, int64_t expected, bool desc) { + if (!out || RAY_IS_ERR(out) || out->type != RAY_LIST || out->len != 1) return false; + ray_t* cell = ray_list_get(out, 0); + if (!cell || cell->type != RAY_I64 || cell->len != expected) return false; + const int64_t* values = ray_data(cell); + int64_t at = 0; + for (int code = 0; code < 4096 && at < expected; code++) { + int value = desc ? 4095 - code : code; + for (int64_t i = 0; i < histogram[value] && at < expected; i++) + if (values[at++] != value) return false; + } + return at == expected; +} +int main(int argc, char** argv) { + if (argc != 3) { fprintf(stderr, "usage: topk-consumer ROWS K\n"); return 2; } + char *end_n, *end_k; + int64_t n = strtoll(argv[1], &end_n, 10), k = strtoll(argv[2], &end_k, 10); + if (*end_n || *end_k || n < 1 || n > INT32_MAX || k < 1 || k > INT32_MAX) return 2; + ray_runtime_t* runtime = ray_runtime_create(0, NULL); + if (!runtime) return 1; + const char* setting = getenv("RAYFORCE_CORES"); + ray_pool_destroy(); + if (ray_pool_init_total(setting ? (uint32_t)strtoul(setting, NULL, 10) : 0) != RAY_OK) return 1; + ray_t* src = ray_vec_new(RAY_I64, n); + int64_t* rows = ray_alloc_raw((size_t)n * sizeof(int64_t)); + if (!src || RAY_IS_ERR(src) || !rows) return 1; + src->len = n; src->attrs |= RAY_ATTR_HAS_NULLS; + int64_t histogram[4096] = {0}, valid = 0, offset = 0; + for (int64_t i = 0; i < n; i++) { + int64_t value = i * 37 % 4096; + bool missing = i % 97 == 0; + ((int64_t*)ray_data(src))[i] = missing ? NULL_I64 : value; + if (!missing) { histogram[value]++; valid++; } + rows[i] = n - 1 - i; + } + double ms[6]; + g_ray_profile.active = getenv("TOPK_PROFILE") != NULL; + for (unsigned run = 0; run < 6; run++) { + ray_profile_reset(); ray_profile_tick("topK: begin"); + double begin = now_ms(); + ray_t* top = ray_topk_per_group_buf(src, k, 1, rows, &offset, &n, 1); + ray_t* bot = ray_topk_per_group_buf(src, k, 0, rows, &offset, &n, 1); + ms[run] = now_ms() - begin; + if (g_ray_profile.active) { + for (int32_t i = 1; i < g_ray_profile.n; i++) + fprintf(stderr, "run=%u %s %.6f ms\n", run, g_ray_profile.spans[i].msg, + (g_ray_profile.spans[i].ts - g_ray_profile.spans[i - 1].ts) / 1000000.0); + } + int64_t expected = k < valid ? k : valid; + if (!verify(top, histogram, expected, true) || !verify(bot, histogram, expected, false)) { + fprintf(stderr, "top/bottom K differs from histogram oracle\n"); return 1; + } + ray_release(top); ray_release(bot); + } + struct rusage usage; + getrusage(RUSAGE_SELF, &usage); + printf("{\"actual_workers\":%u,\"cold_ms\":%.6f,\"warm_ms\":[%.6f,%.6f,%.6f,%.6f,%.6f],\"peak_rss_kib\":%ld}\n", + ray_pool_total_workers(ray_pool_get()), ms[0], ms[1], ms[2], ms[3], ms[4], ms[5], usage.ru_maxrss); + ray_free_raw(rows); ray_release(src); ray_runtime_destroy(runtime); + return 0; +} diff --git a/bench/groupby_shapes/topk_scaling.py b/bench/groupby_shapes/topk_scaling.py new file mode 100644 index 000000000..e9330f42c --- /dev/null +++ b/bench/groupby_shapes/topk_scaling.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Compare indexed top/bottom-K kernels against an independent histogram oracle. + +Build each checkout with `make lib`, then compile topk_consumer.c against that +checkout's librayforce.a, using -Iinclude -Isrc -lm -lpthread. This Linux harness +reports ru_maxrss in KiB. Query-language K limits are not changed by this test. +""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import statistics +import subprocess + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--binary', type=Path, required=True) + parser.add_argument('--baseline', type=Path, required=True) + parser.add_argument('--rows', type=int, default=4000003) + parser.add_argument('--ks', default='1024,65536,4000003') + parser.add_argument('--baseline-skip-ks', default='', help='K values with a recorded baseline correctness failure') + parser.add_argument('--workers', default='1,2,4,8,16,default') + parser.add_argument('--rounds', type=int, default=3) + parser.add_argument('--output', type=Path, required=True) + args = parser.parse_args() + ks, workers = [int(k) for k in args.ks.split(',')], args.workers.split(',') + if args.rows < 1 or args.rounds < 1 or any(k < 1 for k in ks): + parser.error('rows, rounds and K must be positive') + if any(w != 'default' and (not w.isdecimal() or int(w) < 1) for w in workers): + parser.error('workers must be positive or default') + baseline_skip = {int(k) for k in args.baseline_skip_ks.split(',') if k} + if baseline_skip - set(ks): + parser.error('baseline skip K must be in --ks') + binaries = [('baseline', args.baseline.resolve()), ('current', args.binary.resolve())] + hashes = {name: hashlib.sha256(path.read_bytes()).hexdigest() for name, path in binaries} + matrix = [(k, worker, name, path) for k in ks for worker in workers for name, path in binaries + if not (name == 'baseline' and k in baseline_skip)] + records = [] + for round_number in range(1, args.rounds + 1): + for k, worker, name, path in matrix if round_number % 2 else reversed(matrix): + env = os.environ.copy() + env.pop('RAYFORCE_CORES', None) + if worker != 'default': + env['RAYFORCE_CORES'] = worker + run = subprocess.run([str(path), str(args.rows), str(k)], env=env, + capture_output=True, text=True, check=True) + result = json.loads(run.stdout) + if worker != 'default' and result['actual_workers'] != int(worker): + raise RuntimeError('worker setting was not honored') + result.update(case=f'topk-k{k}', workers=worker, binary=name, + round=round_number, rows=args.rows, k=k, sha256=hashes[name]) + records.append(result) + args.output.write_text(json.dumps(records, indent=2) + '\n') + print(f'{name} K={k} workers={worker} round={round_number}: ' + f'warm={statistics.median(result["warm_ms"]):.3f} ms', flush=True) + print(f'Histogram oracle matched all {len(records)} runs.') + + +if __name__ == '__main__': + main() diff --git a/bench/groupby_shapes/type_coverage.rfl b/bench/groupby_shapes/type_coverage.rfl new file mode 100644 index 000000000..beb41d829 --- /dev/null +++ b/bench/groupby_shapes/type_coverage.rfl @@ -0,0 +1,40 @@ +;; Run identical release binaries sequentially: rayforce -c 8 this-file.rfl. +;; Milliseconds; first repetition includes cold allocations. Data is deterministic. +(set n 1000000) +(set i (til n)) +(set v (% (* i 13) 10000)) +(set base (table [v] (list v))) +(set f32 (at (select {from:base x:(as 'F32 v)}) 'x)) +(set t (table [k narrow tm f32 f64 wide] + (list (as 'I32 (% (* i 17) 200000)) (as 'I32 v) (as 'TIME v) + f32 (as 'F64 v) v))) +(set t (update {from:t k:(as 'I32 0N) where:(== k 0)})) +(println "nullable I32 key / min TIME") +(map (fn [_] (println (timeit (select {from:t by:k s:(min tm)})))) (til 6)) +(println "I32 sum and extrema") +(map (fn [_] (println (timeit (select {from:t by:k s:(sum narrow) m:(min narrow)})))) (til 6)) +(println "F32 extrema") +(map (fn [_] (println (timeit (select {from:t by:k lo:(min f32) hi:(max f32)})))) (til 6)) +(println "F64 existing average and variance") +(map (fn [_] (println (timeit (select {from:t by:k a:(avg f64) v:(var f64)})))) (til 6)) +(println "mixed streaming and median") +(map (fn [_] (println (timeit (select {from:t by:k s:(sum wide) m:(med f64)})))) (til 6)) +(println "F32 filter and top 100") +(map (fn [_] (println (timeit (select {from:t where:(> f32 5000.0) asc:f32 take:100 k:k v:f32})))) (til 6)) +(println "composite F32 sort") +(map (fn [_] (println (timeit (select {from:t asc:[f32 k] take:100 k:k v:f32})))) (til 6)) +(println "nullable count-distinct TIME") +(map (fn [_] (println (timeit (select {from:t by:k n:(count (distinct tm))})))) (til 6)) +(println "typed string broadcast") +(map (fn [_] (println (timeit (select {from:t by:k n:(count wide) label:"row"})))) (til 6)) +(println "temporal expression") +(map (fn [_] (println (timeit (select {from:t by:k s:(sum (+ tm tm))})))) (til 6)) +(set symbols (table [k v] (list (at ['alpha 'beta 'gamma 'delta] (% i 4)) (at t 'tm)))) +(println "SYM count-distinct TIME") +(map (fn [_] (println (timeit (select {from:symbols by:k n:(count (distinct v))})))) (til 6)) +(set words (table [k v] (list (at ["long pooled alpha string" "beta" "gamma" "delta"] (% i 4)) v))) +(println "STR grouping key") +(map (fn [_] (println (timeit (select {from:words by:k s:(sum v)})))) (til 6)) +(set floats (table [k v] (list f32 v))) +(println "F32 grouping key") +(map (fn [_] (println (timeit (select {from:floats by:k s:(sum v)})))) (til 6)) diff --git a/docs/aggregation-fast-path-plan.md b/docs/aggregation-fast-path-plan.md new file mode 100644 index 000000000..b57c7af07 --- /dev/null +++ b/docs/aggregation-fast-path-plan.md @@ -0,0 +1,379 @@ +# Query fast-path type coverage plan + +This records the original generic coverage requirements. Current scaling work +is tracked in the [grouping engine plan](grouping-engine-scaling-plan.md). + +## Objective and scope + +Make efficient query execution cover the types and null semantics already +supported by the language. A supported aggregate should not lose the optimized +grouping engine merely because its input is I32, F32, or TIME. A query with +several aggregates should not silently regress when one unsupported +specialization is added. + +This census covers all 14 ordinary value kinds (BOOL, U8, I16, I32, I64, F32, +F64, DATE, TIME, TIMESTAMP, GUID, SYM, STR, LIST) across grouped aggregation, +group keys, and the adjacent count-distinct, predicate, top-k, sort, expression, +and output-broadcast paths inspected below. It is not an exhaustive audit of +joins, windows, graph operations, or every arithmetic operator. + +Compound/heterogeneous LIST values need an explicit generic strategy, not +reinterpretation as fixed-width numeric data. Preserve deliberately invalid +operations and resource limits. Do not remove a gate until all downstream +readers, null checks, state merges, and output writers support its new cases. + +## Evidence + +### Runtime census + +[aggregation-type-census.csv](aggregation-type-census.csv) records 210 probes: +14 input kinds × 15 unary operations, checking registry presence, scalar result +type, and grouped result-column type. Four rows, two integer-key groups, one +execution core; linked against the baseline release objects. Primitive fixtures +contain 1,2,3,4 (BOOL casts to true); SYM/STR contain b,a,b,c; GUID uses four +generated GUIDs; LIST contains four boxed integers. F32 was constructed with +a query projection cast because the eval-level F32 cast is not supported. +Actual fixture types were checked before the probes. + +These probes establish availability and output types, not value correctness, +parallel routing, null behavior, or performance. `ERROR` is an observed error, +not automatically a bug. The operation spellings include `var_pop` and +`stddev_pop`. Binary aggregates and parameterized aggregates were inspected in +source, but are not represented by this CSV. + +## Coverage census + +### A. Aggregate registry + +Source: `src/ops/agg_stream.c:agg_resolve`, with admission in +`src/ops/agg_engine.c:agg_v2_can_handle`. + +| ID | Operation family | Registered at baseline | Coverage planned | +|---|---|---|---| +| A1 | sum | I64, F64 | BOOL/U8/I16/I32/F32 and TIME | +| A2 | min/max | I64, F64 | BOOL/U8/I16/I32/F32; DATE/TIME/TIMESTAMP; SYM/STR/GUID | +| A3 | avg | F64 | BOOL/U8/I16/I32/I64/F32 and DATE/TIME/TIMESTAMP | +| A4 | var/var_pop/stddev/stddev_pop | I64, F64 | BOOL/U8/I16/I32/F32 and DATE/TIME/TIMESTAMP | +| A5 | first/last | none | All supported scalar kinds; explicit handling for boxed LIST | +| A6 | prod | none | Numeric inputs, including F32; preserve temporal rejection | +| A7 | all/any | BOOL/U8/I16/I32/I64/F64 | F32; homogeneous boxed input only through a safe generic path | +| A8 | pearson/cov/scov/wsum/wavg | BOOL/U8/I16/I32/I64/F64 inputs | F32, including mixed input pairs; verify each operation's scalar type contract before considering temporals | +| A9 | med, top/bottom K | I64/F64 buffered vtables | Other supported types; buffered admission and memory strategy also required | +| A10 | quantile/percentile, mode | none | Dedicated state/parameter contracts for their existing supported types | +| A11 | count | Type-independent | Preserve count-all-rows behavior; registry presence does not imply key-path admission | + +Important distinctions: + +- A missing registration is not always the only blocker: `agg_v2_can_handle` + currently rejects non-streaming vtables, including the registered med/top-K + vtables. The expression adapter repeats admission restrictions. +- Missing any one required vtable can send the entire grouped query to legacy + execution. Test mixed queries, not just one aggregate at a time. +- SUM(DATE/TIMESTAMP), PROD(temporals), and ALL/ANY(temporals) remain invalid. + Do not infer legality from an identical storage width. +- On these runtime probes, F32 reductions returned F64, including min/max and + first/last. Preserve current behavior until a separate semantic decision is + made; do not assume every extreme must return its input type. +- Narrow integer and temporal extremes retain their logical types; TIME sum + returns TIME. Integer sums/products return I64, and means/statistics return F64. +- Scalar med(BOOL/F32) succeeds but grouped med(BOOL/F32) errors on the fixture. + Treat these as correctness investigations in the first work package, before + attempting to use grouped legacy execution as an oracle for those cells. +- The LIST fixture contains only numbers. It establishes no contract for + arbitrary mixed objects, nesting, or null cells. + +### B. Keys, nullability, and strategy selection + +| ID | Gap | Evidence and required work | +|---|---|---| +| K1 | Nullable integer/temporal keys lose v2 dense and radix | `agg_dense_plan`, `agg_dense_plan_sel`, and `exec_group_v2_run` reject may-have-null metadata for non-SYM keys. Add explicit null-key representation in dense/radix plans. | +| K2 | Float, GUID, STR, LIST keys cannot enter v2 | `agg_v2_can_handle` admits integer/temporal/SYM only. Some lower helpers/comments mention STR/F64 fallback, but they do not constitute an end-to-end supported route. Add typed hash/equality/gather support before admission. | +| K3 | Generic parallel hash overallocates | `exec_group_v2_parallel` allocates each worker for input-sized group/state capacity and a hash table sized from total rows. Bound memory as key support expands; avoid O(workers × input) slabs. | +| K4 | Legacy alternatives have narrower aggregate support | Legacy direct-insert accepts COUNT/SUM/AVG only, and nullable inputs are declined; legacy dense/sparse key gates also reject nullable non-SYM. Prefer a complete v2 route over duplicating every new kernel in legacy code. | + +K1 is necessary for nullable temporal grouping: merely registering MIN(TIME) can move the +query to a different expensive fallback. Nullable signed integer sentinels may +be used in a proven canonical representation; null must remain distinct from +zero and ordinary extreme values. Dense planning must exclude null from min/max +range estimation and budget a separate null slot. Composite keys need a null +identity per component. BOOL/U8 vectors are non-nullable in this revision; +do not invent a null bitmap contract for them. + +For floats, grouping must canonicalize signed zero and null/NaN consistently +with existing equality and distinct semantics. For symbols, preserve adaptive +width and dictionary domains. STR/GUID need full values or stable row references, +never truncation to an int64. Generic LIST keys retain deep equality semantics. + +### C. Related query fast paths + +| ID | Confirmed coverage restriction | Source | +|---|---|---| +| Q1 | Fused count-distinct accepts I16/I32/I64/SYM only; omits BOOL/U8/temporals and floats/wide types | `cdfuse.c:cdf_type_ok`; caller's two-stage rewrite admits more integer/temporal types | +| Q2 | SYM fused count-distinct is unreachable through its null gate | `ray_cd_fused` rejects `ray_vec_may_have_nulls`, which always returns true for SYM; the outer rewrite uses the exact null test instead | +| Q3 | Fused predicates omit F32/F64, GUID, ordinary non-dictionary STR comparisons; nullable numeric/temporal columns are declined | `fused_pred.c:fp_atom_col_compatible`, predicate admission and `fp_col_supported_op` | +| Q4 | Fused filter + top-k sort keys omit F32/F64 and GUID | `fused_topk.c:ray_fused_topk_select` type gate; predicate support is a separate gate | +| Q5 | Composite radix sort excludes F32 when there is more than one sort key | `sort.c:sort_indices_ex`, `t == RAY_F32 && n_cols != 1` | +| Q6 | Typed literal broadcasts omit F32/GUID/STR | `query.c:can_atom_broadcast`, `atom_broadcast_vec`; fallback allocates per-group cells | +| Q7 | Query numeric admission omits F32 while lower expression code admits it for pow | `query.c:dag_numeric_type_admitted` / `dag_pow_type_admitted` vs `expr.c:ew_pow_type_admitted` | +| Q8 | Temporal arithmetic inside aggregate expressions forces eval | `query.c:expr_contains_temporal_arith`, `is_group_dag_agg_expr_dag_safe`; a semantic guard for units and result types, not a safe gate-only change | + +Wide STR/GUID sort already has rank/comparison alternatives; calling that +fallback does not establish that it is slow. Benchmark before replacing it. +Similarly, Q3 gates protect null comparisons and temporal conversions. Reuse +the existing comparison contract (including null ordering), not SQL-style +assumptions about null predicates. + +Q1/Q2 changes must preserve `(count (distinct x))`, including how null contributes +to distinct, rather than adopting SQL COUNT(DISTINCT) semantics. An exact +null-free scan can restore SYM admission initially, but measure its cost; a +null-capable canonical code kernel is the eventual route. + +## Implementation sequence + +Each work package should be reviewable and leave fallback execution correct. +The order below is the intended delivery order; dependencies are explicit. + +### P0 — Lock semantic contracts and expose routing + +Covers all IDs; prerequisite for new admission. + +1. Turn the census into maintained, table-driven contract cases in + `test/test_agg_registry.c`, `test/test_agg_engine.c`, and Rayfall query tests. + Separate language legality, result type, registry support, and strategy + support. Include scalar and grouped values, not only availability. +2. Resolve the BOOL/F32 grouped-med discrepancies with minimal reproducers and + explicit expected semantics. Check scalar/grouped F32 return-type behavior + across filtered, unfiltered, serial, parallel, and persisted inputs. +3. Add test-visible strategy counters or a diagnostic record: admitted engine, + chosen strategy, and decline reason (unsupported input/output type, + nullable key, buffered aggregate, expression, resource budget). Integrate + optional profiler reporting without adding work inside hot row loops. +4. Expand fixtures to every legal op/type cell, binary mixed-type pairs, and + parameterized aggregates. Track intentional rejection cells explicitly. + +Acceptance: tests can prove that the intended fast path ran; an equal answer +from fallback alone cannot satisfy a fast-path regression test. + +### P1 — Complete typed accumulator plumbing + +Covers prerequisites for A1–A10 and K2. Depends on P0. + +- Extend `agg_acc.h` validity to F32; its current switch handles F64 NaN and + signed sentinels, but defaults other types to valid. Resolve slice validity + once per batch. Keep a no-null loop selected outside the row loop. +- Introduce typed input readers/kernels; do not alias TIME/I32/F32 to an I64 + pointer. Centralize logical result type and accumulator storage type as + distinct concepts. +- Replace both `agg_put_cell` and `agg_put_cell_value`: today every non-F64 + scalar output is written as int64. That would overwrite narrow output + buffers if new narrow/temporal registrations were simply enabled. +- Keep parallel payload writes disjoint and merge null metadata after workers + finish. Audit serial, dense, generic hash, radix, and partition emitters. +- Prepare a source-column/row context for value-preserving extrema and ordered + aggregates. The existing vtable has no source-row parameter in update_batch; + parallel first/last needs original row identity, including filtered input. +- Support native-width bulk finalization where useful to avoid allocating one + temporary scalar per output group. Keep ownership and OOM cleanup explicit. + +Acceptance: width-safe output under ASan/UBSan; typed nulls and domain/owner +lifetimes correct; no new registration enabled before its full pipeline works. + +### P2 — Numeric and temporal streaming coverage + +Covers A1–A4, A6–A8. Depends on P1. + +- Generate or share typed sum/min/max/avg/prod/statistics loops for every legal + numeric/temporal cell in section A. Add F32 boolean and binary statistics + readers and mixed-pair dispatch; retain temporal legality checks per op. +- Preserve unsigned-wrap integer reduction behavior, float normalization, + zero/empty identities, all-null results, and sample/population distinctions. + Do not combine this change with a new statistical algorithm or precision policy. +- Admit the same capabilities in plain-column and materialized-expression + adapters. Route mixed aggregate sets together where the engine supports all + states. Ensure COUNT still counts input rows, including null values. + +Acceptance: full op/type value parity, exact result types, boundary integers +above 2^53 for integer extrema, and no regression for existing I64/F64 kernels. + +### P3 — Nullable keys and bounded grouping memory + +Covers K1/K3/K4. Depends on P1; combine with P2 for nullable-key acceptance. + +- Add null-aware dense slot mapping and radix key encoding, including selected + rows, all-null columns, composite keys, and null-preserving key emission. +- Use byte-aware, overflow-safe memory budgets for worker slabs and scatter + buffers. Grow generic hash tables with observed groups or partition input + so each worker does not reserve total-input capacity. +- Retain sparse-range and excessive-domain fallback; never allocate a dense + array spanning a signed null sentinel to the largest real key. +- Benchmark dense vs radix using actual range/cardinality and worker count. + Do not optimize a particular workload by an unconditional dispatch override. +- Leave legacy as a correctness fallback. Only extend its direct-insert merge + operations if profiling shows a remaining workload that v2 cannot address. + +Acceptance: Nullable-key MIN(TIME) uses an efficient null-aware route and improves +repeatably against the same-machine baseline, with identical typed results and +reported scratch/RSS. No hardcoded claimed speedup before measurement. + +### P4 — Ordered and wide-value aggregates + +Covers A2/A5 and the scalar-value part of A9/A10. Depends on P1/P2. + +- Add first/last using source-row position, not worker merge order. Match the + established skip-null semantics. Preserve original row positions through + selection compaction, radix scatter, expression materialization, and partitions. +- Add SYM/STR/GUID min/max with existing comparators. SYM order is lexical by + domain strings, not intern-code order. Use winning row references for wide + values and gather after merging; retain source owners until emit completes. +- Handle LIST first/last and comparable extremes through explicit value/row + semantics. Do not specialize heterogeneous arithmetic by guessing from one row. + +Acceptance: correct results across shuffled worker completion, cross-partition +groups, multiple SYM domains/widths, pooled strings, GUIDs, and null-only groups. + +### P5 — Float and wide grouping keys + +Covers K2. Depends on P3/P4's key/value utilities. + +- Add F32/F64 canonical hash/equality, then GUID and STR key strategies; use + dictionary codes for STR only with a valid domain and matching equality. +- Replace integer-only tuple readers in every newly admitted path. Direct + int64 interpretation of F64 or truncation of GUID/STR is not an implementation. +- Allow LIST keys through a bounded generic grouping strategy that can share + typed aggregation where legal. Preserve deep comparison and ownership. +- Handle composite keys and selections at the same time as single keys; keep + existing supported key-count limits explicit and consistent. + +Acceptance: grouping/distinct parity for ±0, canonical/noncanonical NaNs, +nulls, duplicate wide values, and composite keys, with bounded memory. + +### P6 — Buffered and parameterized aggregates + +Covers A9/A10. Depends on P0/P1/P3/P4. + +- Add missing typed med/top-K/bottom-K kernels and quantile/mode capabilities + only for existing legal input types. Define parameter forwarding once, + including quantile probability, K, ties, empty groups, and null handling. +- Replace the blanket streaming-only admission with strategy-specific support: + buffered state requires a proven memory plan. The existing serial driver and + streaming guards cannot simply be bypassed for large data. +- Share group IDs/row slices for mixed streaming and holistic aggregates where + practical; avoid regrouping the same input once per unsupported aggregate. +- Give buffered state complete merge/destruction/OOM/cancellation behavior. + Consider bounded top-K storage rather than retaining every row by default. + +Acceptance: all supported input types have an explicit optimized or bounded +generic route, and large-group med/top-K cannot multiply memory by worker count. + +### P7 — Count-distinct coverage + +Covers Q1/Q2. Depends on P0/P3; float/wide extensions depend on P5. + +- Restore reachable SYM specialization with correct null handling. +- Add BOOL/U8/DATE/TIME/TIMESTAMP, then float and wide representations using + the shared key contracts. Align the rewrite and kernel admission predicates. +- Preserve the 16-byte composite packing limit of the current rewrite until + a different representation is implemented; do not just widen the type gate. +- Cover nullable group keys and nullable distinct values independently. + +Acceptance: dispatch tests for SYM and temporals, value parity including null +distinctness, and end-to-end timings including any admission prescan. + +### P8 — Predicates, top-k, and sort + +Covers Q3–Q5. Depends on P0 and shared comparison/key contracts from P5. + +- Add F32/F64 fused comparison operands and constants with correct mixed numeric + comparison, NaN/null semantics, and precision for large integer constants. +- Add nullable numeric/temporal predicate evaluation; preserve exact temporal + type/unit compatibility unless explicit conversion is compiled. +- Add GUID equality/order and ordinary STR comparisons where reusable kernels + make them worthwhile. Preserve existing SYM/STR equality semantics. +- Add float/GUID top-k sort keys with existing ordering/null/tie contracts; + share comparator semantics with the general sorter. +- Enable composite F32 radix sorting using a proven float sort-key transform. + Benchmark existing rank fallback before attempting a new wide-key radix path. + +Acceptance: fused/unfused differential values and row ordering under ascending, +descending, ties, nulls, ±0 and NaNs; large-input dispatch assertions. + +### P9 — Expression admission and output construction + +Covers Q6–Q8. Depends on P0/P1; temporal aggregate integration depends on P2. + +- Add F32/GUID/STR broadcasts, with proper STR pool/GUID representation and + null metadata. For LIST output, retain explicit per-cell ownership semantics. +- Reconcile F32 query numeric admission with the lower expression executor + operation by operation. Preserve promotion and rounding, not just pow output. +- Compile legal temporal arithmetic with explicit unit conversion and logical + result type before allowing it inside fused aggregate expressions. Keep + semantically invalid temporal combinations rejected. +- Replace duplicated capability lists where practical with operation-specific + helpers shared by admission and execution. Do not introduce one overly broad + `is_numeric` predicate for operations with different contracts. + +Acceptance: adding an F32 expression or typed literal to a grouped query does +not unexpectedly trigger per-group evaluation or boxed output construction. + +### P10 — Regression and performance closure + +Depends on all work packages. Each preceding package also runs its own checks. + +- Retire a legacy restriction only after its relevant cases have parity and + dispatch coverage. Keep documented generic fallbacks for heterogeneous data + and genuinely unsupported strategy shapes. +- Run the complete C/Rayfall suite (`make test`), relevant sanitizer checks, + and targeted race checks for new parallel writers/state merges. Build the + release binary for timings; never benchmark the sanitizer binary. +- Update the census with result semantics and reachable optimized strategies, + and record accepted intentional fallbacks rather than leaving silent holes. + +## Validation matrix and performance protocol + +Every newly admitted op/type cell needs both correctness and path evidence. +Use table-driven semantic tests and selected cross-products, not a huge set of +tests that merely repeats each switch case. + +| Dimension | Required cases | +|---|---| +| Values | empty, singleton, all-null where representable, mixed nulls, zero, extremes, overflow, NaN normalization, values above 2^53 | +| Layout | owned vectors, nonzero-offset slices, gathered/filtered inputs, mapped/splayed columns, empty and nonempty partitions | +| Groups | one, low cardinality/dense range, sparse range, high cardinality, all unique, skewed, composite, null keys vs zero keys | +| Parallelism | one core, a fixed multi-core count, automatic pool; groups crossing morsels and partitions | +| Query shape | scalar, grouped, multiple mixed aggregates, WHERE selections, aggregate expressions, sort/take and literals | +| Metadata | BOOL/U8 non-nullability, SYM W8/W16/W32/W64 and domains, STR owners/pools, typed null propagation | +| Failures | bounded allocation failure, cancellation, partial accumulator initialization, fallback cleanup | + +Compare optimized results to established scalar operations per group where +their semantics agree, plus hand-calculated edge cases. Retain order-aware +oracles for first/last and sort/take. Use tolerances only for floating reductions +where reduction order permits them; integer/temporal extrema must be exact. + +Performance fixtures: + +1. Deterministic synthetic fixtures with nullable I32 keys and TIME values, + varying row counts, cardinalities and skew. +2. Same workload with I16/I32/I64/F32/F64/TIME/TIMESTAMP value columns and legal + aggregates, nullable and null-free variants. Measure mixed aggregates too. +3. Low/high-cardinality SYM count-distinct, temporal count-distinct, float + filter+top-k, composite F32 sort, and high-group-count literal broadcasts. +4. Existing representative I64/F64 workloads and tiny queries to catch added + planning/prescan overhead; wide-key cases to validate fallback economics. + +Run before/after binaries sequentially in alternating order with identical +core count, data, build flags and CPU conditions. Report warm median and spread, +cold allocation separately, phase times, scratch high-water mark/RSS and chosen +strategy. Include input conversion and admission scans in end-to-end query cost. +Agree per-workload regression tolerances from measured baseline variance; do +not use a universal nanosecond threshold in unit tests. + +## Completion criteria + +- Every census gap A1–A11, K1–K4 and Q1–Q8 is mapped to an implemented route or + an explicit, justified generic/rejected case with tests. +- Nullable-key temporal grouping gains a measured improvement from an efficient nullable-key temporal + aggregation route, with exact result/null/type parity. +- No new admission can reach an incompatible reader, emitter, validity check, + or unordered merge implementation. +- The suite checks fast-path reachability, so future type additions cannot + silently pass correctness tests by dropping back to a slower engine. diff --git a/docs/aggregation-fast-path-results.md b/docs/aggregation-fast-path-results.md new file mode 100644 index 000000000..a8ce6596e --- /dev/null +++ b/docs/aggregation-fast-path-results.md @@ -0,0 +1,46 @@ +# Aggregation and query type coverage + +This records earlier type-coverage work. Current implementation and validation +are tracked in the [grouping engine plan](grouping-engine-scaling-plan.md) and +[results report](grouping-engine-scaling-results.md). + +## Implemented coverage + +| Plan | Gaps | Final implementation | +|---|---|---| +| P1 | Plumbing for A1–A10 | Native input widths, F32 NaN validity, type-correct scalar writes, disjoint parallel payload writes with null metadata merged afterward. Native extrema emission avoids temporary atoms. Stable source row indices retain column/domain ownership for ordered and wide values. | +| P2 | A1–A4, A6–A8, A11 | Streaming sum/min/max/avg/product/statistics across legal numeric/temporal types. F32 truth and mixed binary statistics. COUNT counts every row, including nulls. DATE/TIMESTAMP sum and temporal product/truth remain illegal. | +| P3 | K1, K3, K4 | Nullable dense mapping reserves a null slot without spanning the sentinel; selected/composite/all-null keys use the same mapping. Signed radix keys preserve nulls. Dense worker/global slabs have allocation and sampled traffic budgets; setup, merge and scalar emission are parallel. Removed full-input-per-worker generic hash allocation. | +| P4 | A2, A5 | First/last use stable original-row order, including selections; wide extrema use source rows and existing comparators. SYM extrema compare domain strings. LIST first/last retain selected values. | +| P5 | K2 | Canonical float hashing/equality, GUID bytes, STR bytes and structural LIST grouping. Small/ordered/buffered shapes use one shared index layout. Plain LIST-key aggregate queries, including composite keys, reach it through `select`. Large float/GUID/STR streaming shapes with up to eight keys use the existing parallel wide-key engine. | +| P6 | A9, A10 | Shared group IDs and stable row slices for median, quantile, mode, top/bottom-K, ordered/wide and mixed streaming aggregates. Wide/F32 top-K stores at most K row indices. Existing typed numeric heaps and rank helpers remain shared. | +| P7 | Q1, Q2 | Fused exact count-distinct accepts nullable BOOL/U8/signed/temporal/SYM/F32/F64 pairs. SYM is reachable despite conservative null metadata. F32 generic exact counting widens losslessly once to reuse F64 dedupe. | +| P8 | Q3–Q5 | F32/F64, nullable numeric/temporal, GUID and ordinary STR fused comparisons. Float/GUID filter/top-k sorting. Composite F32 sorts reuse the tested single-column float transform and radix-compose ranks. | +| P9 | Q6–Q8 | Typed F32/GUID/STR broadcasts, F32 numeric/pow admission, and one-time materialization of pure temporal arithmetic through unit-aware operations before typed aggregation. | + +The [census](aggregation-type-census.csv) preserves the original 210 observations +and adds current streaming registration and grouped route columns. Registry +presence and query strategy are different: holistic/ordered operations use row +indices instead of streaming accumulator states. + +## Explicit generic and rejected cases + +- Heterogeneous LIST arithmetic/extrema/rank operations retain dynamic value + evaluation. No inference from the first element selects a fixed-width kernel. + Structural LIST keys and LIST first/last have a bounded shared-index route. +- GUID/STR/LIST count-distinct uses exact generic dedupe. The packed count-distinct + rewrite still requires total key/value width at most 16 bytes; wide values + are not truncated or interpreted as integers. +- Large float/wide streaming keys retain the established parallel implementation. + On one million rows, moving these to serial index grouping regressed STR and + F32 key workloads by about 3x. Admission now reports `parallel wide-key strategy`. + Shapes needing ordered/buffered states, LIST keys, and more than eight keys + use the bounded index route. +- Nullable LIKE/IN, SYM ordering predicates, and cross-unit temporal comparisons + retain their existing evaluators. The new fused comparison leg covers the six + scalar comparison operators; it does not silently extend other evaluators. +- Raw temporal DAG arithmetic stays guarded. Pure `+`, `-`, and `*` expressions + are materialized once using existing unit/promotion rules. Other expressions, + side-effecting calls, and unsupported partition shapes retain existing evaluation. +- Eval-level `as 'F32` remains outside this change's language surface. F32 test + and benchmark fixtures use the existing query projection cast or C constructors. diff --git a/docs/aggregation-type-census.csv b/docs/aggregation-type-census.csv new file mode 100644 index 000000000..c4530650f --- /dev/null +++ b/docs/aggregation-type-census.csv @@ -0,0 +1,211 @@ +input,operation,registry,scalar_result,grouped_result,current_streaming_registry,current_group_route +BOOL,count,yes,i64,I64,yes,typed streaming +BOOL,sum,no,i64,I64,yes,typed streaming +BOOL,avg,no,f64,F64,yes,typed streaming +BOOL,min,no,b8,B8,yes,typed streaming +BOOL,max,no,b8,B8,yes,typed streaming +BOOL,first,no,b8,B8,no,shared row indices +BOOL,last,no,b8,B8,no,shared row indices +BOOL,prod,no,i64,I64,yes,typed streaming +BOOL,var,no,f64,F64,yes,typed streaming +BOOL,var_pop,no,f64,F64,yes,typed streaming +BOOL,stddev,no,f64,F64,yes,typed streaming +BOOL,stddev_pop,no,f64,F64,yes,typed streaming +BOOL,all,yes,b8,B8,yes,typed streaming +BOOL,any,yes,b8,B8,yes,typed streaming +BOOL,med,no,f64,ERROR,no,rejected: language type contract +U8,count,yes,i64,I64,yes,typed streaming +U8,sum,no,i64,I64,yes,typed streaming +U8,avg,no,f64,F64,yes,typed streaming +U8,min,no,u8,U8,yes,typed streaming +U8,max,no,u8,U8,yes,typed streaming +U8,first,no,u8,U8,no,shared row indices +U8,last,no,u8,U8,no,shared row indices +U8,prod,no,i64,I64,yes,typed streaming +U8,var,no,f64,F64,yes,typed streaming +U8,var_pop,no,f64,F64,yes,typed streaming +U8,stddev,no,f64,F64,yes,typed streaming +U8,stddev_pop,no,f64,F64,yes,typed streaming +U8,all,yes,b8,B8,yes,typed streaming +U8,any,yes,b8,B8,yes,typed streaming +U8,med,no,f64,F64,no,shared row indices +I16,count,yes,i64,I64,yes,typed streaming +I16,sum,no,i64,I64,yes,typed streaming +I16,avg,no,f64,F64,yes,typed streaming +I16,min,no,i16,I16,yes,typed streaming +I16,max,no,i16,I16,yes,typed streaming +I16,first,no,i16,I16,no,shared row indices +I16,last,no,i16,I16,no,shared row indices +I16,prod,no,i64,I64,yes,typed streaming +I16,var,no,f64,F64,yes,typed streaming +I16,var_pop,no,f64,F64,yes,typed streaming +I16,stddev,no,f64,F64,yes,typed streaming +I16,stddev_pop,no,f64,F64,yes,typed streaming +I16,all,yes,b8,B8,yes,typed streaming +I16,any,yes,b8,B8,yes,typed streaming +I16,med,no,f64,F64,no,shared row indices +I32,count,yes,i64,I64,yes,typed streaming +I32,sum,no,i64,I64,yes,typed streaming +I32,avg,no,f64,F64,yes,typed streaming +I32,min,no,i32,I32,yes,typed streaming +I32,max,no,i32,I32,yes,typed streaming +I32,first,no,i32,I32,no,shared row indices +I32,last,no,i32,I32,no,shared row indices +I32,prod,no,i64,I64,yes,typed streaming +I32,var,no,f64,F64,yes,typed streaming +I32,var_pop,no,f64,F64,yes,typed streaming +I32,stddev,no,f64,F64,yes,typed streaming +I32,stddev_pop,no,f64,F64,yes,typed streaming +I32,all,yes,b8,B8,yes,typed streaming +I32,any,yes,b8,B8,yes,typed streaming +I32,med,no,f64,F64,no,shared row indices +I64,count,yes,i64,I64,yes,typed streaming +I64,sum,yes,i64,I64,yes,typed streaming +I64,avg,no,f64,F64,yes,typed streaming +I64,min,yes,i64,I64,yes,typed streaming +I64,max,yes,i64,I64,yes,typed streaming +I64,first,no,i64,I64,no,shared row indices +I64,last,no,i64,I64,no,shared row indices +I64,prod,no,i64,I64,yes,typed streaming +I64,var,yes,f64,F64,yes,typed streaming +I64,var_pop,yes,f64,F64,yes,typed streaming +I64,stddev,yes,f64,F64,yes,typed streaming +I64,stddev_pop,yes,f64,F64,yes,typed streaming +I64,all,yes,b8,B8,yes,typed streaming +I64,any,yes,b8,B8,yes,typed streaming +I64,med,yes,f64,F64,no,shared row indices +F32,count,yes,i64,I64,yes,typed streaming +F32,sum,no,f64,F64,yes,typed streaming +F32,avg,no,f64,F64,yes,typed streaming +F32,min,no,f64,F64,yes,typed streaming +F32,max,no,f64,F64,yes,typed streaming +F32,first,no,f64,F64,no,shared row indices +F32,last,no,f64,F64,no,shared row indices +F32,prod,no,f64,F64,yes,typed streaming +F32,var,no,f64,F64,yes,typed streaming +F32,var_pop,no,f64,F64,yes,typed streaming +F32,stddev,no,f64,F64,yes,typed streaming +F32,stddev_pop,no,f64,F64,yes,typed streaming +F32,all,no,b8,B8,yes,typed streaming +F32,any,no,b8,B8,yes,typed streaming +F32,med,no,f64,ERROR,no,rejected: language type contract +F64,count,yes,i64,I64,yes,typed streaming +F64,sum,yes,f64,F64,yes,typed streaming +F64,avg,yes,f64,F64,yes,typed streaming +F64,min,yes,f64,F64,yes,typed streaming +F64,max,yes,f64,F64,yes,typed streaming +F64,first,no,f64,F64,no,shared row indices +F64,last,no,f64,F64,no,shared row indices +F64,prod,no,f64,F64,yes,typed streaming +F64,var,yes,f64,F64,yes,typed streaming +F64,var_pop,yes,f64,F64,yes,typed streaming +F64,stddev,yes,f64,F64,yes,typed streaming +F64,stddev_pop,yes,f64,F64,yes,typed streaming +F64,all,yes,b8,B8,yes,typed streaming +F64,any,yes,b8,B8,yes,typed streaming +F64,med,yes,f64,F64,no,shared row indices +DATE,count,yes,i64,I64,yes,typed streaming +DATE,sum,no,ERROR,ERROR,no,rejected: language type contract +DATE,avg,no,f64,F64,yes,typed streaming +DATE,min,no,date,DATE,yes,typed streaming +DATE,max,no,date,DATE,yes,typed streaming +DATE,first,no,date,DATE,no,shared row indices +DATE,last,no,date,DATE,no,shared row indices +DATE,prod,no,ERROR,ERROR,no,rejected: language type contract +DATE,var,no,f64,F64,yes,typed streaming +DATE,var_pop,no,f64,F64,yes,typed streaming +DATE,stddev,no,f64,F64,yes,typed streaming +DATE,stddev_pop,no,f64,F64,yes,typed streaming +DATE,all,no,ERROR,ERROR,no,rejected: language type contract +DATE,any,no,ERROR,ERROR,no,rejected: language type contract +DATE,med,no,f64,F64,no,shared row indices +TIME,count,yes,i64,I64,yes,typed streaming +TIME,sum,no,time,TIME,yes,typed streaming +TIME,avg,no,f64,F64,yes,typed streaming +TIME,min,no,time,TIME,yes,typed streaming +TIME,max,no,time,TIME,yes,typed streaming +TIME,first,no,time,TIME,no,shared row indices +TIME,last,no,time,TIME,no,shared row indices +TIME,prod,no,ERROR,ERROR,no,rejected: language type contract +TIME,var,no,f64,F64,yes,typed streaming +TIME,var_pop,no,f64,F64,yes,typed streaming +TIME,stddev,no,f64,F64,yes,typed streaming +TIME,stddev_pop,no,f64,F64,yes,typed streaming +TIME,all,no,ERROR,ERROR,no,rejected: language type contract +TIME,any,no,ERROR,ERROR,no,rejected: language type contract +TIME,med,no,f64,F64,no,shared row indices +TIMESTAMP,count,yes,i64,I64,yes,typed streaming +TIMESTAMP,sum,no,ERROR,ERROR,no,rejected: language type contract +TIMESTAMP,avg,no,f64,F64,yes,typed streaming +TIMESTAMP,min,no,timestamp,TIMESTAMP,yes,typed streaming +TIMESTAMP,max,no,timestamp,TIMESTAMP,yes,typed streaming +TIMESTAMP,first,no,timestamp,TIMESTAMP,no,shared row indices +TIMESTAMP,last,no,timestamp,TIMESTAMP,no,shared row indices +TIMESTAMP,prod,no,ERROR,ERROR,no,rejected: language type contract +TIMESTAMP,var,no,f64,F64,yes,typed streaming +TIMESTAMP,var_pop,no,f64,F64,yes,typed streaming +TIMESTAMP,stddev,no,f64,F64,yes,typed streaming +TIMESTAMP,stddev_pop,no,f64,F64,yes,typed streaming +TIMESTAMP,all,no,ERROR,ERROR,no,rejected: language type contract +TIMESTAMP,any,no,ERROR,ERROR,no,rejected: language type contract +TIMESTAMP,med,no,f64,F64,no,shared row indices +GUID,count,yes,i64,I64,yes,typed streaming +GUID,sum,no,ERROR,ERROR,no,rejected: language type contract +GUID,avg,no,ERROR,ERROR,no,rejected: language type contract +GUID,min,no,guid,GUID,no,shared row indices +GUID,max,no,guid,GUID,no,shared row indices +GUID,first,no,guid,GUID,no,shared row indices +GUID,last,no,guid,GUID,no,shared row indices +GUID,prod,no,ERROR,ERROR,no,rejected: language type contract +GUID,var,no,ERROR,ERROR,no,rejected: language type contract +GUID,var_pop,no,ERROR,ERROR,no,rejected: language type contract +GUID,stddev,no,ERROR,ERROR,no,rejected: language type contract +GUID,stddev_pop,no,ERROR,ERROR,no,rejected: language type contract +GUID,all,no,ERROR,ERROR,no,rejected: language type contract +GUID,any,no,ERROR,ERROR,no,rejected: language type contract +GUID,med,no,ERROR,ERROR,no,rejected: language type contract +SYM,count,yes,i64,I64,yes,typed streaming +SYM,sum,no,ERROR,ERROR,no,rejected: language type contract +SYM,avg,no,ERROR,ERROR,no,rejected: language type contract +SYM,min,no,sym,SYM,no,shared row indices +SYM,max,no,sym,SYM,no,shared row indices +SYM,first,no,sym,SYM,no,shared row indices +SYM,last,no,sym,SYM,no,shared row indices +SYM,prod,no,ERROR,ERROR,no,rejected: language type contract +SYM,var,no,ERROR,ERROR,no,rejected: language type contract +SYM,var_pop,no,ERROR,ERROR,no,rejected: language type contract +SYM,stddev,no,ERROR,ERROR,no,rejected: language type contract +SYM,stddev_pop,no,ERROR,ERROR,no,rejected: language type contract +SYM,all,no,ERROR,ERROR,no,rejected: language type contract +SYM,any,no,ERROR,ERROR,no,rejected: language type contract +SYM,med,no,ERROR,ERROR,no,rejected: language type contract +STR,count,yes,i64,I64,yes,typed streaming +STR,sum,no,ERROR,ERROR,no,rejected: language type contract +STR,avg,no,ERROR,ERROR,no,rejected: language type contract +STR,min,no,str,STR,no,shared row indices +STR,max,no,str,STR,no,shared row indices +STR,first,no,str,STR,no,shared row indices +STR,last,no,str,STR,no,shared row indices +STR,prod,no,ERROR,ERROR,no,rejected: language type contract +STR,var,no,ERROR,ERROR,no,rejected: language type contract +STR,var_pop,no,ERROR,ERROR,no,rejected: language type contract +STR,stddev,no,ERROR,ERROR,no,rejected: language type contract +STR,stddev_pop,no,ERROR,ERROR,no,rejected: language type contract +STR,all,no,ERROR,ERROR,no,rejected: language type contract +STR,any,no,ERROR,ERROR,no,rejected: language type contract +STR,med,no,ERROR,ERROR,no,rejected: language type contract +LIST,count,yes,i64,I64,yes,typed streaming +LIST,sum,no,i64,I64,no,generic: dynamic LIST values +LIST,avg,no,f64,F64,no,generic: dynamic LIST values +LIST,min,no,i64,I64,no,generic: dynamic LIST values +LIST,max,no,i64,I64,no,generic: dynamic LIST values +LIST,first,no,i64,I64,no,shared row indices +LIST,last,no,i64,I64,no,shared row indices +LIST,prod,no,i64,I64,no,generic: dynamic LIST values +LIST,var,no,f64,F64,no,generic: dynamic LIST values +LIST,var_pop,no,f64,F64,no,generic: dynamic LIST values +LIST,stddev,no,f64,F64,no,generic: dynamic LIST values +LIST,stddev_pop,no,f64,F64,no,generic: dynamic LIST values +LIST,all,no,b8,B8,no,generic: dynamic LIST values +LIST,any,no,b8,B8,no,generic: dynamic LIST values +LIST,med,no,f64,F64,no,generic: dynamic LIST values diff --git a/docs/docs/language/functions.md b/docs/docs/language/functions.md index 02f76647c..c6c6eb4d0 100644 --- a/docs/docs/language/functions.md +++ b/docs/docs/language/functions.md @@ -290,7 +290,7 @@ Indices are *pre-insertion* positions in `[0, count]`; `idx == count` is equival For a parted target, `rows` must match the physical data-column schema exactly in name and concrete vector type. Do **not** include the virtual `date` or `part` column. A list maps values in physical column order; a table or dictionary may reorder columns, but must contain every physical name exactly once. Atoms append one row. Equal-length vectors append a batch, and atom values alongside them broadcast across that batch. Generic `null` is accepted for sentinel-nullable columns; `SYM` and `STR` map it to their empty value because those types have no distinct null, while `BOOL` and `U8` reject it because they are non-nullable. -The partition key must equal the current last key, which grows that live segment, or be strictly later, which starts a new live segment. Earlier keys — including an existing historical partition — are immutable and cannot be inserted into. Existing partition metadata must already be strictly increasing in logical key order; use zero-padded integer directory names when lexical directory order would otherwise disagree with numeric order. Every historical physical segment must be present. A missing segment in the last partition can be repaired only by a non-empty same-key append; it blocks an empty insert or a move to a later key. `BOOL`/`U8` cannot be null-backfilled when that missing segment already represents existing rows, but a missing zero-row segment needs no backfill. +The partition key must equal the current last key, which grows that live segment, or be strictly later, which starts a new live segment. Earlier keys — including an existing historical partition — are immutable and cannot be inserted into. Existing partition metadata must already be strictly increasing in logical key order; integer directory names are ordered by value (`1`, `2`, `10` — no zero-padding needed), so only two spellings of one value (`1` and `01`) violate this, and they are rejected as `corrupt`. Every historical physical segment must be present. A missing segment in the last partition can be repaired only by a non-empty same-key append; it blocks an empty insert or a move to a later key. `BOOL`/`U8` cannot be null-backfilled when that missing segment already represents existing rows, but a missing zero-row segment needs no backfill. A non-empty functional insert returns a new logical table view; the quoted-symbol form publishes that view by rebinding the symbol. Historical mmap segments are retained without copying, while a segment receiving rows becomes heap-backed. Queries and values that already retained the previous table continue to see that snapshot; subsequent resolution of a rebound target symbol sees the newly appended rows. A non-empty same-key append rebuilds partition metadata and copies the active segment; a later key materializes a new tail instead. Batch incoming rows to avoid repeatedly copying a growing intraday segment. diff --git a/docs/docs/language/repl.md b/docs/docs/language/repl.md index 567c94323..3b66f8c40 100644 --- a/docs/docs/language/repl.md +++ b/docs/docs/language/repl.md @@ -17,7 +17,7 @@ The `rayforce` binary operates in three modes depending on how it is invoked: echo '(+ 1 2)' | ./rayforce ``` -Interactive mode is detected automatically when stdin is a terminal. On startup the REPL prints a banner with the version, CPU, memory, and core count, then shows the `‣` prompt. +Interactive mode is detected automatically when stdin is a terminal. On startup the REPL prints a banner with the version, CPU, memory, logical CPU count, and worker count, then shows the `‣` prompt. By default the pool includes every online logical CPU, including SMT threads. Use `-c N` to choose a total worker count explicitly. Individual operations may use fewer tasks when their workload or memory budget requires it. ## REPL Commands diff --git a/docs/docs/namespaces/db.md b/docs/docs/namespaces/db.md index 376d59e0a..63f3d45b5 100644 --- a/docs/docs/namespaces/db.md +++ b/docs/docs/namespaces/db.md @@ -58,6 +58,8 @@ Signature: `(.db.parted.get "db_root" 'tbl_name)`. The table name **must** be a Returns a single logical table assembled from every partition directory under `db_root/tbl_name/`. The result carries a virtual `MAPCOMMON` partition column derived from directory names, and every data column is a `RAY_PARTED_*` view over the segment files. Partition pruning kicks in automatically for `select` predicates on the virtual column. +The directory names decide the key type and the partition order. `YYYY.MM.DD` names give a `date` column; digit-only names give an `int64` `part` column and are ordered by value, so `1`, `2`, `10` load as `[1 2 10]` with no zero-padding; any other digit/dot names give a `sym` `part` column in name order. A digit-only name that does not fit `int64` is rejected as `corrupt`, and the message names the directory. + ```lisp (set dbroot "/tmp/rayforce-parted-db") (set symfile (format "%/.sym" dbroot)) @@ -81,7 +83,7 @@ Use `(insert parted partition-key rows)` to build a fresh logical view containin The row payload describes the physical splayed schema only: omit the virtual `date` or `part` column, and supply exactly one matching value per physical column. A list follows physical column order. A table or dictionary may reorder its payload, but must contain every physical column name exactly once. Atoms append one row; equal-length vectors append a batch, with atom values broadcast across that batch. Concrete vector types must match exactly. Generic `null` is accepted for sentinel-nullable columns; it becomes the empty value for `SYM`/`STR`, while non-nullable `BOOL`/`U8` reject it. -The key may equal the table's last partition key, growing the current segment, or be strictly later, starting a new current segment. Inserts into any earlier or non-last partition are rejected; historical partitions are immutable. The loaded partition keys must already be strictly increasing in their logical type. In particular, zero-pad integer directory names if their lexical order would otherwise differ from numeric order. All historical physical segments must be present. A missing segment in the last partition can be repaired only by a non-empty same-key append; until repaired it blocks advancing the key. Missing `BOOL`/`U8` cannot be null-backfilled when the partition already has rows, but a zero-row segment needs no backfill. +The key may equal the table's last partition key, growing the current segment, or be strictly later, starting a new current segment. Inserts into any earlier or non-last partition are rejected; historical partitions are immutable. The loaded partition keys must already be strictly increasing in their logical type. Integer directory names are ordered by value, so `1`, `2`, `10` need no zero-padding; two spellings of one value (`1` and `01`) load as equal keys and are rejected here as `corrupt`. All historical physical segments must be present. A missing segment in the last partition can be repaired only by a non-empty same-key append; until repaired it blocks advancing the key. Missing `BOOL`/`U8` cannot be null-backfilled when the partition already has rows, but a zero-row segment needs no backfill. For a non-empty insert, the quoted-symbol form rebinds the target to the new logical view. Existing historical mmap segments are retained, while a segment receiving live rows becomes heap-backed. Queries and values that already retained the previous table remain stable snapshots; queries that resolve the symbol after the rebind see the new rows. Same-key growth rebuilds partition metadata and copies only the active segment; a later key builds a new tail. Batch incoming rows to avoid repeatedly copying a growing intraday segment. diff --git a/docs/grouping-engine-scaling-plan.md b/docs/grouping-engine-scaling-plan.md new file mode 100644 index 000000000..5faa649c4 --- /dev/null +++ b/docs/grouping-engine-scaling-plan.md @@ -0,0 +1,254 @@ +# Grouping engine scaling plan + +Status: implementation and local acceptance are complete at `f61a4eda`. +The feature is ready for PR review into `dev`; required CI checks gate merging. +This extends the type-coverage plan. + +Acceptance compares immutable release binaries with identical generated inputs. +The rebased ASan/UBSan suite passes 3,828/3,828 tests. Current measurements +and reproducible commands are in [the results report](grouping-engine-scaling-results.md). + +## Objective + +Remove avoidable grouping, allocation, synchronization, and merge overhead for +all supported grouped aggregate families. Preserve types, nulls, ordering, +overflow behavior, symbol domains, and cancellation. This work covers grouping +and its query integration, not unrelated joins, CSV parsing, or sorting queries. + +Equal latency across aggregates is not an acceptance criterion. Sum consumes +every valid value; extrema can avoid updates; exact median and distinct need +additional data structures. Each family needs its own baseline and scaling proof. + +## Implementation and evidence + +| Family / shape | Execution | Completed evidence | +|---|---|---| +| BOOL/U8/I16/I32/DATE/TIME min/max, eligible dense single key | Shared state through explicit concurrent-update capability | Native/null contracts; adversarial value order and dominant-key cases at every worker count | +| Numeric sums, count, average, statistics, product, boolean and binary reductions | Bounded partitioned or task-local dense states; native scalar output | Registry-wide writer contracts, independent arithmetic oracles and separate family timings | +| I64/TIMESTAMP/F32/F64 extrema | Partitioned dense | Native-width, null and non-finite contracts; repeated worker sweep | +| Selected and composite dense keys | Deduplicated native payload and shared source-row indices | Selected-row identity, empty selection and composite/mixed contracts; worker sweep | +| Sparse integer/temporal/SYM keys | Radix | Sparse/skew/null matrix; phase profiles and memory measurements | +| Float, STR, GUID and LIST keys | Shared parallel directory with complete equality and deterministic first-row IDs | Full-value/canonicalization oracles, race checks and repeated worker sweep | +| First/last, wide extrema, median, quantile, mode, top/bottom K | Shared stable row slices, row-balanced groups and dominant-group splitting | Independent order/rank/frequency oracles; 90 internal K runs; dominant-group worker sweep | +| Count-distinct | Pair-hash partitioning and parallel stable output ordering | Native/wide value and ordering oracles; repeated worker sweep | +| Mixed streaming/indexed aggregates and expressions | Shared groups and stable row slices | Independent mixed contracts and repeated worker sweep | + +The [results report](grouping-engine-scaling-results.md) links the complete +900-configuration summary, all 2,700 process records, phase investigations and +regression repeats. Baseline failures receive no speedup claim. Measured cold +setup and bounded-memory tradeoffs remain explicit. + +## Work-package status + +| Package | Status | Evidence | +|---|---|---| +| G0 — Baseline and census | Complete | Immutable baseline/candidate hashes; 77 cases and 2,700 typed process comparisons; complete timings and peak RSS | +| G1 — Dense streaming | Complete | All streaming writers, unary/binary/mixed contracts, bounded state budget tests, dense route profiles and worker matrix | +| G2 — Shared key layouts | Complete | Selection/composite/sparse/wide-key oracles and full worker matrix; shared stable row indices | +| G3 — Ordered and distinct consumers | Complete | Dominant-group worker matrix, exact rank/order/frequency tests and 90 top/bottom-K histogram checks | +| G4 — Acceptance and delivery | Local acceptance complete; ready for PR review | ASan/UBSan 3,828/3,828, TSan 24/24 at four total threads, 2,700 synthetic runs, 90 kernel oracles, 130 regression repeats and 68 profiles; immutable release and privacy checks | + +## Work packages, in dependency order + +### G0 — Reproducible baseline and coverage census + +1. Preserve release at bb81e621 and current release separately; record revisions + and worker settings. Unit-test builds must not replace benchmark binaries. +2. Cover aggregate families with reproducible synthetic fixtures and typed + result comparisons. Keep native temporal extrema as a control. +3. Measure temporal and integer sums, count, average, variance, wide extrema, + binary reductions, and mixed queries independently. Record cold execution + separately from five warm executions in each fresh process. +4. Add synthetic fixtures for native widths, nulls, uniform/clustered/skewed + keys, low/high cardinality, sparse ranges, selection and composite keys. +5. Record actual route and fallback reason alongside phase profiles. Unsupported + operation/type pairs remain explicit errors, not artificial fast-path goals. + +Exit: a coverage ledger with baseline, current route, correctness oracle and +worker sweep for every family above. Complete: 77 synthetic cases, six worker +settings and three rounds; immutable binary hashes accompany the process records. + +### G1 — Close the dense streaming refactor + +1. Measure histogram, scatter, reduction, partial merge and output separately + for sum/count/statistics, not just shared extrema. +2. Reduce duplicated input fields and state traffic where profiles justify it. +3. Tune partition tasks using work and state size, with bounded split storage; + validate a dominant null key and a dominant non-null key. +4. Keep strategy choice capability-driven. Add shared updates only where safe + and measured; contended atomic sums are not an automatic replacement. +5. Check default uses all logical CPUs; distinguish pool size from bounded tasks. + +Exit: correct unary, binary and mixed streaming results; repeated synthetic +8/default scaling measurements for min, sum, count and statistics. Investigate +flat scaling to the responsible phase before declaring this package complete. + +### G2 — Share grouping across selections and key shapes + +1. Profile selected/composite task-local replication and sparse radix traffic. +2. Extend partition ownership to selected/composite keys where a bounded plan + wins, preserving stable row identity for ordered aggregates. +3. Reuse one group-ID/index layout across all aggregates in a query. +4. Parallelize wide-key grouping stages that remain serial; preserve complete + string/GUID values, symbol domains and float canonicalization. + +Exit: correctness and scaling matrix for selection, composite, sparse and wide +keys, including empty selection and all-null keys; no O(workers × rows) state. + +### G3 — Ordered, buffered and distinct consumers + +1. Isolate count-distinct regression before changing its implementation. +2. Schedule independent indexed groups by estimated rows/work, rather than + equal group counts. Measure dominant-group behavior explicitly. +3. Use mergeable bounded state where the operation permits it; preserve stable + first/last semantics and exact median/quantile/distinct results. +4. Bound peak memory including index layouts, scatter and retained values. +5. Test mixed streaming/indexed queries so one consumer does not rebuild groups + or cause a hidden whole-query performance cliff. + +Exit: family-specific speedup evidence, correctness oracles, and bounded-memory +checks. An algorithmic lower bound must be distinguished from avoidable serial +engine work with profiles; documentation alone does not close a measured defect. + +### G4 — Acceptance and delivery + +- Run saved baseline and candidate sequentially, alternating order, at + 1/2/4/8/16/default workers, at least three fresh processes per case. +- Report per-process cold time, warm median and range, plus peak RSS. Retain + outliers; do not describe small noisy differences as scaling. +- Compare sorted typed results exactly for integer and ordered outputs. For + floating reductions, compare keys/types/nulls exactly and values against an + independent oracle with operation-specific absolute/relative tolerances; + parallel reassociation may change low bits. +- Investigate repeatable regressions over 5%; accept neither noise-based + speedup claims nor an unexplained slowdown as completion. +- Run complete ASan/UBSan suite and relevant TSan tests after functional edits. +- Review diff and commit the feature branch; rebuild release with committed + revision and rerun synthetic min/sum controls on the delivered binary. Push + `perf/grouping-engine-scaling` and open a PR into `dev` after acceptance. + +Completion requires all packages above to have evidence and explicit status. +The current min(time) result and passing tests do not close the whole plan. + +## Implemented changes + +- Native-width payload scatter and shared input-field deduplication. +- Bounded dense state allocation, task-local initialization, and parallel output. +- Capability-driven shared extrema and row-balanced partition reduction. +- Shared parallel key directory with full float, string, GUID and LIST equality. +- Stable row slices reused by mixed streaming and indexed consumers. +- Row-weighted ordered/wide consumers and exact dominant-group rank selection. +- Row-split winner selection, bounded partial top/bottom-K heaps, and exact + partitioned mode frequencies with bounded local preaggregation. +- Parallel native result gathering and adaptive-width symbol slice offsets. +- Bounded exact distinct preaggregation and parallel stable output ordering. +- Domain-aware symbol read views during immutable worker phases. +- Typed empty output columns and corrected wide distinct admission. + +Full ASan/UBSan validation passes 3,828/3,828 tests; targeted TSan passes 24/24. +The complete synthetic acceptance and regression investigations are recorded in +the results report. Integration is gated by the required PR checks. + +## Dominant-group execution stages + +Assigning a whole group to one worker leaves a single large group serial even +when grouping itself scales. Consumer scheduling therefore considers rows +within groups as well as independent groups: + +- First/last and wide extrema select candidates from contiguous row chunks and + merge them in original index order. For extrema, null-only chunks contribute + no candidate. +- Small top/bottom K uses one bounded heap per source chunk, then selects from + their union. Larger candidate sets merge in parallel. Large K first uses + exact three-way selection, then sorts only the retained K rows and merges + their sorted chunks. Each merge is split by output rank, including the final + pair. Two row-index buffers bound scratch storage by input rows rather than + workers times K. Partially filled serial heaps are heapified before sorting. +- Mode locally combines exact value counts in a fixed-size table, partitions + partial counts by full value hash, and reduces partitions independently. + Equality checks resolve hash collisions. Counts carry the earliest original + position, preserving ties even when source-row indices are reordered. +- Median and quantile use exact parallel rank selection within large groups. + Splitting uses the common parallel grain and the per-worker row share, so a + few medium-sized groups can also use the pool. Histogram oracles cover the + grain boundary, medium groups and dominant groups. +- Native output gathering writes disjoint result payload ranges and publishes + null metadata after workers finish. + +All stages are query-local. No previously computed query answers are retained. +The synthetic matrix includes single-group first/last, symbol/string extrema, +numeric/string mode, and numeric/string/symbol top/bottom K. Independent tests +also cover temporal and narrow native widths, null-only and empty groups, +frequency ties, reordered row indices, and local frequency-table flushing. + +The grouped query compiler currently accepts K from 1 through 1024. This change +preserves that language contract. `topk_consumer.c` separately exercises the +internal indexed kernel with K near the group size; `topk_scaling.py` checks +all output values against an independent histogram and records cold/warm times +and peak RSS. This prevents a query-front-end limit from hiding kernel gaps. + +## Follow-up on low-worker overhead + +- When task-local group counters fit in 256 KiB, build stable row slices with + direct source-task histograms and prefix offsets. This avoids a second row + buffer and partitioning passes while retaining source order. Larger group + directories keep the bounded partitioned layout. +- Partition larger directories into contiguous group-id ranges so workers + own adjacent output regions instead of scattering interleaved groups across + the same pages. Use one source range per worker; split hot partitions by rows. +- Dispatch native top-K input types outside the scan loop. Discard losing rows + before entering heap maintenance, and specialize native heap comparisons. +- Separate top-K scheduling from heap ownership: each worker retains one + private heap across smaller source tasks, then sorts it once after the scan. + Exact preselection handles larger K, followed by bounded sorting runs and + merges split by output rank. +- Read native first/last null sentinels directly after type dispatch; retain + periodic cancellation checks and source-row winner semantics. + +- For task-local dense states, sample each source range before choosing eager + initialization. Narrow local key ranges retain lazy initialization instead of + initializing the entire global domain. Sampling controls work only. +- Use one dense scatter source range per worker to reduce adjacent writes; + partition reduction still splits dominant partitions by their row counts. + +These changes are included in the final synthetic sweep and sanitizer checks. + +## Shared directory allocation + +- Use 32-bit atomic representatives when row IDs fit, retaining 64-bit entries + for larger dense inputs. The empty sentinel remains outside the valid row range. +- Allocate representative output storage after exact group counts are known. +- Initialize the directory in coarse aligned ranges to reduce concurrent first + writes to the same huge pages. Pad partition counters between worker slices. + +## Native validity and small-domain scheduling + +- Native accumulator loops select the sentinel reader from the registered input + representation at compile time. Nullable rows no longer repeat type dispatch. +- Use additional independent source tasks for small dense domains when their + replicated state fits within one eighth of the existing scatter budget. + Larger domains retain the bounded partition or task-local strategy. + +## Native streaming output + +Every registered streaming accumulator provides a native scalar writer. Sums, +counts, averages, statistics, products, truth and binary reductions now join +extrema on this path. The added writers share a primitive result calculation with boxed output, +including typed nulls, wrapped integers and finite-float canonicalization. +Dense, small-hash, radix and indexed serial emission use the same interface; +parallel emitters publish null metadata after their worker barrier. + +Registry tests require this capability for every streaming operation/type pair. +Independent scalar and grouped contracts cover empty/all-null states, arithmetic +overflow, non-finite results, zero weights and covariance. Small-domain task +expansion also observes the heap watermark, including row IDs and final states; +a constrained-budget test checks that it falls back to fewer tasks. + +## Mixed payload scatter + +Complete all deduplicated input fields for a bounded row chunk before advancing +through each source range. This avoids repeatedly traversing the full output +buffer for binary and mixed streaming reductions. The existing native-width +readers, source selection and partition ownership remain shared. Chunk sizing +bounds the active payload footprint; scratch consists of partition cursors, +without another row-sized buffer. diff --git a/docs/grouping-engine-scaling-results.md b/docs/grouping-engine-scaling-results.md new file mode 100644 index 000000000..559a02f44 --- /dev/null +++ b/docs/grouping-engine-scaling-results.md @@ -0,0 +1,245 @@ +# Grouping engine scaling results + +Status: the full scaling matrix was validated at `f61a4eda`; the audit +follow-up below covers the query regression fixes. This report contains only synthetic +fixtures and generic engine validation. Required PR checks gate merging. + +## Audit follow-up + +The PR audit identified missing query coverage and an empty-result inference +regression after the measured revision above. + +- Restored the original W32 SYM single-key query and kept the additional + two-key radix fixture. The single-key result is normalized after query + execution by its unique numeric row value before checking every source key + and aggregate. This preserves query-path coverage and key/value association + checks without assuming a group output order that the engine does not promise. +- Added query fixtures for all nine STR/GUID/LIST key/value combinations, + selected and mixed count-distinct queries, full key-column emission, and + composite keys wider than 16 bytes. Expected counts come from a four-row + pattern, with values differing beyond a shared prefix. +- Added empty-input and selected-out query fixtures, including aggregate + aliases that exercise generic unary evaluation. Empty-input domain/type + errors no longer turn a zero-group query into an error. A separate injected + error test checks that OOM and cancellation still propagate. +- Added task-array bounds assertions, a dense partition capacity assertion, + a generic native-width copy fallback, and invalid rank-selection checks. + +The cancellation concern does not require clearing every slab on the +coordinator: dispatch joins its tasks, cancellation is checked before merge, +and destruction already skips slabs whose `ready` flag is false. Cancellation +after that check cannot undo a completed task's initialization. + +Validation at `fdf365f3`: **3,832/3,832 ASan/UBSan tests passed**. The restored +symbol fixture and wide count-distinct fixture also passed separately with +28 total threads. Injected empty-result errors and invalid rank boundaries +are covered in the full suite. + +A clean release of `fdf365f3` completed without warnings (SHA-256 +`ab1f19e100f89164af1ef511fe6390cf3e634114a1fb0c5df9bd039c578e343b`). +The follow-up compared eight dense-query shapes against `f61a4eda` at 1, 8, +and default workers: **144/144 fresh-process typed comparisons passed**. +Three default-worker warm slowdowns were repeated with five processes per +binary, adding **30/30 passing comparisons**. Their slower medians did not +reproduce in that follow-up; both samples remain available. + +| Default-worker case | Initial baseline / candidate warm ms | Repeat baseline / candidate warm ms | +|---|---:|---:| +| Binary aggregates | 2.431 / 3.034 | 2.349 / 2.316 | +| Clustered keys | 0.630 / 0.730 | 0.626 / 0.603 | +| Symbol keys | 0.232 / 0.249 | 0.237 / 0.229 | + +See the [environment](../bench/groupby_shapes/results/2026-09-16-audit/environment.json), +[summary](../bench/groupby_shapes/results/2026-09-16-audit/grouping.csv), +[all process samples](../bench/groupby_shapes/results/2026-09-16-audit/processes.csv), +[repeat summary](../bench/groupby_shapes/results/2026-09-16-audit/repeat.csv), and +[repeat process samples](../bench/groupby_shapes/results/2026-09-16-audit/repeat-processes.csv). +This focused comparison does not establish a new general speedup. + +The restored query fixture also exposed two sorting races under hosted TSan. +The shared sortedness flag now uses relaxed atomic accesses, and in-place key +packing no longer reads a neighboring task's element; the existing coordinator +pass checks boundary pairs after the dispatch barrier. The triggering fixture +reproduced both races before the fix and passed afterward. Follow-up validation: +**130/130 sort ASan/UBSan tests** and **41/41 parallel TSan tests** passed without +race reports, using four total threads for TSan. The aggregation TSan suite at +`fdf365f3` also passed **26/26** without suppressions. These sorting fixes do not +change the grouping traversal measured above. + +The timing tables below remain measurements of `f61a4eda`; they are not +presented as a new full sweep of the audit follow-up. + +## Method + +Use separate immutable release binaries, identical generated inputs and worker +settings 1, 2, 4, 8, 16 and default. Run binaries sequentially, reversing their +order between rounds. Record the first query separately from five warm queries +in each of three fresh processes. Retain timing ranges and peak RSS. + +The default pool uses all 28 logical CPUs on the validation host. Typed result +comparisons check keys, nulls and integer values exactly; floating reductions +use relative tolerance `1e-9` and absolute tolerance `1e-8`. Independent unit +oracles cover ordering, nulls, rank and distinct semantics. + +## Correctness + +The following completed checks apply to revision `f61a4eda`. + +- **2,700 fresh-process runs**, 77 synthetic cases, six worker settings and three rounds: all typed cold/warm result comparisons passed. +- Complete ASan/UBSan suite: **3,828/3,828 passed**. +- Targeted TSan: **24/24 passed**, `RAYFORCE_CORES=3` (four total threads), `setarch x86_64 -R`, no suppressions. +- Coverage includes native widths, source symbol domains, structural LIST keys, + stable row indices, dominant-group exact `med`/quantile, frequency ties, + parallel top/bottom-K merges, adaptive symbol slices and typed empty output. + +Earlier TSan attempts with five total threads stalled during pool shutdown before +the aggregation checks; they are not counted as passes. The final run used four +total threads and reported no races. No pool implementation or suppression was +changed to obtain that result. + +Four synthetic cases fail in the baseline: STR-valued count-distinct and its +mixed form crash; LIST-key count-distinct and its mixed form raise a type error. +These cases receive no baseline speedup claim. Candidate results are checked +across workers and with independent unit-test oracles. + +## Warm performance by family + +Times below are medians of three process warm medians, in milliseconds. +The [complete summary](../bench/groupby_shapes/results/2026-09-15/grouping.csv) +contains all 77 cases and all worker counts. The [process records](../bench/groupby_shapes/results/2026-09-15/processes.csv) +retain every cold time, five warm times, result/binary hashes and peak RSS. + +| Synthetic case | Baseline, 28 | Current, 8 | Current, 28 | Baseline/current at 28 | +|---|---:|---:|---:|---:| +| `extrema-time` | 6.379 | 1.816 | 0.958 | 6.66x | +| `sum-time` | 5.228 | 1.098 | 0.678 | 7.72x | +| `sum-i64` | 6.013 | 1.101 | 0.724 | 8.30x | +| `count` | 3.350 | 1.249 | 0.571 | 5.86x | +| `statistics` | 10.153 | 2.320 | 1.313 | 7.73x | +| `binary` | 17.848 | 4.203 | 2.415 | 7.39x | +| `product` | 5.789 | 1.221 | 0.737 | 7.85x | +| `truth` | 8.331 | 1.802 | 1.004 | 8.30x | +| `median` | 6.209 | 3.247 | 2.021 | 3.07x | +| `quantile` | 6.110 | 3.323 | 2.022 | 3.02x | +| `mode` | 6.682 | 4.346 | 2.264 | 2.95x | +| `top-bottom` | 9.028 | 5.655 | 4.373 | 2.06x | +| `first-last` | 9.594 | 2.808 | 1.738 | 5.52x | +| `distinct` | 12.735 | 6.107 | 4.112 | 3.10x | +| `mixed` | 8.763 | 3.857 | 2.397 | 3.66x | +| `composite` | 13.981 | 3.886 | 2.119 | 6.60x | +| `selected` | 2.525 | 1.191 | 0.968 | 2.61x | +| `sparse` | 5.975 | 7.922 | 7.014 | 0.85x | +| `float-key` | 6.357 | 4.220 | 2.208 | 2.88x | +| `string-key` | 8.497 | 2.628 | 1.652 | 5.14x | +| `guid-key` | 10.530 | 2.948 | 2.518 | 4.18x | +| `list-key` | 27.750 | 3.764 | 2.762 | 10.05x | + +Aggregate costs differ, and these measurements do not establish linear scaling +or a universal best worker count. I64 sum improves from eight to 28 workers in +this final sweep; its strategy changes from task-local state to partitioned +state. The GUID case now splits medium-sized rank groups using the common +parallel grain, closing the scheduling gap found during phase review. + +## Cold costs and repeatability + +The main sweep retains these regressions. The follow-up used five additional +fresh processes per binary for every configuration that was over 5% slower in +at least two main-sweep rounds: 13 configurations and **130 verified runs**. +All [follow-up process records](../bench/groupby_shapes/results/2026-09-15/regression-repeat.csv) +are retained; they do not replace the main samples. + +| Case / workers | Metric | Main baseline ms | Main current ms | Repeat baseline ms | Repeat current ms | +|---|---|---:|---:|---:|---:| +| `clustered` / 4 | cold | 1.700 | 2.588 | 2.248 | 1.709 | +| `clustered` / 8 | cold | 2.145 | 2.596 | 1.938 | 2.506 | +| `hot-min-descending` / 8 | cold | 1.446 | 2.047 | 1.409 | 2.099 | +| `float-key` / default | cold | 3.841 | 4.994 | 3.921 | 5.040 | +| `nonnull` / 8 | cold | 3.062 | 3.411 | 2.493 | 3.191 | +| `product` / 8 | cold | 2.348 | 2.615 | 2.260 | 2.600 | +| `symbol-key` / 2 | cold | 0.647 | 0.885 | 0.750 | 0.710 | +| `symbol-key` / 2 | warm | 0.527 | 0.763 | 0.671 | 0.559 | +| `sparse` / default | warm | 5.975 | 7.014 | 6.196 | 5.890 | + +Neither warm slowdown reproduced in the five-process follow-up. The symbol +and sparse samples vary across runs; these fixtures support neither a fixed +warm regression nor a universal speedup claim. The additional cold flags for +`extrema-f32`, `sum-f64`, `truth`, `extrema-i32` and `sum-time` also did not +retain a median regression above 5% in the follow-up. + +Cold regressions remain for the eight-worker descending-min, clustered, +nonnull and product fixtures, and for default-worker float keys. The +[68 profiled runs](../bench/groupby_shapes/results/2026-09-15/phase-profiles.csv) +place the extra time in slab allocation, histogram/scatter, directory setup +and filling row indices: + +- Descending-min and product spend about 0.66 and 0.64 ms, respectively, in + cold slab allocation; the corresponding warm phase rounds to zero. +- Clustered histogram/scatter takes about 1.83 ms cold versus 0.49 ms warm. +- Float-key directory allocation/initialization takes about 1.05 ms cold + versus 0.09 ms warm; row-index filling takes 1.49 versus 0.59 ms. + +The bounded state and shared-directory strategies reduce repeated state +initialization and merging, while retaining these first-execution costs. +The report keeps those costs alongside the warm improvements. + +The rank profiles also confirm the medium-group scheduling change: the +four-group GUID fixture now uses parallel rank selection within its groups. +Small groups retain the direct consumer; task counts remain bounded by work +and scratch storage. All query state is rebuilt per execution. + +## Coverage ledger + +The `agg_contract` tests in [test_agg_contract.c](../test/test_agg_contract.c) +check values and routes independently of benchmark timings. + +| Engine work | Synthetic cases | Correctness evidence | +|---|---|---| +| Native streaming and scalar output | `sum-*`, `extrema-*`, `count`, `statistics`, `product`, `truth` | Unary scalar/grouped contracts, every streaming registry writer, native null/overflow output, parallel float oracle | +| Binary reductions | `binary` | All numeric input-type pairs; zero weights, null-only pairs and empty states | +| Dense strategies and skew | `clustered`, `unique-keys`, `skew`, `all-null-key`, `hot-min-*` | Route assertions, extreme native values, constrained heap budget, independent per-key results | +| Selected and composite grouping | `selected`, `composite`, `selected-indexed` | Selected source-row identity, composite keys and mixed consumers | +| Shared wide-key directory | `float-*`, `string-*`, `guid-key`, `list-key` | Full value equality, float canonicalization, structural keys and stable group indices | +| Ordered and wide consumers | `first-last`, `wide-extrema`, `symbol-extrema`, corresponding `hot-*` cases | First/last source order, symbol domains, native gathering and dominant-group winners | +| Exact rank, mode and top/bottom K | `median`, `quantile`, `mode`, `top-bottom`, wide/symbol and `hot-*` variants | Rank/null/slice oracles, frequency ties, reordered indices, bounded heaps and parallel merges | +| Exact distinct and mixed consumers | `distinct*`, `mixed-distinct*`, `mixed`, `skew-indexed` | Native and wide distinct oracles, first-row ordering, shared stable layouts | + +The final timing matrix covers each case at 1/2/4/8/16/default workers. Invalid +operation/type pairs stay explicit errors; heterogeneous LIST arithmetic keeps +its existing dynamic evaluation. Default pool size and useful task count are +separate: the pool exposes all CPUs while each stage bounds tasks by its work +and memory budget. + +## Internal top/bottom-K kernel + +All **90 runs** at revision `f61a4eda` matched the independent histogram oracle. +These synthetic inputs have 4,000,003 rows; K above 1024 exercises the internal +kernel, without changing the query language limit. + +| K | 1 worker warm ms | 8 workers warm ms | Default (28) warm ms | +|---:|---:|---:|---:| +| 1024 | 11.206 | 2.148 | 1.795 | +| 65536 | 53.571 | 20.432 | 17.512 | +| 4000003 | 772.737 | 159.594 | 89.194 | + +Values are medians of the three process warm medians. The CSV in +`bench/groupby_shapes/results/2026-09-15/topk.csv` retains process ranges, +cold medians and peak RSS. Scaling varies with K; these results do not establish linear scaling. The baseline fails the +full-size-K oracle because of partially filled heap ordering, so that case +has no baseline speedup claim. + +## Reproduction + +```sh +python3 bench/groupby_shapes/grouping_scaling.py \ + --binary /path/to/candidate --baseline /path/to/baseline \ + --baseline-skip-cases distinct-string-values,mixed-distinct-string-values,distinct-list-keys,mixed-distinct-list-keys \ + --workers 1,2,4,8,16,default --rounds 3 --output /tmp/grouping.json + +python3 bench/groupby_shapes/summarize_scaling.py /tmp/grouping.json /tmp/grouping.csv \ + --process-output /tmp/grouping-processes.csv +``` + +To investigate one configuration, use the same driver with, for example, +`--cases symbol-key --workers 2 --rounds 5`. Add `--profile` for a separate +phase-timing run; profiling measurements are kept separate from the main sweep +and unprofiled repeats. diff --git a/src/app/repl.c b/src/app/repl.c index 9b3862047..6775c8909 100644 --- a/src/app/repl.c +++ b/src/app/repl.c @@ -402,7 +402,7 @@ static void print_banner(void) { fprintf(stdout, "\033[1m" " RayforceDB: %s %s\n" - " %s %"PRId64"(MB) %d core(s)\n" + " %s %"PRId64"(MB) %d logical CPU(s)\n" " Using %u worker(s)\n" #ifdef DEBUG " Build: debug (ASan + UBSan, -O0)\n" diff --git a/src/core/ipc.c b/src/core/ipc.c index 741271f32..ac6cd3215 100644 --- a/src/core/ipc.c +++ b/src/core/ipc.c @@ -32,6 +32,7 @@ #include "ops/ops.h" #include "store/journal.h" #include +#include #include #include #include @@ -76,10 +77,24 @@ static void mark_ipc_literal_fallbacks(ray_t* obj) { size_t ray_ipc_compress(const uint8_t* src, size_t len, uint8_t* dst, size_t dst_cap) { - if (len <= RAY_IPC_COMPRESS_THRESHOLD) return 0; + return ray_ipc_compress_at(src, len, dst, dst_cap, + (size_t)RAY_IPC_COMPRESS_THRESHOLD); +} + +size_t ray_ipc_link_threshold(ray_sock_t fd) +{ + return ray_sock_peer_is_local(fd) ? RAY_IPC_COMPRESS_NEVER + : (size_t)RAY_IPC_COMPRESS_THRESHOLD; +} + +size_t ray_ipc_compress_at(const uint8_t* src, size_t len, + uint8_t* dst, size_t dst_cap, size_t threshold) +{ + /* RAY_IPC_COMPRESS_NEVER is SIZE_MAX, so this rejects every length. */ + if (len <= threshold) return 0; /* Step 1: delta-encode into temporary buffer */ - uint8_t* delta = (uint8_t*)ray_sys_alloc(len); + uint8_t* delta = (uint8_t*)ray_alloc_raw(len); if (!delta) return 0; delta[0] = src[0]; @@ -96,7 +111,7 @@ size_t ray_ipc_compress(const uint8_t* src, size_t len, size_t run = 1; while (si + run < len && delta[si + run] == val && run < 127) run++; - if (di + 2 > dst_cap) { ray_sys_free(delta); return 0; } + if (di + 2 > dst_cap) { ray_free_raw(delta); return 0; } dst[di++] = (uint8_t)run; dst[di++] = val; si += run; @@ -109,14 +124,14 @@ size_t ray_ipc_compress(const uint8_t* src, size_t len, si++; llen++; } - if (di + 1 + llen > dst_cap) { ray_sys_free(delta); return 0; } + if (di + 1 + llen > dst_cap) { ray_free_raw(delta); return 0; } dst[di++] = (uint8_t)(-(int8_t)llen); memcpy(dst + di, delta + start, llen); di += llen; } } - ray_sys_free(delta); + ray_free_raw(delta); if (di >= len) return 0; return di; } @@ -124,7 +139,7 @@ size_t ray_ipc_compress(const uint8_t* src, size_t len, size_t ray_ipc_decompress(const uint8_t* src, size_t clen, uint8_t* dst, size_t dst_len) { - uint8_t* decoded = (uint8_t*)ray_sys_alloc(dst_len); + uint8_t* decoded = (uint8_t*)ray_alloc_raw(dst_len); if (!decoded) return 0; size_t si = 0; @@ -133,16 +148,16 @@ size_t ray_ipc_decompress(const uint8_t* src, size_t clen, while (si < clen && di < dst_len) { int8_t count = (int8_t)src[si++]; if (count > 0) { - if (si >= clen) { ray_sys_free(decoded); return 0; } + if (si >= clen) { ray_free_raw(decoded); return 0; } uint8_t val = src[si++]; size_t n = (size_t)count; - if (di + n > dst_len) { ray_sys_free(decoded); return 0; } + if (di + n > dst_len) { ray_free_raw(decoded); return 0; } memset(decoded + di, val, n); di += n; } else { size_t n = (size_t)(-(int)count); if (si + n > clen || di + n > dst_len) { - ray_sys_free(decoded); + ray_free_raw(decoded); return 0; } memcpy(decoded + di, src + si, n); @@ -152,12 +167,12 @@ size_t ray_ipc_decompress(const uint8_t* src, size_t clen, } /* Un-delta */ - if (di == 0) { ray_sys_free(decoded); return 0; } + if (di == 0) { ray_free_raw(decoded); return 0; } dst[0] = decoded[0]; for (size_t i = 1; i < di; i++) dst[i] = (uint8_t)(decoded[i] + dst[i - 1]); - ray_sys_free(decoded); + ray_free_raw(decoded); return di; } @@ -303,8 +318,7 @@ static ray_t* hook_lookup(int idx) { * Errors are logged and swallowed — a buggy logging hook must never * wedge connection teardown. `poll` is the poll the connection lives * in, exposed thread-locally so the hook body can use the handle with - * `.ipc.post` / `.ipc.send` / `.ipc.close`; the legacy server path - * passes NULL (its conn-index handles are not selector ids). */ + * `.ipc.post` / `.ipc.send` / `.ipc.close`. */ static void hook_call_lifecycle(ray_poll_t* poll, int idx, int64_t handle) { ray_t* fn = hook_lookup(idx); if (!fn) return; @@ -398,7 +412,7 @@ static int conn_tx_drain_blocking(ray_poll_t* poll, ray_selector_t* sel) return 0; } -static void send_response(ray_sock_t fd, ray_t* result) +static void send_response(ray_sock_t fd, ray_t* result, size_t threshold) { int64_t ser_size = ray_serde_size(result); @@ -417,7 +431,7 @@ static void send_response(ray_sock_t fd, ray_t* result) if (ser_size <= 0) { if (fallback) ray_error_free(fallback); return; } } - uint8_t* payload = (uint8_t*)ray_sys_alloc((size_t)ser_size); + uint8_t* payload = (uint8_t*)ray_alloc_raw((size_t)ser_size); if (!payload) { if (fallback) ray_error_free(fallback); return; } ray_ser_raw(payload, result); @@ -425,14 +439,14 @@ static void send_response(ray_sock_t fd, ray_t* result) size_t send_len = 0; uint8_t flags = 0; - if ((size_t)ser_size > RAY_IPC_COMPRESS_THRESHOLD) { - uint8_t* comp = (uint8_t*)ray_sys_alloc((size_t)ser_size); + if ((size_t)ser_size > threshold) { + uint8_t* comp = (uint8_t*)ray_alloc_raw((size_t)ser_size); if (comp) { - size_t clen = ray_ipc_compress(payload, (size_t)ser_size, - comp, (size_t)ser_size); + size_t clen = ray_ipc_compress_at(payload, (size_t)ser_size, + comp, (size_t)ser_size, threshold); if (clen > 0 && clen + 4 < (size_t)ser_size) { send_len = clen + 4; - send_buf = (uint8_t*)ray_sys_alloc(send_len); + send_buf = (uint8_t*)ray_alloc_raw(send_len); if (send_buf) { uint32_t uncomp = (uint32_t)ser_size; memcpy(send_buf, &uncomp, 4); @@ -440,7 +454,7 @@ static void send_response(ray_sock_t fd, ray_t* result) flags = RAY_IPC_FLAG_COMPRESSED; } } - ray_sys_free(comp); + ray_free_raw(comp); } } @@ -461,8 +475,8 @@ static void send_response(ray_sock_t fd, ray_t* result) ray_sock_send(fd, &hdr, sizeof(hdr)); ray_sock_send(fd, send_buf, send_len); - ray_sys_free(send_buf); - if (payload) ray_sys_free(payload); + ray_free_raw(send_buf); + if (payload) ray_free_raw(payload); if (fallback) ray_error_free(fallback); } @@ -478,12 +492,12 @@ static ray_t* deser_frame(uint8_t* payload, size_t payload_len, uint8_t flags) uint32_t uncomp_size; memcpy(&uncomp_size, payload, 4); if (uncomp_size == 0 || uncomp_size > 256u * 1024u * 1024u) return NULL; - decompressed = (uint8_t*)ray_sys_alloc(uncomp_size); + decompressed = (uint8_t*)ray_alloc_raw(uncomp_size); if (!decompressed) return NULL; size_t dlen = ray_ipc_decompress(payload + 4, payload_len - 4, decompressed, uncomp_size); if (dlen != uncomp_size) { - ray_sys_free(decompressed); + ray_free_raw(decompressed); return NULL; } payload = decompressed; @@ -491,7 +505,7 @@ static ray_t* deser_frame(uint8_t* payload, size_t payload_len, uint8_t flags) } int64_t de_len = (int64_t)payload_len; ray_t* msg = ray_de_raw(payload, &de_len); - if (decompressed) ray_sys_free(decompressed); + if (decompressed) ray_free_raw(decompressed); return msg; } @@ -723,6 +737,10 @@ typedef struct { ray_ipc_header_t hdr; uint8_t phase; int64_t listener_id; /* id of the listener selector; -1 = outbound */ + /* Compression policy for this link, resolved once when the connection + * is established (ray_ipc_link_threshold) so the send paths do not pay + * a getpeername per frame. */ + size_t compress_threshold; bool auth_required; /* server has -u/-U */ bool restricted; /* server has -U */ /* Sync round-trip state: while a ray_ipc_send waits on this conn it @@ -818,6 +836,7 @@ static ray_t* ipc_accept(ray_poll_t* poll, ray_selector_t* sel) cd->listener_id = sel->id; cd->auth_required = (poll->auth_secret[0] != '\0'); cd->restricted = poll->restricted; + cd->compress_threshold = ray_ipc_link_threshold(new_fd); ray_poll_reg_t reg = {0}; reg.fd = (int64_t)new_fd; @@ -1048,7 +1067,12 @@ static ray_t* ipc_read_payload(ray_poll_t* poll, ray_selector_t* sel) * write and let the poll/pump layer deregister it. */ if (cur && cur->data == (void*)cd && conn_tx_drain_blocking(poll, cur) == 0) - send_response((ray_sock_t)cur->fd, result); + { + ray_ipc_conn_data_t* rcd = (ray_ipc_conn_data_t*)cur->data; + send_response((ray_sock_t)cur->fd, result, + rcd ? rcd->compress_threshold + : ray_ipc_link_threshold((ray_sock_t)cur->fd)); + } } if (result != RAY_NULL_OBJ) ray_release(result); /* The request is served: this is the end of the server's unit of work, @@ -1124,383 +1148,6 @@ int64_t ray_ipc_listen(ray_poll_t* poll, uint16_t port) return ray_ipc_listen_at(poll, NULL, port); } -/* ====================================================================== - * Server API - * ====================================================================== */ - -static void conn_close(ray_ipc_server_t* srv, ray_ipc_conn_t* c) -{ - /* `.ipc.on.close` fires only for conns that were actually opened — - * a slot whose phase never advanced past HANDSHAKE/CREDS was never - * announced via on.open and so shouldn't be announced via on.close. - * Keeps the pair balanced for the user. */ - if (c->phase == RAY_IPC_PHASE_HEADER || - c->phase == RAY_IPC_PHASE_PAYLOAD) { - hook_call_lifecycle(NULL, IPC_HOOK_CLOSE, (int64_t)(c - srv->conns)); - } - -#if defined(__linux__) - epoll_ctl(srv->poll_fd, EPOLL_CTL_DEL, c->fd, NULL); -#elif defined(__APPLE__) - struct kevent kev; - EV_SET(&kev, c->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); - kevent(srv->poll_fd, &kev, 1, NULL, 0, NULL); -#else - (void)srv; -#endif - - ray_sock_close(c->fd); - if (c->rx_buf) ray_sys_free(c->rx_buf); - c->fd = RAY_INVALID_SOCK; - c->rx_buf = NULL; - c->rx_len = 0; - c->rx_need = 0; - - uint32_t idx = (uint32_t)(c - srv->conns); - if (idx + 1 < srv->n_conns) - srv->conns[idx] = srv->conns[srv->n_conns - 1]; - if (srv->n_conns > 0) srv->n_conns--; -} - -static void conn_on_handshake(ray_ipc_server_t* srv, ray_ipc_conn_t* c) -{ - /* Refuse peers speaking a different wire version up front — see the - * matching check in ipc_read_handshake. */ - if (!c->rx_buf || c->rx_buf[0] != RAY_SERDE_WIRE_VERSION) { - conn_close(srv, c); - return; - } - - bool auth_req = (srv->auth_secret[0] != '\0'); - uint8_t resp[2] = { RAY_SERDE_WIRE_VERSION, auth_req ? 0x01 : 0x00 }; - ray_sock_send(c->fd, resp, 2); - - ray_sys_free(c->rx_buf); - c->rx_buf = NULL; - c->rx_len = 0; - - if (auth_req) { - c->rx_need = 1; /* length byte */ - c->phase = RAY_IPC_PHASE_CREDS; - return; - } - - c->rx_need = sizeof(ray_ipc_header_t); - c->phase = RAY_IPC_PHASE_HEADER; - /* Legacy path mirror of the poll-path post-handshake fire. */ - hook_call_lifecycle(NULL, IPC_HOOK_OPEN, (int64_t)(c - srv->conns)); -} - -static void conn_on_header(ray_ipc_server_t* srv, ray_ipc_conn_t* c) -{ - memcpy(&c->hdr, c->rx_buf, sizeof(ray_ipc_header_t)); - - if (c->hdr.prefix != RAY_SERDE_PREFIX) { conn_close(srv, c); return; } - if (c->hdr.version != RAY_SERDE_WIRE_VERSION) { conn_close(srv, c); return; } - if (c->hdr.endian != RAY_SERDE_ENDIAN) { conn_close(srv, c); return; } - if (c->hdr.size <= 0) { conn_close(srv, c); return; } - if (c->hdr.size > 256 * 1024 * 1024) { conn_close(srv, c); return; } - - ray_sys_free(c->rx_buf); - c->rx_buf = (uint8_t*)ray_sys_alloc((size_t)c->hdr.size); - if (!c->rx_buf) { conn_close(srv, c); return; } - c->rx_len = 0; - c->rx_need = (size_t)c->hdr.size; - c->phase = RAY_IPC_PHASE_PAYLOAD; -} - -static void conn_on_payload(ray_ipc_server_t* srv, ray_ipc_conn_t* c) -{ - bool prev = ray_eval_get_restricted(); - ray_eval_set_restricted(srv->restricted); - - /* Conn-array index doubles as the handle on the legacy path — - * stable for the connection's lifetime, distinct across active - * connections, freed back to the pool on close. Mirrored shape - * of the poll path's sel->id. */ - int64_t prev_handle = ipc_ctx_handle(); - ray_poll_t* prev_poll = ipc_ctx_poll(); - ipc_ctx_set((int64_t)(c - srv->conns), prev_poll); - - ray_t* result = eval_payload(c->rx_buf, c->rx_len, &c->hdr); - - ipc_ctx_set(prev_handle, prev_poll); - ray_eval_set_restricted(prev); - - if (c->hdr.msgtype == RAY_IPC_MSG_SYNC) - send_response(c->fd, result); - if (result != RAY_NULL_OBJ) ray_release(result); - /* The request is served: this is the end of the server's unit of work, - * and stamping here is what lets the poll loop tell an idle server from - * one between two requests. Stamping on frame arrival instead would - * make the measured gap the request's own duration. */ - ray_heap_note_activity(); - - ray_sys_free(c->rx_buf); - c->rx_buf = NULL; - c->rx_len = 0; - c->rx_need = sizeof(ray_ipc_header_t); - c->phase = RAY_IPC_PHASE_HEADER; -} - -static void conn_on_creds(ray_ipc_server_t* srv, ray_ipc_conn_t* c) -{ - if (c->rx_len == 1) { - /* Got length byte — reallocate buffer for full credential */ - uint8_t cred_len = c->rx_buf[0]; - size_t need = 1 + (size_t)cred_len; - uint8_t* newbuf = (uint8_t*)ray_sys_alloc(need); - if (!newbuf) { conn_close(srv, c); return; } - newbuf[0] = cred_len; - ray_sys_free(c->rx_buf); - c->rx_buf = newbuf; - c->rx_need = need; - return; - } - - uint8_t cred_len = c->rx_buf[0]; - bool ok = validate_creds(c->rx_buf + 1, cred_len, srv->auth_secret); - - /* Legacy path mirror of the poll-path on.auth call: same handle-as- - * conn-index convention, same narrowing semantics. */ - if (ok) { - int hook_ok = hook_call_auth(NULL, (int64_t)(c - srv->conns), - c->rx_buf + 1, cred_len); - if (hook_ok == 0) ok = false; - } - - uint8_t result = ok ? 0x00 : 0x01; - ray_sock_send(c->fd, &result, 1); - - if (!ok) { - conn_close(srv, c); - return; - } - - ray_sys_free(c->rx_buf); - c->rx_buf = NULL; - c->rx_len = 0; - c->rx_need = sizeof(ray_ipc_header_t); - c->phase = RAY_IPC_PHASE_HEADER; - hook_call_lifecycle(NULL, IPC_HOOK_OPEN, (int64_t)(c - srv->conns)); -} - -static void conn_on_readable(ray_ipc_server_t* srv, ray_ipc_conn_t* c) -{ - if (!c->rx_buf) { - c->rx_buf = (uint8_t*)ray_sys_alloc(c->rx_need); - if (!c->rx_buf) { conn_close(srv, c); return; } - } - - int64_t n = ray_sock_recv(c->fd, c->rx_buf + c->rx_len, - c->rx_need - c->rx_len); - if (n <= 0) { conn_close(srv, c); return; } - c->rx_len += (size_t)n; - - if (c->rx_len < c->rx_need) return; - - switch (c->phase) { - case RAY_IPC_PHASE_HANDSHAKE: conn_on_handshake(srv, c); break; - case RAY_IPC_PHASE_CREDS: conn_on_creds(srv, c); break; - case RAY_IPC_PHASE_HEADER: conn_on_header(srv, c); break; - case RAY_IPC_PHASE_PAYLOAD: conn_on_payload(srv, c); break; - } -} - -ray_err_t ray_ipc_server_init_at(ray_ipc_server_t* srv, const char* host, uint16_t port) -{ - memset(srv, 0, sizeof(*srv)); - srv->listen_fd = ray_sock_listen_at(host, port); - if (srv->listen_fd == RAY_INVALID_SOCK) return RAY_ERR_IO; - ray_sock_set_nonblocking(srv->listen_fd); - -#if defined(__linux__) - srv->poll_fd = epoll_create1(0); - if (srv->poll_fd < 0) { - ray_sock_close(srv->listen_fd); - return RAY_ERR_IO; - } - struct epoll_event ev = { .events = EPOLLIN, .data.fd = srv->listen_fd }; - epoll_ctl(srv->poll_fd, EPOLL_CTL_ADD, srv->listen_fd, &ev); -#elif defined(__APPLE__) - srv->poll_fd = kqueue(); - if (srv->poll_fd < 0) { - ray_sock_close(srv->listen_fd); - return RAY_ERR_IO; - } - struct kevent kev; - EV_SET(&kev, srv->listen_fd, EVFILT_READ, EV_ADD, 0, 0, NULL); - kevent(srv->poll_fd, &kev, 1, NULL, 0, NULL); -#else - srv->poll_fd = -1; -#endif - - srv->running = true; - return RAY_OK; -} - -ray_err_t ray_ipc_server_init(ray_ipc_server_t* srv, uint16_t port) -{ - return ray_ipc_server_init_at(srv, NULL, port); -} - -void ray_ipc_server_destroy(ray_ipc_server_t* srv) -{ - for (uint32_t i = 0; i < srv->n_conns; i++) { - ray_ipc_conn_t* c = &srv->conns[i]; - if (c->fd != RAY_INVALID_SOCK) { - if (c->rx_buf) ray_sys_free(c->rx_buf); - ray_sock_close(c->fd); - } - } - srv->n_conns = 0; - - ray_sock_close(srv->listen_fd); - srv->listen_fd = RAY_INVALID_SOCK; - - if (srv->poll_fd >= 0) { -#ifndef RAY_OS_WINDOWS - close(srv->poll_fd); -#endif - } - srv->poll_fd = -1; - srv->running = false; -} - -int ray_ipc_poll(ray_ipc_server_t* srv, int timeout_ms) -{ - int ready = 0; - -#if defined(__linux__) - struct epoll_event events[RAY_IPC_MAX_EVENTS]; - int nfds = epoll_wait(srv->poll_fd, events, RAY_IPC_MAX_EVENTS, timeout_ms); - if (nfds < 0) return (errno == EINTR) ? 0 : -1; - - for (int i = 0; i < nfds; i++) { - int fd = events[i].data.fd; - - if (fd == srv->listen_fd) { - ray_sock_t new_fd = ray_sock_accept(srv->listen_fd); - if (new_fd == RAY_INVALID_SOCK) continue; - ray_sock_set_nonblocking(new_fd); - if (srv->n_conns >= RAY_IPC_MAX_CONNS) { - ray_sock_close(new_fd); - continue; - } - ray_ipc_conn_t* c = &srv->conns[srv->n_conns++]; - c->fd = new_fd; - c->rx_buf = NULL; - c->rx_len = 0; - c->rx_need = 2; - c->phase = RAY_IPC_PHASE_HANDSHAKE; - struct epoll_event cev = { .events = EPOLLIN, .data.fd = new_fd }; - epoll_ctl(srv->poll_fd, EPOLL_CTL_ADD, new_fd, &cev); - } else { - bool found = false; - for (uint32_t j = 0; j < srv->n_conns; j++) { - if (srv->conns[j].fd == fd) { - conn_on_readable(srv, &srv->conns[j]); - found = true; - break; - } - } - if (!found) ready++; - } - } - -#elif defined(__APPLE__) - struct kevent events[RAY_IPC_MAX_EVENTS]; - struct timespec ts; - struct timespec* tsp = NULL; - if (timeout_ms >= 0) { - ts.tv_sec = timeout_ms / 1000; - ts.tv_nsec = (timeout_ms % 1000) * 1000000L; - tsp = &ts; - } - int nfds = kevent(srv->poll_fd, NULL, 0, events, RAY_IPC_MAX_EVENTS, tsp); - if (nfds < 0) return (errno == EINTR) ? 0 : -1; - - for (int i = 0; i < nfds; i++) { - int fd = (int)events[i].ident; - - if (fd == srv->listen_fd) { - ray_sock_t new_fd = ray_sock_accept(srv->listen_fd); - if (new_fd == RAY_INVALID_SOCK) continue; - ray_sock_set_nonblocking(new_fd); - if (srv->n_conns >= RAY_IPC_MAX_CONNS) { - ray_sock_close(new_fd); - continue; - } - ray_ipc_conn_t* c = &srv->conns[srv->n_conns++]; - c->fd = new_fd; - c->rx_buf = NULL; - c->rx_len = 0; - c->rx_need = 2; - c->phase = RAY_IPC_PHASE_HANDSHAKE; - struct kevent kev; - EV_SET(&kev, new_fd, EVFILT_READ, EV_ADD, 0, 0, NULL); - kevent(srv->poll_fd, &kev, 1, NULL, 0, NULL); - } else { - bool found = false; - for (uint32_t j = 0; j < srv->n_conns; j++) { - if (srv->conns[j].fd == fd) { - conn_on_readable(srv, &srv->conns[j]); - found = true; - break; - } - } - if (!found) ready++; - } - } - -#else /* Windows: select-based fallback */ - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(srv->listen_fd, &rfds); - ray_sock_t maxfd = srv->listen_fd; - for (uint32_t i = 0; i < srv->n_conns; i++) { - FD_SET(srv->conns[i].fd, &rfds); - if (srv->conns[i].fd > maxfd) maxfd = srv->conns[i].fd; - } - - struct timeval tv; - struct timeval* tvp = NULL; - if (timeout_ms >= 0) { - tv.tv_sec = timeout_ms / 1000; - tv.tv_usec = (timeout_ms % 1000) * 1000; - tvp = &tv; - } - - int nfds = select((int)(maxfd + 1), &rfds, NULL, NULL, tvp); - if (nfds < 0) return (errno == EINTR) ? 0 : -1; - - if (FD_ISSET(srv->listen_fd, &rfds)) { - ray_sock_t new_fd = ray_sock_accept(srv->listen_fd); - if (new_fd != RAY_INVALID_SOCK) { - ray_sock_set_nonblocking(new_fd); - if (srv->n_conns >= RAY_IPC_MAX_CONNS) { - ray_sock_close(new_fd); - } else { - ray_ipc_conn_t* c = &srv->conns[srv->n_conns++]; - c->fd = new_fd; - c->rx_buf = NULL; - c->rx_len = 0; - c->rx_need = 2; - c->phase = RAY_IPC_PHASE_HANDSHAKE; - } - } - } - - for (uint32_t i = srv->n_conns; i > 0; ) { - --i; - if (srv->conns[i].fd != RAY_INVALID_SOCK && FD_ISSET(srv->conns[i].fd, &rfds)) - conn_on_readable(srv, &srv->conns[i]); - } -#endif - - return ready; -} - /* ===== Connection-handle API ===== * * One handle namespace: a handle is the poll selector id of an IPC @@ -1576,7 +1223,8 @@ static int conn_pump(ray_poll_t* poll, int64_t id) * many connections it goes to: ray_mcast_pub shares one frame across * every subscriber's queue (#487). */ static ray_poll_frame_t* conn_frame_msg(ray_t* msg, uint8_t msgtype, - uint8_t extra_flags, ray_err_t* err_out) + uint8_t extra_flags, size_t threshold, + ray_err_t* err_out) { if (err_out) *err_out = RAY_OK; int64_t ser_size = ray_serde_size(msg); @@ -1585,7 +1233,7 @@ static ray_poll_frame_t* conn_frame_msg(ray_t* msg, uint8_t msgtype, return NULL; } - uint8_t* payload = (uint8_t*)ray_sys_alloc((size_t)ser_size); + uint8_t* payload = (uint8_t*)ray_alloc_raw((size_t)ser_size); if (!payload) { if (err_out) *err_out = RAY_ERR_OOM; return NULL; @@ -1596,14 +1244,14 @@ static ray_poll_frame_t* conn_frame_msg(ray_t* msg, uint8_t msgtype, size_t send_len = 0; uint8_t flags = 0; - if ((size_t)ser_size > RAY_IPC_COMPRESS_THRESHOLD) { - uint8_t* comp = (uint8_t*)ray_sys_alloc((size_t)ser_size); + if ((size_t)ser_size > threshold) { + uint8_t* comp = (uint8_t*)ray_alloc_raw((size_t)ser_size); if (comp) { - size_t clen = ray_ipc_compress(payload, (size_t)ser_size, - comp, (size_t)ser_size); + size_t clen = ray_ipc_compress_at(payload, (size_t)ser_size, + comp, (size_t)ser_size, threshold); if (clen > 0 && clen + 4 < (size_t)ser_size) { send_len = clen + 4; - send_buf = (uint8_t*)ray_sys_alloc(send_len); + send_buf = (uint8_t*)ray_alloc_raw(send_len); if (send_buf) { uint32_t uncomp = (uint32_t)ser_size; memcpy(send_buf, &uncomp, 4); @@ -1611,7 +1259,7 @@ static ray_poll_frame_t* conn_frame_msg(ray_t* msg, uint8_t msgtype, flags = RAY_IPC_FLAG_COMPRESSED; } } - ray_sys_free(comp); + ray_free_raw(comp); } } @@ -1632,24 +1280,24 @@ static ray_poll_frame_t* conn_frame_msg(ray_t* msg, uint8_t msgtype, size_t total = sizeof(hdr) + send_len; if (total > (size_t)RAY_IPC_FRAME_MAX_BYTES) { - ray_sys_free(send_buf); - if (payload) ray_sys_free(payload); + ray_free_raw(send_buf); + if (payload) ray_free_raw(payload); if (err_out) *err_out = RAY_ERR_IO; return NULL; } ray_poll_frame_t* frame = ray_poll_frame_new((int64_t)total); if (!frame) { - ray_sys_free(send_buf); - if (payload) ray_sys_free(payload); + ray_free_raw(send_buf); + if (payload) ray_free_raw(payload); if (err_out) *err_out = RAY_ERR_OOM; return NULL; } memcpy(frame->data, &hdr, sizeof(hdr)); memcpy(frame->data + sizeof(hdr), send_buf, send_len); - ray_sys_free(send_buf); - if (payload) ray_sys_free(payload); + ray_free_raw(send_buf); + if (payload) ray_free_raw(payload); return frame; } @@ -1660,11 +1308,24 @@ static ray_poll_frame_t* conn_frame_msg(ray_t* msg, uint8_t msgtype, * frame must conn_tx_drain_blocking() first (see sync_send / * ray_ipc_send_async). Returns 0 on success, -1 on serialization or * socket failure. */ +/* The compression policy of an established connection. Prefer the value + * cached on the conn data — it carries any explicit `compress` option from + * .ipc.open, which re-deriving from the peer address would silently + * discard — and fall back to link locality when there is no conn data. */ +static size_t conn_threshold(ray_selector_t* sel) +{ + if (!sel) return (size_t)RAY_IPC_COMPRESS_THRESHOLD; + ray_ipc_conn_data_t* cd = (ray_ipc_conn_data_t*)sel->data; + if (cd) return cd->compress_threshold; + return ray_ipc_link_threshold((ray_sock_t)sel->fd); +} + static int64_t conn_write_msg(ray_sock_t fd, ray_t* msg, uint8_t msgtype, - uint8_t extra_flags) + uint8_t extra_flags, size_t threshold) { ray_err_t err = RAY_OK; - ray_poll_frame_t* frame = conn_frame_msg(msg, msgtype, extra_flags, &err); + ray_poll_frame_t* frame = conn_frame_msg(msg, msgtype, extra_flags, + threshold, &err); if (!frame) return -1; int64_t rc = ray_sock_send(fd, frame->data, (size_t)frame->size); ray_poll_frame_release(frame); @@ -1695,6 +1356,14 @@ static int64_t connect_fail_code(int err) { int64_t ray_ipc_connect(const char* host, uint16_t port, const char* user, const char* password, int timeout_ms) +{ + return ray_ipc_connect_opts(host, port, user, password, timeout_ms, + RAY_IPC_COMPRESS_AUTO); +} + +int64_t ray_ipc_connect_opts(const char* host, uint16_t port, + const char* user, const char* password, + int timeout_ms, size_t compress_threshold) { /* The connection lives in the active poll's selector table — its * selector id IS the handle. No poll, no handle namespace: refuse @@ -1785,6 +1454,9 @@ int64_t ray_ipc_connect(const char* host, uint16_t port, cd->phase = RAY_IPC_PHASE_HEADER; cd->listener_id = -1; /* outbound: on.open never fires, on.close does */ cd->restricted = poll->restricted; /* -U narrows pushed evals too */ + cd->compress_threshold = (compress_threshold == RAY_IPC_COMPRESS_AUTO) + ? ray_ipc_link_threshold(fd) + : compress_threshold; ray_sock_set_nonblocking(fd); @@ -1855,7 +1527,7 @@ static ray_t* sync_send(int64_t handle, ray_t* msg, uint8_t extra_flags) * sync request, so its bytes can't interleave into the pending frame. */ if (conn_tx_drain_blocking(poll, sel) < 0 || conn_write_msg((ray_sock_t)sel->fd, msg, RAY_IPC_MSG_SYNC, - extra_flags) < 0) { + extra_flags, conn_threshold(sel)) < 0) { if (owned) ray_release(msg); return ray_error("io", "ipc send failed"); } @@ -1917,6 +1589,14 @@ ray_t* ray_ipc_send(int64_t handle, ray_t* msg) return sync_send(handle, msg, 0); } +size_t ray_ipc_handle_threshold(int64_t handle) +{ + ray_poll_t* poll; + ray_selector_t* sel = conn_resolve(&poll, handle); + if (!sel) return RAY_IPC_COMPRESS_AUTO; + return conn_threshold(sel); +} + ray_err_t ray_ipc_send_async(int64_t handle, ray_t* msg) { bool owned = false; @@ -1934,7 +1614,8 @@ ray_err_t ray_ipc_send_async(int64_t handle, ray_t* msg) ray_selector_t* sel = conn_resolve(&poll, handle); ray_err_t rc = (!sel || conn_tx_drain_blocking(poll, sel) < 0 || conn_write_msg((ray_sock_t)sel->fd, msg, - RAY_IPC_MSG_ASYNC, 0) < 0) + RAY_IPC_MSG_ASYNC, 0, + conn_threshold(sel)) < 0) ? RAY_ERR_IO : RAY_OK; if (owned) ray_release(msg); return rc; @@ -2004,6 +1685,12 @@ static int conn_try_send_frame(ray_selector_t* sel, ray_poll_buf_t* frame) } ray_err_t ray_ipc_frame_async(ray_t* msg, ray_poll_frame_t** out) +{ + return ray_ipc_frame_async_at(msg, (size_t)RAY_IPC_COMPRESS_THRESHOLD, out); +} + +ray_err_t ray_ipc_frame_async_at(ray_t* msg, size_t compress_threshold, + ray_poll_frame_t** out) { if (!out) return RAY_ERR_TYPE; *out = NULL; @@ -2019,7 +1706,12 @@ ray_err_t ray_ipc_frame_async(ray_t* msg, ray_poll_frame_t** out) owned = true; } ray_err_t err = RAY_OK; - ray_poll_frame_t* frame = conn_frame_msg(msg, RAY_IPC_MSG_ASYNC, 0, &err); + /* A frame is shared by every subscriber that wants this threshold + * (#487), so the policy belongs to the framing, not to the peer: the + * caller groups its subscribers by threshold and asks for one framing + * per distinct value (#551). */ + ray_poll_frame_t* frame = conn_frame_msg(msg, RAY_IPC_MSG_ASYNC, 0, + compress_threshold, &err); if (owned) ray_release(msg); if (!frame) return err == RAY_OK ? RAY_ERR_IO : err; *out = frame; @@ -2120,3 +1812,106 @@ ray_t* ray_ipc_send_verbose(int64_t handle, ray_t* msg) { return sync_send(handle, msg, RAY_IPC_FLAG_VERBOSE); } + +/* ===== .ipc.open options ===== */ + +/* Read one optional integer field. Returns 1 if present, 0 if absent, + * -1 on a type error (with *err set). */ +static int opts_i64(ray_t* d, const char* name, int64_t* out, ray_t** err) +{ + ray_t* key = ray_sym(ray_sym_intern(name, strlen(name))); + if (!key) { *err = ray_error("oom", ".ipc.open options"); return -1; } + ray_t* v = ray_dict_get(d, key); + ray_release(key); + if (!v) return 0; + if (!ray_is_atom(v) || (v->type != -RAY_I64 && v->type != -RAY_I32)) { + *err = ray_error("type", ".ipc.open option `%s` must be an integer, got %s", + name, ray_type_name(v->type)); + ray_release(v); + return -1; + } + *out = (v->type == -RAY_I64) ? v->i64 : (int64_t)v->i32; + ray_release(v); + return 1; +} + +ray_t* ray_ipc_parse_open_opts(ray_t* arg, int* timeout_ms, + size_t* compress_threshold) +{ + *timeout_ms = 0; + *compress_threshold = RAY_IPC_COMPRESS_AUTO; + if (!arg) return NULL; + + /* Backwards-compatible form: a bare integer is the connect timeout. */ + if (ray_is_atom(arg) && (arg->type == -RAY_I64 || arg->type == -RAY_I32)) { + int64_t tv = (arg->type == -RAY_I64) ? arg->i64 : (int64_t)arg->i32; + if (tv == NULL_I64) return NULL; /* 0N -> default budget */ + if (tv < 0) + return ray_error("domain", ".ipc.open timeout must be >= 0, got %lld", + (long long)tv); + *timeout_ms = (tv > INT_MAX) ? INT_MAX : (int)tv; + return NULL; + } + + if (arg->type != RAY_DICT) + return ray_error("type", ".ipc.open expects an integer timeout or an options dict, got %s", + ray_type_name(arg->type)); + + /* Reject unknown keys: a typo must not silently mean "default". + * + * Every key must be a symbol and must be validated. ray_dict_find_idx + * returns -1 on a key-type mismatch rather than erroring, so a dict + * keyed by anything else would make both lookups miss and this + * function return success with defaults — reinstating the very silent + * default this check exists to prevent, and swallowing a typo with + * it. */ + ray_t* keys = ray_dict_keys(arg); + if (keys && keys->len > 0) { + for (int64_t i = 0; i < keys->len; i++) { + const char* p = NULL; + size_t n = 0; + if (keys->type == RAY_SYM) { + const int64_t* kd = (const int64_t*)ray_data(keys); + ray_t* ks = ray_sym_str(kd[i]); + if (ks) { p = ray_str_ptr(ks); n = ray_str_len(ks); } + } else if (keys->type == RAY_LIST) { + ray_t* ke = ((ray_t**)ray_data(keys))[i]; + if (!ke || ke->type != -RAY_SYM) + return ray_error("type", ".ipc.open: option keys must be symbols, got %s", + ke ? ray_type_name(ke->type) : "null"); + ray_t* ks = ray_sym_str(ke->i64); + if (ks) { p = ray_str_ptr(ks); n = ray_str_len(ks); } + } else { + return ray_error("type", ".ipc.open: option keys must be symbols, got %s", + ray_type_name(keys->type)); + } + bool known = (n == 7 && memcmp(p, "timeout", 7) == 0) + || (n == 8 && memcmp(p, "compress", 8) == 0); + if (!known) + return ray_error("domain", ".ipc.open: unknown option `%.*s` (expected `timeout` or `compress`)", + (int)n, p ? p : ""); + } + } + + ray_t* err = NULL; + int64_t tv = 0; + int got = opts_i64(arg, "timeout", &tv, &err); + if (got < 0) return err; + if (got == 1 && tv != NULL_I64) { + if (tv < 0) + return ray_error("domain", ".ipc.open timeout must be >= 0, got %lld", + (long long)tv); + *timeout_ms = (tv > INT_MAX) ? INT_MAX : (int)tv; + } + + int64_t cv = 0; + got = opts_i64(arg, "compress", &cv, &err); + if (got < 0) return err; + if (got == 1) { + /* 0N = never. 0 = always. n = compress payloads larger than n. */ + if (cv == NULL_I64) *compress_threshold = RAY_IPC_COMPRESS_NEVER; + else if (cv < 0) return ray_error("domain", ".ipc.open compress threshold must be >= 0 or 0N (never), got %lld", (long long)cv); + else *compress_threshold = (size_t)cv; + } + return NULL; +} diff --git a/src/core/ipc.h b/src/core/ipc.h index 2a77fc623..a961c40f3 100644 --- a/src/core/ipc.h +++ b/src/core/ipc.h @@ -33,8 +33,27 @@ #define RAY_IPC_COMPRESS_THRESHOLD 2000 +/* Sentinel threshold: no payload length can exceed it, so a link carrying + * it never compresses. Compression is a sender-side, per-frame decision + * signalled by RAY_IPC_FLAG_COMPRESSED, so a link may skip it unilaterally + * without any negotiation — a peer on any build still reads the frame. */ +#define RAY_IPC_COMPRESS_NEVER ((size_t)-1) + +/* "Not specified": the link decides from peer locality + * (ray_ipc_link_threshold). Never stored on a connection — resolved to a + * concrete threshold when the connection is established. */ +#define RAY_IPC_COMPRESS_AUTO ((size_t)-2) + +/* Compression policy for one link: loopback and UNIX-domain peers never + * compress (no bandwidth to buy with the CPU), everything else keeps the + * compiled-in default. An unknown peer keeps the default. */ +size_t ray_ipc_link_threshold(ray_sock_t fd); + size_t ray_ipc_compress(const uint8_t* src, size_t len, uint8_t* dst, size_t dst_cap); +/* As ray_ipc_compress, with an explicit threshold instead of the default. */ +size_t ray_ipc_compress_at(const uint8_t* src, size_t len, + uint8_t* dst, size_t dst_cap, size_t threshold); size_t ray_ipc_decompress(const uint8_t* src, size_t clen, uint8_t* dst, size_t dst_len); @@ -86,32 +105,6 @@ int64_t ray_ipc_listen(ray_poll_t* poll, uint16_t port); /* Bind to a specific IPv4 address; host NULL/empty means INADDR_ANY (#427). */ int64_t ray_ipc_listen_at(ray_poll_t* poll, const char* host, uint16_t port); -/* ===== Legacy server API (wraps poll internally for tests) ===== */ - -typedef struct ray_ipc_conn { - ray_sock_t fd; - uint8_t* rx_buf; - size_t rx_len; - size_t rx_need; - uint8_t phase; - ray_ipc_header_t hdr; -} ray_ipc_conn_t; - -typedef struct ray_ipc_server { - ray_sock_t listen_fd; - int poll_fd; - ray_ipc_conn_t conns[RAY_IPC_MAX_CONNS]; - uint32_t n_conns; - bool running; - char auth_secret[256]; /* password from -u/-U */ - bool restricted; /* -U mode */ -} ray_ipc_server_t; - -ray_err_t ray_ipc_server_init(ray_ipc_server_t* srv, uint16_t port); -ray_err_t ray_ipc_server_init_at(ray_ipc_server_t* srv, const char* host, uint16_t port); -void ray_ipc_server_destroy(ray_ipc_server_t* srv); -int ray_ipc_poll(ray_ipc_server_t* srv, int timeout_ms); - /* ===== Connection-handle API ===== * * One handle namespace: a handle is the poll selector id of an IPC @@ -130,6 +123,24 @@ int ray_ipc_poll(ray_ipc_server_t* srv, int timeout_ms); int64_t ray_ipc_connect(const char* host, uint16_t port, const char* user, const char* password, int timeout_ms); +/* As ray_ipc_connect, with an explicit compression threshold for the new + * link. RAY_IPC_COMPRESS_AUTO keeps the locality-derived default. */ +int64_t ray_ipc_connect_opts(const char* host, uint16_t port, + const char* user, const char* password, + int timeout_ms, size_t compress_threshold); + +/* Parse .ipc.open's optional second argument: an integer timeout in + * milliseconds, or a dict with optional `timeout` and `compress`. + * `compress` is a threshold in bytes — 0N never compresses, 0 always + * does, n compresses payloads larger than n; absent leaves + * RAY_IPC_COMPRESS_AUTO. Returns NULL on success, or an error object + * the caller returns as-is. */ +ray_t* ray_ipc_parse_open_opts(ray_t* arg, int* timeout_ms, + size_t* compress_threshold); + +/* The compression threshold in force on an open handle, or + * RAY_IPC_COMPRESS_AUTO if the handle does not resolve. */ +size_t ray_ipc_handle_threshold(int64_t handle); void ray_ipc_close(int64_t handle); ray_t* ray_ipc_send(int64_t handle, ray_t* msg); ray_err_t ray_ipc_send_async(int64_t handle, ray_t* msg); @@ -143,6 +154,11 @@ ray_err_t ray_ipc_try_send_async(int64_t handle, ray_t* msg); * return codes as ray_ipc_try_send_async, which is now the one-connection * composition of the two. */ ray_err_t ray_ipc_frame_async(ray_t* msg, ray_poll_frame_t** out); +/* As ray_ipc_frame_async, framing at an explicit compression threshold. + * Multicast builds one framing per distinct subscriber policy with this, so + * a topic whose subscribers are all local never compresses (#551). */ +ray_err_t ray_ipc_frame_async_at(ray_t* msg, size_t compress_threshold, + ray_poll_frame_t** out); ray_err_t ray_ipc_try_send_frame(int64_t handle, ray_poll_frame_t* frame); /* Transmit backlog (#486). A connection's queue admits a frame only while diff --git a/src/core/mcast.c b/src/core/mcast.c index c2bd08646..84eca04aa 100644 --- a/src/core/mcast.c +++ b/src/core/mcast.c @@ -33,7 +33,9 @@ struct ray_mcast { int64_t published; int64_t delivered; int64_t dropped; - int64_t framed; /* wire frames built: one per publication, not per subscriber */ + int64_t framed; /* wire frames built: one per DISTINCT compression + * policy per publication (#551), not one per publication + * and not one per subscriber */ }; static int64_t topic_sym(ray_t* topic) { @@ -267,6 +269,42 @@ ray_t* ray_mcast_unsub(ray_poll_t* poll, int64_t handle, ray_t* topic) { return RAY_NULL_OBJ; } +/* At most this many distinct compression policies get their own framing per + * publication. Two covers the local/remote split that motivates this; the + * spare slots absorb explicit per-connection `compress` thresholds without + * letting a pathological topic serialize once per subscriber. */ +#define MC_MAX_FRAMINGS 4 + +typedef struct { + size_t thr; + ray_poll_frame_t* frame; +} mc_framing_t; + +/* The framing this subscriber's link wants, building it on first use. Past + * MC_MAX_FRAMINGS distinct thresholds everyone shares the first framing — + * correct for any peer (the receiver honours the per-frame COMPRESSED flag), + * just not that peer's preference. */ +static ray_poll_frame_t* mc_frame_for(ray_mcast_t* mc, ray_t* msg, int64_t handle, + mc_framing_t* framings, int32_t* n, + ray_err_t* ferr) { + size_t thr = ray_ipc_handle_threshold(handle); + if (thr == RAY_IPC_COMPRESS_AUTO) thr = (size_t)RAY_IPC_COMPRESS_THRESHOLD; + + for (int32_t f = 0; f < *n; f++) + if (framings[f].thr == thr) return framings[f].frame; + if (*n == MC_MAX_FRAMINGS) return framings[0].frame; + + ray_poll_frame_t* frame = NULL; + ray_err_t e = ray_ipc_frame_async_at(msg, thr, &frame); + if (e != RAY_OK || !frame) { *ferr = e == RAY_OK ? RAY_ERR_IO : e; return NULL; } + framings[*n].thr = thr; + framings[*n].frame = frame; + (*n)++; + mc->framed++; + return frame; +} + + ray_t* ray_mcast_pub(ray_poll_t* poll, ray_t* topic, ray_t* payload) { if (!poll) return ray_error("domain", ".mc.pub requires an active poll"); int64_t sym = topic_sym(topic); @@ -293,22 +331,30 @@ ray_t* ray_mcast_pub(ray_poll_t* poll, ray_t* topic, ray_t* payload) { } } - /* Serialize and compress once; every subscriber's queue shares the - * frame and keeps only its own write offset (#487). */ - ray_poll_frame_t* frame = NULL; - ray_err_t ferr = ray_ipc_frame_async(msg, &frame); - ray_release(msg); - if (ferr != RAY_OK || !frame) { - if (dead_handles) ray_sys_free(dead_handles); - return ferr == RAY_ERR_OOM ? ray_error("oom", NULL) - : ray_error("io", ".mc.pub could not frame the payload"); - } - mc->framed++; + /* Serialize once per distinct compression policy; every subscriber that + * wants that policy shares the frame and keeps only its own write offset + * (#487). A topic whose subscribers are all local — the tickerplant + * case — still builds exactly one framing, and never compresses it + * (#551). A mixed topic pays one extra serialization rather than one + * per subscriber. */ + mc_framing_t framings[MC_MAX_FRAMINGS]; + int32_t n_framings = 0; + ray_err_t ferr = RAY_OK; t->next_seq++; mc->published++; - for (int32_t i = 0; i < t->n_subs;) { + /* Build every framing this topic needs BEFORE sending any of them. A + * framing failure is a local resource failure that says nothing about + * any peer, so it must neither drop a subscriber the way a failed send + * does nor leave the publication half fanned out: with nothing sent + * yet, the error below leaves every subscription exactly as it was. */ + for (int32_t i = 0; i < t->n_subs && ferr == RAY_OK; i++) + (void)mc_frame_for(mc, msg, t->subs[i].handle, framings, &n_framings, &ferr); + + for (int32_t i = 0; ferr == RAY_OK && i < t->n_subs;) { + ray_poll_frame_t* frame = mc_frame_for(mc, msg, t->subs[i].handle, + framings, &n_framings, &ferr); ray_err_t rc = ray_ipc_try_send_frame(t->subs[i].handle, frame); if (rc == RAY_OK) { t->subs[i].last_sent_seq = seq; @@ -322,7 +368,18 @@ ray_t* ray_mcast_pub(ray_poll_t* poll, ray_t* topic, ray_t* payload) { dead_handles[dead_n++] = dead; } } - ray_poll_frame_release(frame); + for (int32_t f = 0; f < n_framings; f++) + ray_poll_frame_release(framings[f].frame); + ray_release(msg); + if (ferr != RAY_OK) { + /* Framing is all done before the first send, so nothing was + * delivered and no subscriber was dropped (dead_n is 0 here). + * Report the failure instead of returning a sequence number for a + * publication that never fanned out. */ + if (dead_handles) ray_sys_free(dead_handles); + return ferr == RAY_ERR_OOM ? ray_error("oom", NULL) + : ray_error("io", ".mc.pub could not frame the payload"); + } if (t->n_subs == 0) { int32_t ti = topic_index(mc, sym); if (ti >= 0) remove_topic(mc, ti); diff --git a/src/core/poll.c b/src/core/poll.c index ce8a38bc1..8f2ef5b00 100644 --- a/src/core/poll.c +++ b/src/core/poll.c @@ -66,7 +66,7 @@ void ray_poll_drain_timers(ray_poll_t* poll) ray_poll_buf_t* ray_poll_buf_new(int64_t size) { - ray_poll_buf_t* buf = (ray_poll_buf_t*)ray_sys_alloc( + ray_poll_buf_t* buf = (ray_poll_buf_t*)ray_alloc_raw( sizeof(ray_poll_buf_t) + (size_t)size); if (!buf) return NULL; buf->next = NULL; @@ -82,14 +82,14 @@ void ray_poll_buf_free(ray_poll_buf_t* buf) while (buf) { ray_poll_buf_t* next = buf->next; if (buf->frame) ray_poll_frame_release(buf->frame); - ray_sys_free(buf); + ray_free_raw(buf); buf = next; } } ray_poll_frame_t* ray_poll_frame_new(int64_t size) { - ray_poll_frame_t* f = (ray_poll_frame_t*)ray_sys_alloc( + ray_poll_frame_t* f = (ray_poll_frame_t*)ray_alloc_raw( sizeof(ray_poll_frame_t) + (size_t)size); if (!f) return NULL; f->rc = 1; @@ -104,13 +104,13 @@ void ray_poll_frame_retain(ray_poll_frame_t* f) void ray_poll_frame_release(ray_poll_frame_t* f) { - if (f && --f->rc == 0) ray_sys_free(f); + if (f && --f->rc == 0) ray_free_raw(f); } ray_poll_buf_t* ray_poll_buf_from_frame(ray_poll_frame_t* f) { if (!f) return NULL; - ray_poll_buf_t* buf = (ray_poll_buf_t*)ray_sys_alloc(sizeof(ray_poll_buf_t)); + ray_poll_buf_t* buf = (ray_poll_buf_t*)ray_alloc_raw(sizeof(ray_poll_buf_t)); if (!buf) return NULL; buf->next = NULL; buf->size = f->size; diff --git a/src/core/pool.c b/src/core/pool.c index d883e9da2..0b81070ab 100644 --- a/src/core/pool.c +++ b/src/core/pool.c @@ -154,13 +154,9 @@ static ray_err_t ray_pool_create_impl(ray_pool_t* pool, uint32_t n_workers, long v = strtol(env, NULL, 10); n_workers = (v > 0) ? (uint32_t)v : 0; } else { - /* Physical cores, not SMT threads: the pool's kernels are - * memory-bound, and hyperthread pairs sharing one core's - * load/store machinery only contend (full ClickBench suite - * measured ~11% slower at 32 SMT threads than at the 16 - * physical cores of a 5950X). ray_physical_core_count falls - * back to the logical count when topology is unreadable. */ - uint32_t ncpu = ray_physical_core_count(); + /* Default to every online logical CPU. Individual operations may + * bound their task count to their workload or memory budget. */ + uint32_t ncpu = ray_thread_count(); n_workers = (ncpu > 1) ? ncpu - 1 : 0; } } @@ -390,31 +386,10 @@ void ray_pool_dispatch(ray_pool_t* pool, ray_pool_fn fn, void* ctx, ray_rc_sync = false; } -/* -------------------------------------------------------------------------- - * ray_pool_dispatch_n — dispatch exactly n_tasks tasks, each [i, i+1) - * -------------------------------------------------------------------------- */ - -void ray_pool_dispatch_n(ray_pool_t* pool, ray_pool_fn fn, void* ctx, - uint32_t n_tasks) { - if (n_tasks == 0) return; - - /* Grow ring if needed */ - if (n_tasks > pool->task_cap) { - uint32_t new_cap = pool->task_cap; - while (new_cap < n_tasks && new_cap < MAX_RING_CAP) new_cap *= 2; - if (new_cap > pool->task_cap) { - ray_pool_task_t* new_tasks = (ray_pool_task_t*)ray_sys_realloc( - pool->tasks, new_cap * sizeof(ray_pool_task_t)); - if (new_tasks) { - pool->tasks = new_tasks; - pool->task_cap = new_cap; - } - } - } - - /* Clamp n_tasks to task_cap to prevent ring overflow */ - if (n_tasks > pool->task_cap) n_tasks = pool->task_cap; - +/* One round of ray_pool_dispatch_n: tasks [first, first+n_tasks), each handed + * to fn as [i, i+1) with its ABSOLUTE index, n_tasks <= task_cap. */ +static void dispatch_n_round(ray_pool_t* pool, ray_pool_fn fn, void* ctx, + uint32_t first, uint32_t n_tasks) { /* Carve a fresh window [base, base+n_tasks) off the monotonic high-water * mark (the prior dispatch is fully claimed, so base == task_claim). */ uint64_t base = atomic_load_explicit(&pool->task_limit, memory_order_relaxed); @@ -424,8 +399,8 @@ void ray_pool_dispatch_n(ray_pool_t* pool, ray_pool_fn fn, void* ctx, uint32_t slot = (uint32_t)((base + i) & (pool->task_cap - 1)); pool->tasks[slot].fn = fn; pool->tasks[slot].ctx = ctx; - pool->tasks[slot].start = (int64_t)i; - pool->tasks[slot].end = (int64_t)i + 1; + pool->tasks[slot].start = (int64_t)(first + i); + pool->tasks[slot].end = (int64_t)(first + i) + 1; } atomic_store_explicit(&pool->pending, n_tasks, memory_order_relaxed); @@ -477,6 +452,40 @@ void ray_pool_dispatch_n(ray_pool_t* pool, ray_pool_fn fn, void* ctx, ray_rc_sync = false; } +/* -------------------------------------------------------------------------- + * ray_pool_dispatch_n — dispatch exactly n_tasks tasks, each [i, i+1) + * -------------------------------------------------------------------------- */ + +void ray_pool_dispatch_n(ray_pool_t* pool, ray_pool_fn fn, void* ctx, + uint32_t n_tasks) { + if (n_tasks == 0) return; + + /* Grow ring if needed */ + if (n_tasks > pool->task_cap) { + uint32_t new_cap = pool->task_cap; + while (new_cap < n_tasks && new_cap < MAX_RING_CAP) new_cap *= 2; + if (new_cap > pool->task_cap) { + ray_pool_task_t* new_tasks = (ray_pool_task_t*)ray_sys_realloc( + pool->tasks, new_cap * sizeof(ray_pool_task_t)); + if (new_tasks) { + pool->tasks = new_tasks; + pool->task_cap = new_cap; + } + } + } + + /* The ring holds task_cap tasks at a time. Anything past that runs in + * further rounds rather than being dropped: a window with more + * partitions than the ring, or a join with more morsels, used to lose + * every task past the cap silently. */ + for (uint32_t first = 0; first < n_tasks; ) { + uint32_t batch = n_tasks - first; + if (batch > pool->task_cap) batch = pool->task_cap; + dispatch_n_round(pool, fn, ctx, first, batch); + first += batch; + } +} + /* -------------------------------------------------------------------------- * Global pool singleton (lazy init) * -------------------------------------------------------------------------- */ diff --git a/src/core/sock.c b/src/core/sock.c index 661d8ff27..750af93a2 100644 --- a/src/core/sock.c +++ b/src/core/sock.c @@ -357,3 +357,49 @@ ray_err_t ray_sock_set_blocking(ray_sock_t s) #endif return RAY_OK; } + +/* ===== Link locality ===== */ + +bool ray_sock_addr_is_local(const void* sa, size_t salen) +{ + /* sa_family_t is not a Win32 type; take the size from the member. */ + if (!sa || salen < sizeof(((const struct sockaddr*)0)->sa_family)) return false; + const struct sockaddr* a = (const struct sockaddr*)sa; + + switch (a->sa_family) { +#ifndef RAY_OS_WINDOWS + case AF_UNIX: + /* Same machine by construction. */ + return true; +#endif + case AF_INET: { + if (salen < sizeof(struct sockaddr_in)) return false; + const struct sockaddr_in* s4 = (const struct sockaddr_in*)sa; + /* The whole 127/8 block is loopback, not just 127.0.0.1. */ + uint32_t h = ntohl(s4->sin_addr.s_addr); + return (h >> 24) == 127u; + } + case AF_INET6: { + if (salen < sizeof(struct sockaddr_in6)) return false; + const struct sockaddr_in6* s6 = (const struct sockaddr_in6*)sa; + const uint8_t* b = (const uint8_t*)&s6->sin6_addr; + if (IN6_IS_ADDR_LOOPBACK(&s6->sin6_addr)) return true; + /* A dual-stack listener reports a v4 peer as ::ffff:a.b.c.d, so + * unwrap the mapping before judging it. */ + if (IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr)) return b[12] == 127u; + return false; + } + default: + return false; + } +} + +bool ray_sock_peer_is_local(ray_sock_t s) +{ + if (s == RAY_INVALID_SOCK) return false; + struct sockaddr_storage ss; + socklen_t len = (socklen_t)sizeof(ss); + memset(&ss, 0, sizeof(ss)); + if (getpeername((int)s, (struct sockaddr*)&ss, &len) != 0) return false; + return ray_sock_addr_is_local(&ss, (size_t)len); +} diff --git a/src/core/sock.h b/src/core/sock.h index c11b6691d..e35e566c1 100644 --- a/src/core/sock.h +++ b/src/core/sock.h @@ -66,4 +66,16 @@ void ray_sock_close(ray_sock_t s); ray_err_t ray_sock_set_nonblocking(ray_sock_t s); ray_err_t ray_sock_set_blocking(ray_sock_t s); +/* ===== Link locality ===== + * True when the peer is on this machine: AF_UNIX, IPv4 127/8, IPv6 ::1, + * or a v4-mapped loopback. `sa` points at a `struct sockaddr`; it is + * taken as void* so this header stays free of . A short + * or NULL address reads as non-local rather than being trusted. + * + * ray_sock_peer_is_local resolves the peer with getpeername and answers + * false when there is none (unconnected socket, bad fd) — an unknown + * link must fall back to the conservative default, never to "local". */ +bool ray_sock_addr_is_local(const void* sa, size_t salen); +bool ray_sock_peer_is_local(ray_sock_t s); + #endif /* RAY_SOCK_H */ diff --git a/src/lang/eval.c b/src/lang/eval.c index a3f08df94..e481002b3 100644 --- a/src/lang/eval.c +++ b/src/lang/eval.c @@ -2080,6 +2080,13 @@ static void add_eval_error_frame(ray_t* nfo, ray_t* node) { /* Execute compiled bytecode for a lambda. */ static ray_t* vm_exec(ray_t* lambda, ray_t** call_args, int64_t argc); +/* Sym ID of "self" for call_lambda's frame. It belongs to the current sym + * table: runtime destroy tears the table down and the next runtime can give + * "self" a different ID, so ray_lang_destroy resets it, as ray_compile_reset + * does for the compiler's special-form IDs. A stale ID bound the lambda under + * a name that was no longer "self", shadowing whatever variable owned it. */ +static int64_t g_call_self_sym = -1; + /* Call a lambda: compile on first call, then execute bytecode. */ ray_t* call_lambda(ray_t* lambda, ray_t** call_args, int64_t argc) { /* Lazy compilation on first call */ @@ -2110,11 +2117,8 @@ ray_t* call_lambda(ray_t* lambda, ray_t** call_args, int64_t argc) { } /* Bind 'self' to the current lambda for recursion */ - { - static int64_t self_sym_id = -1; - if (self_sym_id < 0) self_sym_id = ray_sym_intern("self", 4); - ray_env_set_local(self_sym_id, lambda); - } + if (g_call_self_sym < 0) g_call_self_sym = ray_sym_intern("self", 4); + ray_env_set_local(g_call_self_sym, lambda); int64_t* param_ids = (int64_t*)ray_data(params_list); for (int64_t i = 0; i < param_count && i < argc; i++) { @@ -3587,6 +3591,7 @@ void ray_lang_destroy(void) { ray_dl_reset_rules(); ray_env_destroy(); ray_compile_reset(); + g_call_self_sym = -1; } /* ══════════════════════════════════════════ diff --git a/src/lang/internal.h b/src/lang/internal.h index bc21a86f0..0f73e5f89 100644 --- a/src/lang/internal.h +++ b/src/lang/internal.h @@ -461,6 +461,7 @@ ray_t* ray_max2_fn(ray_t* a, ray_t* b); * elements. Returns NaN if n <= 0. Used by aggr_med_per_group_buf in * query.c for the fast per-group median path. */ double ray_median_dbl_inplace(double* a, int64_t n); +double ray_nth_dbl_inplace(double* a, int64_t n, int64_t k); double ray_quantile_dbl_inplace(double* a, int64_t n, double q); ray_t* ray_mode_per_group_buf(ray_t* src, const int64_t* idx_buf, @@ -672,6 +673,7 @@ ray_t* ray_log_purge_fn(ray_t** args, int64_t n); /* Group (formerly static in eval.c, now extern for query.c) */ ray_t* ray_group_indices_fn(ray_t* x); +ray_t* agg_group_indices(ray_t* source); /* I/O and formatting builtins (formerly in eval.c, now in ops/builtins.c) */ ray_t* ray_println_fn(ray_t** args, int64_t n); diff --git a/src/mem/heap.c b/src/mem/heap.c index 1096ef782..1f82a7e69 100644 --- a/src/mem/heap.c +++ b/src/mem/heap.c @@ -1232,17 +1232,24 @@ static void* heap_direct_map_file(ray_heap_t* h, size_t map_size, * again on reuse. Under watermark pressure the cache is drained before * new memory is committed. File-backed spill blocks are never cached. * - * Fit: first block with map_size in [need, need + max(need/4, 2MB)] — the + * Fit: best block with map_size in [need, need + max(need/4, 2MB)] — the * repeat-query case hits exactly; the bound keeps waste at <= 25%. - * Concurrency: any thread may alloc or free a direct block, so a global - * spinlock guards the table — direct ops are a handful per query, so - * contention is nil. Budget: 1/16 of the anon watermark (physical RAM - * by default, the -m budget when set), capped at 512MB — no knob; the - * cache is invisible to correctness and self-drains under pressure. */ -#define RAY_DIRECT_CACHE_SLOTS 16 -typedef struct { void* base; size_t map_size; } ray_direct_cache_slot_t; -static ray_direct_cache_slot_t g_direct_cache[RAY_DIRECT_CACHE_SLOTS]; -static size_t g_direct_cache_bytes = 0; + * Concurrency: any thread may alloc or free a direct block. A global lock + * guards an intrusive FIFO of recently freed blocks. Its links reuse the dead + * allocation's prefix, so entry count needs no allocation or arbitrary cap. + * Replacement evicts the oldest cached blocks to admit newly freed scratch; + * obsolete CSV buffers cannot monopolize the byte budget indefinitely. + * Detached mappings are returned to the OS outside the cache lock. */ +typedef struct ray_direct_cache_node { + struct ray_direct_cache_node* next; + struct ray_direct_cache_node* prev; + size_t map_size; +} ray_direct_cache_node_t; +_Static_assert(sizeof(ray_direct_cache_node_t) <= RAY_DIRECT_HDR, + "cached links must fit in the dead direct-allocation prefix"); +static ray_direct_cache_node_t* g_direct_cache_head; +static ray_direct_cache_node_t* g_direct_cache_tail; +static size_t g_direct_cache_bytes; static _Atomic(int) g_direct_cache_spin = 0; static inline void direct_cache_lock(void) { @@ -1267,65 +1274,83 @@ static size_t direct_cache_budget(void) { return b; } -/* Take a cached block whose map_size fits [need, need + waste bound]. - * Returns the base pointer (its map_size in *out_size) or NULL. */ +/* Caller holds the lock. Ownership transfers from the cache to the caller. */ +static void direct_cache_unlink(ray_direct_cache_node_t* node) { + if (node->prev) node->prev->next = node->next; + else g_direct_cache_head = node->next; + if (node->next) node->next->prev = node->prev; + else g_direct_cache_tail = node->prev; + g_direct_cache_bytes -= node->map_size; +} + +/* Only detached nodes are passed here. No cache lock is held across munmap. */ +static void direct_cache_release(ray_direct_cache_node_t* node) { + while (node) { + ray_direct_cache_node_t* next = node->next; + size_t bytes = node->map_size; + atomic_fetch_sub_explicit(&g_anon_committed, (int64_t)bytes, memory_order_relaxed); + ray_vm_free(node, bytes); + node = next; + } +} + +/* Best fitting cached block within the existing waste bound. Exact matches + * stop the search; similar-sized analytical scratch is the common case. */ static void* direct_cache_take(size_t need, size_t* out_size) { - void* base = NULL; direct_cache_lock(); size_t slack = need / 4; if (slack < (2u << 20)) slack = (2u << 20); - for (int i = 0; i < RAY_DIRECT_CACHE_SLOTS; i++) { - size_t ms = g_direct_cache[i].map_size; - if (g_direct_cache[i].base && ms >= need && ms - need <= slack) { - base = g_direct_cache[i].base; - *out_size = ms; - g_direct_cache[i].base = NULL; - g_direct_cache[i].map_size = 0; - g_direct_cache_bytes -= ms; - break; + ray_direct_cache_node_t* best = NULL; + for (ray_direct_cache_node_t* node = g_direct_cache_head; node; node = node->next) { + size_t bytes = node->map_size; + if (bytes >= need && bytes - need <= slack && (!best || bytes < best->map_size)) { + best = node; + if (bytes == need) break; } } + if (best) { + *out_size = best->map_size; + direct_cache_unlink(best); + } direct_cache_unlock(); - return base; + return best; } -/* Stash a freed ANON block; returns true when cached (caller must then - * NOT munmap or un-commit it). */ +/* Recently freed blocks replace the oldest cached blocks within the unchanged + * byte budget. The caller has already removed this block from live statistics. */ static bool direct_cache_put(void* base, size_t map_size) { size_t budget = direct_cache_budget(); - if (budget == 0) return false; - bool cached = false; + if (map_size > budget) return false; + ray_direct_cache_node_t* evicted = NULL; direct_cache_lock(); - if (g_direct_cache_bytes + map_size <= budget) { - for (int i = 0; i < RAY_DIRECT_CACHE_SLOTS; i++) { - if (!g_direct_cache[i].base) { - g_direct_cache[i].base = base; - g_direct_cache[i].map_size = map_size; - g_direct_cache_bytes += map_size; - cached = true; - break; - } - } - } + while (g_direct_cache_bytes > budget - map_size) { + ray_direct_cache_node_t* old = g_direct_cache_head; + direct_cache_unlink(old); + old->next = evicted; + evicted = old; + } + ray_direct_cache_node_t* node = base; + node->map_size = map_size; + node->next = NULL; + node->prev = g_direct_cache_tail; + if (g_direct_cache_tail) g_direct_cache_tail->next = node; + else g_direct_cache_head = node; + g_direct_cache_tail = node; + g_direct_cache_bytes += map_size; direct_cache_unlock(); - return cached; + direct_cache_release(evicted); + return true; } -/* Release every cached block back to the kernel (memory pressure). */ +/* Detach under the lock; return mappings to the OS without blocking alloc/free + * callers on kernel unmapping and TLB invalidation. */ static void direct_cache_drain(void) { direct_cache_lock(); - for (int i = 0; i < RAY_DIRECT_CACHE_SLOTS; i++) { - if (g_direct_cache[i].base) { - size_t ms = g_direct_cache[i].map_size; - atomic_fetch_sub_explicit(&g_anon_committed, (int64_t)ms, - memory_order_relaxed); - ray_vm_free(g_direct_cache[i].base, ms); - g_direct_cache[i].base = NULL; - g_direct_cache[i].map_size = 0; - g_direct_cache_bytes -= ms; - } - } + ray_direct_cache_node_t* nodes = g_direct_cache_head; + g_direct_cache_head = g_direct_cache_tail = NULL; + g_direct_cache_bytes = 0; direct_cache_unlock(); + direct_cache_release(nodes); } void ray_heap_direct_cache_drain(void) { diff --git a/src/ops/agg.c b/src/ops/agg.c index fcdb99858..51b0138d1 100644 --- a/src/ops/agg.c +++ b/src/ops/agg.c @@ -959,6 +959,12 @@ static ray_t* quantile_prob_arg(ray_t* q_obj, double scale, const char* name, return NULL; } +double ray_nth_dbl_inplace(double* a, int64_t n, int64_t k) { + if (!a || n <= 0 || k < 0 || k >= n) return NULL_F64; + nth_element_dbl(a, 0, n - 1, k); + return a[k]; +} + double ray_quantile_dbl_inplace(double* a, int64_t n, double q) { if (n <= 0) return 0.0; if (n == 1) return a[0]; diff --git a/src/ops/agg_acc.h b/src/ops/agg_acc.h index 012e5f4de..eb60c43e7 100644 --- a/src/ops/agg_acc.h +++ b/src/ops/agg_acc.h @@ -30,6 +30,10 @@ static inline bool ray_valid_at(const ray_valid_t* v, int64_t row) { return ((const int32_t*)v->base)[row] != NULL_I32; case RAY_I16: return ((const int16_t*)v->base)[row] != NULL_I16; + case RAY_F32: { + float d = ((const float*)v->base)[row]; + return d == d; + } case RAY_F64: { double d = ((const double*)v->base)[row]; return d == d; /* only NaN fails self-equality */ @@ -67,6 +71,14 @@ typedef struct { /* param: per-aggregate integer parameter (K for top_n/bot_n via ext->agg_k[a]; * 0 and ignored for all other aggregates). */ ray_t* (*finalize) (const void* state, acc_arena_t* arena, int64_t param); + /* Native scalar emit: supplied by every registered streaming kernel, + * optional for buffered kernels. Writes one payload, returns whether null. */ + bool (*finalize_value)(const void* state, void* dst); + /* Optional concurrent update of the same initialized state. Init and + * finalize still run outside the update dispatch. The state layout and + * null semantics must match update_batch; no separately owned memory. */ + void (*update_shared)(void* states_base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, int64_t n); /* Release heap state owned by a per-group state slab. NULL for ACC_STREAMING. * The engine calls this on every init'd state once it won't be used again. */ void (*destroy)(void* state); diff --git a/src/ops/agg_engine.c b/src/ops/agg_engine.c index 08e6040ea..b0810f9b8 100644 --- a/src/ops/agg_engine.c +++ b/src/ops/agg_engine.c @@ -6,34 +6,76 @@ #include "ops/internal.h" /* col_vec_new, col_esz */ #include "ops/rowsel.h" /* ray_rowsel_meta, ray_rowsel_to_indices */ #include "lang/internal.h" /* sym_domain_rep */ +#include "table/domain.h" #include "table/sym.h" /* ray_read_sym */ #include #include +/* Radix output address: high 32 bits partition, low 32 bits local group. */ +typedef struct { int64_t idx; } agg_radix_order_t; + bool ray_agg_engine_v2 = true; /* knob; default on */ +static _Thread_local agg_route_stats_t route_stats; +void agg_route_reset(void) { memset(&route_stats, 0, sizeof(route_stats)); } +agg_route_stats_t agg_route_stats(void) { return route_stats; } +void agg_route_note_key_domain(void) { route_stats.key_domain_evals++; } +void agg_route_record(agg_route_t route) { + static const char* const names[AGG_ROUTE_COUNT] = { + "group: none", "group: legacy", "group: slices", "group: parted", + "group: v2 serial dense", "group: v2 serial hash", "group: v2 dense", + "group: v2 radix", "group: v2 hash", "group: v2 smallhash", "group: v2 indexed" + }; + route_stats.routes[route]++; + ray_profile_tick(names[route]); +} +void agg_route_reason(agg_v2_reason_t reason) { + route_stats.last_v2_reason = reason; + static const char* const names[] = { + "group: v2 admitted", "group: v2 unsupported shape", + "group: v2 key expression", "group: v2 unsupported key type", + "group: v2 aggregate expression", "group: v2 unsupported aggregate type", + "group: v2 buffered aggregate", "group: v2 unsupported parameter", + "group: v2 disabled", "group: v2 emit filter", "group: parallel wide-key strategy" + }; + if (reason != AGG_V2_ADMITTED) ray_profile_tick(names[reason]); +} + /* Read element `row` of an integer/temporal/SYM column widened to int64. */ static inline int64_t agg_read_key_i64(ray_t* col, const void* data, int64_t row); -/* Write a finalized scalar cell into output column slot i, marking nulls. */ -static void agg_put_cell(ray_t* out, int64_t i, ray_t* cell); /* Dense direct-index serial grouping; defined below agg_run_one. */ static int agg_group_keys_dense(ray_t** key_cols, int64_t nrows, const dense_plan_t* dp, agg_groups_t* out); -/* Binary-aggregate (pearson) serial driver; defined below agg_run_one. */ -ray_t* agg_run_one_bin(const agg_vtable_t* vt, ray_t* x_col, ray_t* y_col, - const uint32_t* gids, int64_t nrows, int64_t ngroups, - int64_t kparam); +static int agg_group_keys_parallel(ray_t** keys, uint32_t nkeys, int64_t rows, + const dense_plan_t* dp, agg_groups_t* out); -bool agg_v2_can_handle(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { +static bool agg_cancelled(void) { + ray_pool_t* pool = ray_pool_get(); + return pool && atomic_load_explicit(&pool->cancelled, memory_order_relaxed); +} + +/* Row-index consumers share one grouping and one stable index layout. */ +static bool agg_indexed_supported(uint16_t op, int8_t t) { + bool number = t >= RAY_BOOL && t <= RAY_TIMESTAMP; + bool scalar = number || t == RAY_GUID || t == RAY_SYM || t == RAY_STR; + if (op == OP_FIRST || op == OP_LAST) return scalar || t == RAY_LIST; + if (op == OP_MIN || op == OP_MAX) return t == RAY_GUID || t == RAY_STR || t == RAY_SYM; + if (op == OP_MEDIAN || op == OP_QUANTILE) return number; + if (op == OP_MODE || op == OP_TOP_N || op == OP_BOT_N) return scalar; + return false; +} + +agg_v2_reason_t agg_v2_admission(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { + if (!g || !op || !tbl) return AGG_V2_SHAPE; ray_op_ext_t* ext = find_ext(g, op->id); - if (!ext) return false; + if (!ext) return AGG_V2_SHAPE; /* Unbounded keys (>=1): every per-run key buffer the v2 engine touches * (key_cols/key_syms, the radix scatter's per-worker key row, key_data, * agg_group_keys' data[]) is now an exact carve, and the key-count params * downstream are uint32_t — so there is no fixed [16]/[255] cap left to * protect. ext->n_keys is uint32_t (widened, Task 1); dense direct-index * routing still self-limits to <=16 inside agg_dense_plan (see there). */ - if (ext->n_keys < 1) return false; /* need >=1 key */ + if (ext->n_keys < 1) return AGG_V2_SHAPE; /* need >=1 key */ /* n_aggs == 0 (table-distinct: ray_group(keys, n_keys, NULL, NULL, 0)) is * now ADMITTED (cut-3): every strategy below — dense/smallhash/radix * parallel, and the serial dense/hash tail in exec_group_v2_run — sizes @@ -45,7 +87,6 @@ bool agg_v2_can_handle(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { * `n_keys > 8` guard (group.c) died `nyi` on any 9+-column * `(distinct t)` — a live bug, not a deliberate width cap; fixed here * instead of adding a parallel distinct-only route. */ - if (!tbl) return false; /* A pushed WHERE filter (g->selection set) is now handled by exec_group_v2's * compact-table prologue: it gathers the selected rows of the keys/agg-inputs * and runs the normal strategy dispatch on that compact table. No bail. */ @@ -57,74 +98,208 @@ bool agg_v2_can_handle(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { ray_op_ext_t* ke = find_ext(g, ext->keys[0]); if (ke && ke->sym == ray_sym_intern("_src", 4)) { ray_t* cnt = ray_table_get_col(tbl, ray_sym_intern("_count", 6)); - if (cnt && cnt->type == RAY_I64) return false; + if (cnt && cnt->type == RAY_I64) return AGG_V2_SHAPE; } } /* every key must be a plain column scan of a supported type */ for (uint32_t k = 0; k < ext->n_keys; k++) { ray_op_t* key = op_node(g, ext->keys[k]); - if (!key || key->opcode != OP_SCAN) return false; + if (!key || key->opcode != OP_SCAN) return AGG_V2_KEY_EXPRESSION; ray_op_ext_t* kext = find_ext(g, key->id); ray_t* kc = kext ? ray_table_get_col(tbl, kext->sym) : NULL; - if (!kc) return false; + if (!kc) return AGG_V2_SHAPE; switch (kc->type) { case RAY_I64: case RAY_I32: case RAY_I16: case RAY_U8: case RAY_BOOL: case RAY_DATE: case RAY_TIME: - case RAY_TIMESTAMP: case RAY_SYM: break; - default: return false; + case RAY_TIMESTAMP: case RAY_SYM: + case RAY_F32: case RAY_F64: case RAY_GUID: case RAY_STR: case RAY_LIST: break; + default: return AGG_V2_KEY_TYPE; + } + } + + /* The native float hash reducer avoids building row slices for small + * parallel pools. At larger pools its replicated group states cost more + * than the shared directory. Byte/structural keys always use full-key + * grouping, and ordered/buffered consumers always share row slices. */ + ray_pool_t* pool = ray_pool_get(); + uint32_t workers = pool ? ray_pool_total_workers(pool) : 1; + if (ext->n_keys <= 8 && workers >= 2 && workers <= 8 && + ray_table_nrows(tbl) >= RAY_PARALLEL_THRESHOLD) { + bool floating = false, indexed = false; + for (uint32_t k = 0; k < ext->n_keys; k++) { + ray_op_ext_t* ke = find_ext(g, ext->keys[k]); + ray_t* col = ray_table_get_col(tbl, ke->sym); + if (col->type == RAY_F32 || col->type == RAY_F64) floating = true; + if (col->type == RAY_STR || col->type == RAY_GUID || col->type == RAY_LIST) indexed = true; } + for (uint32_t a = 0; a < ext->n_aggs; a++) { + ray_op_ext_t* ie = find_ext(g, ext->agg_ins[a]); + ray_t* col = ie ? ray_table_get_col(tbl, ie->sym) : NULL; + if (col && agg_indexed_supported(ext->agg_ops[a], col->type)) indexed = true; + } + if (floating && !indexed) return AGG_V2_PARALLEL_WIDE; } /* every aggregate must be a registry-resolvable plain-column scan */ for (uint32_t a = 0; a < ext->n_aggs; a++) { if (ext->agg_k && ext->agg_k[a]) { - if (ext->agg_ops[a] != OP_TOP_N && ext->agg_ops[a] != OP_BOT_N) return false; - if (ext->agg_k[a] < 1) return false; + if (ext->agg_ops[a] != OP_TOP_N && ext->agg_ops[a] != OP_BOT_N && ext->agg_ops[a] != OP_QUANTILE) return AGG_V2_PARAMETER; + if (ext->agg_ops[a] != OP_QUANTILE && ext->agg_k[a] < 1) return AGG_V2_PARAMETER; ray_op_t* in = op_node(g, ext->agg_ins[a]); - if (!in || in->opcode != OP_SCAN) return false; + if (!in || in->opcode != OP_SCAN) return AGG_V2_AGG_EXPRESSION; ray_op_ext_t* ie = find_ext(g, in->id); ray_t* ic = ie ? ray_table_get_col(tbl, ie->sym) : NULL; + if (ic && agg_indexed_supported(ext->agg_ops[a], ic->type)) continue; const agg_vtable_t* vt = ic ? agg_resolve(ext->agg_ops[a], ic->type) : NULL; - if (!vt || vt->kind != ACC_STREAMING) return false; + if (!vt) return AGG_V2_AGG_TYPE; + if (vt->kind != ACC_STREAMING) return AGG_V2_BUFFERED; continue; /* admitted */ } if (ext->agg_ins2 && ext->agg_ins2[a] != RAY_OP_NONE) { - if (!agg_is_binary_agg(ext->agg_ops[a])) return false; + if (!agg_is_binary_agg(ext->agg_ops[a])) return AGG_V2_SHAPE; ray_op_t* xin = op_node(g, ext->agg_ins[a]); ray_op_t* yin = op_node(g, ext->agg_ins2[a]); - if (!xin || xin->opcode != OP_SCAN || !yin || yin->opcode != OP_SCAN) return false; + if (!xin || xin->opcode != OP_SCAN || !yin || yin->opcode != OP_SCAN) return AGG_V2_AGG_EXPRESSION; ray_op_ext_t* xe = find_ext(g, xin->id); ray_op_ext_t* ye = find_ext(g, yin->id); ray_t* xc = xe ? ray_table_get_col(tbl, xe->sym) : NULL; ray_t* yc = ye ? ray_table_get_col(tbl, ye->sym) : NULL; - if (!xc || !yc) return false; - if (!agg_resolve(ext->agg_ops[a], xc->type)) return false; - if (!agg_resolve(ext->agg_ops[a], yc->type)) return false; + if (!xc || !yc) return AGG_V2_SHAPE; + if (!agg_resolve(ext->agg_ops[a], xc->type)) return AGG_V2_AGG_TYPE; + if (!agg_resolve(ext->agg_ops[a], yc->type)) return AGG_V2_AGG_TYPE; continue; /* admitted */ } if (ext->agg_ops[a] == OP_COUNT) { /* count: needs no typed input column */ - if (!agg_resolve(OP_COUNT, RAY_I64)) return false; + if (!agg_resolve(OP_COUNT, RAY_I64)) return AGG_V2_SHAPE; continue; } ray_op_t* in = op_node(g, ext->agg_ins[a]); - if (!in || in->opcode != OP_SCAN) return false; + if (!in || in->opcode != OP_SCAN) return AGG_V2_AGG_EXPRESSION; ray_op_ext_t* ie = find_ext(g, in->id); ray_t* ic = (ie) ? ray_table_get_col(tbl, ie->sym) : NULL; - if (!ic) return false; + if (!ic) return AGG_V2_SHAPE; + if (agg_indexed_supported(ext->agg_ops[a], ic->type)) continue; const agg_vtable_t* vt = agg_resolve(ext->agg_ops[a], ic->type); - if (!vt || vt->kind != ACC_STREAMING) return false; + if (!vt) return AGG_V2_AGG_TYPE; + if (vt->kind != ACC_STREAMING) return AGG_V2_BUFFERED; } - return true; + return AGG_V2_ADMITTED; +} + +bool agg_v2_can_handle(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { + return agg_v2_admission(g, op, tbl) == AGG_V2_ADMITTED; } /* ── Dense grouping eligibility selector (mirrors group.c DA path) ──────── * Decides whether the key tuple packs into a bounded direct-index slot space * (gid = sum_k (key_k - min_k)*strides[k]) so grouping can skip hashing. - * Eligible iff: 1..16 keys, every key is an integer/temporal/SYM type with no - * nulls, and the product of per-key ranges is no larger than the input row + * Eligible iff: 1..16 integer/temporal/SYM keys, including reserved null slots, + * and the product of per-key ranges is no larger than the input row * count (computed overflow-safely). Performs one min/max prescan per key column. * Aggregates may be ACC_STREAMING or ACC_BUFFERED (median/top-k): the dense * serial driver carries the per-group destroy lifecycle for buffered state. */ +/* Nullable signed domains reserve the last slot; never span the sentinel. */ +static int64_t agg_key_null(int8_t type) { + switch (type) { + case RAY_I16: return NULL_I16; + case RAY_I32: case RAY_DATE: case RAY_TIME: return NULL_I32; + default: return NULL_I64; + } +} +static inline int64_t agg_dense_component(const dense_plan_t* dp, uint32_t k, int64_t v) { + return dp->nullable[k] && v == dp->nulls[k] + ? dp->ranges[k] - 1 : v - dp->mins[k]; +} +static bool agg_dense_range(dense_plan_t* dp, uint32_t k, int64_t mn, int64_t mx) { + if (mx < mn) { dp->mins[k] = 0; dp->ranges[k] = 1; return dp->nullable[k]; } + uint64_t span = (uint64_t)mx - (uint64_t)mn; + if (span >= (uint64_t)INT64_MAX - (uint64_t)dp->nullable[k]) return false; + dp->mins[k] = mn; + dp->ranges[k] = (int64_t)span + 1 + dp->nullable[k]; + return true; +} + +/* Hoist type and null handling out of range scans so native integer + * reductions can vectorize, including nullable temporal keys. */ +static void agg_key_bounds(ray_t* key, int64_t start, int64_t end, bool nullable, + int64_t null, int64_t* lo, int64_t* hi) { + int64_t mn = INT64_MAX, mx = INT64_MIN; + const void* data = ray_data(key); + #define KEY_BOUNDS(T) do { \ + const T* p = data; \ + if (nullable) { \ + for (int64_t r = start; r < end; r++) { \ + int64_t v = (int64_t)p[r]; \ + int64_t low = v == null ? INT64_MAX : v; \ + int64_t high = v == null ? INT64_MIN : v; \ + if (low < mn) mn = low; \ + if (high > mx) mx = high; \ + } \ + } else { \ + for (int64_t r = start; r < end; r++) { \ + int64_t v = (int64_t)p[r]; \ + if (v < mn) mn = v; \ + if (v > mx) mx = v; \ + } \ + } \ + } while (0) + switch (key->type) { + case RAY_I64: case RAY_TIMESTAMP: KEY_BOUNDS(int64_t); break; + case RAY_I32: case RAY_DATE: case RAY_TIME: KEY_BOUNDS(int32_t); break; + case RAY_I16: KEY_BOUNDS(int16_t); break; + case RAY_U8: case RAY_BOOL: KEY_BOUNDS(uint8_t); break; + case RAY_SYM: + switch (key->attrs & RAY_SYM_W_MASK) { + case RAY_SYM_W8: KEY_BOUNDS(uint8_t); break; + case RAY_SYM_W16: KEY_BOUNDS(uint16_t); break; + case RAY_SYM_W32: KEY_BOUNDS(uint32_t); break; + default: KEY_BOUNDS(int64_t); break; + } + break; + } + #undef KEY_BOUNDS + *lo = mn; *hi = mx; +} + +typedef struct { + ray_t* key; + bool nullable; + int64_t null, rows; + uint32_t tasks; + int64_t* bounds; +} agg_key_bounds_ctx_t; +static void agg_key_bounds_fn(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; + agg_key_bounds_ctx_t* c = raw; + for (int64_t task = start; task < end; task++) { + int64_t begin = c->rows / c->tasks * task; + int64_t limit = task + 1 == c->tasks ? c->rows : c->rows / c->tasks * (task + 1); + agg_key_bounds(c->key, begin, limit, c->nullable, c->null, + &c->bounds[task * 2], &c->bounds[task * 2 + 1]); + } +} +static void agg_key_bounds_parallel(ray_t* key, int64_t rows, bool nullable, + int64_t null, int64_t* lo, int64_t* hi) { + ray_pool_t* pool = ray_pool_get(); + if (pool && rows >= RAY_PARALLEL_THRESHOLD) { + uint32_t tasks = ray_pool_total_workers(pool); + if (tasks > RAY_POOL_MAX_TASKS) tasks = RAY_POOL_MAX_TASKS; + int64_t* bounds = ray_calloc_raw((size_t)tasks * 2 * sizeof(int64_t)); + if (bounds) { + agg_key_bounds_ctx_t c = { key, nullable, null, rows, tasks, bounds }; + ray_pool_dispatch_n(pool, agg_key_bounds_fn, &c, tasks); + *lo = INT64_MAX; *hi = INT64_MIN; + for (uint32_t task = 0; task < tasks; task++) { + if (bounds[task * 2] < *lo) *lo = bounds[task * 2]; + if (bounds[task * 2 + 1] > *hi) *hi = bounds[task * 2 + 1]; + } + ray_free_raw(bounds); + return; + } + } + agg_key_bounds(key, 0, rows, nullable, null, lo, hi); +} + bool agg_dense_plan(ray_t** key_cols, uint32_t n_keys, const agg_vtable_t** vts, uint32_t n_aggs, int64_t nrows, dense_plan_t* out) { @@ -147,7 +322,8 @@ bool agg_dense_plan(ray_t** key_cols, uint32_t n_keys, case RAY_TIMESTAMP: case RAY_SYM: break; default: return false; } - if (kc->type != RAY_SYM && ray_vec_may_have_nulls(kc)) return false; + out->nullable[k] = kc->type != RAY_SYM && ray_vec_may_have_nulls(kc); + out->nulls[k] = agg_key_null(kc->type); if (nrows <= 0) return false; /* empty → no min/max, max mx) mx = v; \ - } } while (0) - switch (kc->type) { - case RAY_I64: case RAY_TIMESTAMP: DENSE_MINMAX(int64_t); break; - case RAY_I32: case RAY_DATE: case RAY_TIME: DENSE_MINMAX(int32_t); break; - case RAY_I16: DENSE_MINMAX(int16_t); break; - case RAY_U8: case RAY_BOOL: DENSE_MINMAX(uint8_t); break; - case RAY_SYM: - switch (kc->attrs & RAY_SYM_W_MASK) { - case RAY_SYM_W8: DENSE_MINMAX(uint8_t); break; - case RAY_SYM_W16: DENSE_MINMAX(uint16_t); break; - case RAY_SYM_W32: DENSE_MINMAX(uint32_t); break; - default: DENSE_MINMAX(int64_t); break; /* W64 */ - } - break; - default: - mn = mx = agg_read_key_i64(kc, data, 0); - for (int64_t r = 1; r < nrows; r++) { - int64_t v = agg_read_key_i64(kc, data, r); - if (v < mn) mn = v; - if (v > mx) mx = v; - } - } - #undef DENSE_MINMAX - if (mx < mn) return false; /* no live values */ - out->mins[k] = mn; - out->ranges[k] = mx - mn + 1; + agg_key_bounds_parallel(kc, nrows, out->nullable[k], out->nulls[k], &mn, &mx); + if (!agg_dense_range(out, k, mn, mx)) return false; } /* Composite packing; keep dense state O(input). */ @@ -286,21 +429,94 @@ int64_t agg_result_col_name(int64_t in_sym, uint16_t agg_op) { return in_sym; } +typedef struct { + const char* source; + char* output; + const int64_t* rows; + size_t width; + char null_value[16]; + _Atomic(bool) any_null; +} agg_gather_native_t; + +static void agg_gather_native_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_gather_native_t* c = raw; + bool any_null = false; + /* Constant byte widths let the compiler emit direct loads/stores. Workers + * own disjoint payload slots and never mutate the output object header. */ + #define GATHER_NATIVE(WIDTH) \ + for (int64_t i = start; i < end; i++) { \ + int64_t row = c->rows[i]; \ + if (row < 0) { \ + memcpy(c->output + (size_t)i * (WIDTH), c->null_value, (WIDTH)); \ + any_null = true; \ + } else memcpy(c->output + (size_t)i * (WIDTH), \ + c->source + (size_t)row * (WIDTH), (WIDTH)); \ + } + assert(c->width > 0 && c->width <= sizeof(c->null_value)); + switch (c->width) { + case 1: GATHER_NATIVE(1); break; + case 2: GATHER_NATIVE(2); break; + case 4: GATHER_NATIVE(4); break; + case 8: GATHER_NATIVE(8); break; + case 16: GATHER_NATIVE(16); break; + default: GATHER_NATIVE(c->width); break; + } + #undef GATHER_NATIVE + if (any_null) atomic_store_explicit(&c->any_null, true, memory_order_relaxed); +} + /* Build a result key column of src_col's type by gathering the first-row cell * of each group at native (type-exact) byte width. For SYM, adopts the source * domain so the intern ids resolve correctly. Caller owns the returned column. */ -static ray_t* agg_gather_key_col(ray_t* src_col, const int64_t* first_row, int64_t n) { +ray_t* ray_group_gather(ray_t* src_col, const int64_t* first_row, int64_t n) { + if (src_col->type == RAY_LIST) { + ray_t* out = ray_list_new(n); + if (!out || RAY_IS_ERR(out)) return out; + for (int64_t i = 0; i < n; i++) { + ray_t* value = first_row[i] < 0 ? NULL : ray_list_get(src_col, first_row[i]); + out = ray_list_append(out, value); + } + return out; + } + if (src_col->type == RAY_STR) { + const char** ptrs = ray_alloc_raw((size_t)(n ? n : 1) * sizeof(char*)); + uint32_t* lens = ray_alloc_raw((size_t)(n ? n : 1) * sizeof(uint32_t)); + if (!ptrs || !lens) { ray_free_raw(ptrs); ray_free_raw(lens); return ray_error("oom", NULL); } + for (int64_t i = 0; i < n; i++) { + size_t len = 0; + ptrs[i] = first_row[i] < 0 ? "" : ray_str_vec_get(src_col, first_row[i], &len); + lens[i] = (uint32_t)len; + } + ray_t* out = ray_str_vec_from_parts(ptrs, lens, NULL, n); + ray_free_raw(ptrs); ray_free_raw(lens); + return out; + } ray_t* out = col_vec_new(src_col, n); if (!out || RAY_IS_ERR(out)) return out; if (out->type == RAY_SYM) ray_sym_vec_adopt_domain(out, sym_domain_rep(src_col)); out->len = n; - size_t esz = col_esz(src_col); - const char* src = (const char*)ray_data(src_col); - char* dst = (char*)ray_data(out); - for (int64_t gi = 0; gi < n; gi++) - memcpy(dst + (size_t)gi * esz, src + (size_t)first_row[gi] * esz, esz); - if (src_col->attrs & RAY_ATTR_HAS_NULLS) out->attrs |= RAY_ATTR_HAS_NULLS; + agg_gather_native_t c = {.source = ray_data(src_col), .output = ray_data(out), + .rows = first_row, .width = col_esz(src_col)}; + #define GATHER_NULL(TYPE, VALUE) do { TYPE value = (VALUE); memcpy(c.null_value, &value, sizeof(value)); } while (0) + switch (src_col->type) { + case RAY_F64: GATHER_NULL(double, NULL_F64); break; + case RAY_F32: GATHER_NULL(float, NULL_F32); break; + case RAY_I64: case RAY_TIMESTAMP: GATHER_NULL(int64_t, NULL_I64); break; + case RAY_I32: case RAY_DATE: case RAY_TIME: GATHER_NULL(int32_t, NULL_I32); break; + case RAY_I16: GATHER_NULL(int16_t, NULL_I16); break; + default: break; /* SYM/GUID use zero payloads; BOOL/U8 have no null. */ + } + #undef GATHER_NULL + ray_pool_t* pool = ray_pool_get(); + if (ray_pool_par_dispatch_ok(pool, n, RAY_PARALLEL_THRESHOLD)) + ray_pool_dispatch(pool, agg_gather_native_run, &c, n); + else agg_gather_native_run(&c, 0, 0, n); + if (agg_cancelled()) { ray_release(out); return ray_error("cancel", NULL); } + if (ray_vec_may_have_nulls(src_col) || + (atomic_load_explicit(&c.any_null, memory_order_relaxed) && + src_col->type != RAY_BOOL && src_col->type != RAY_U8)) + out->attrs |= RAY_ATTR_HAS_NULLS; return out; } @@ -311,7 +527,7 @@ static ray_t* agg_gather_key_col(ray_t* src_col, const int64_t* first_row, int64 * src/ops/rowsel.h: per-segment NONE/ALL/MIX flags + morsel-local idx[] for MIX * segments). Rather than materialize a full O(rows-passed) index array * (ray_rowsel_to_indices) + a full compact column (the old prologue), the - * chunked strategies (dense serial+parallel, radix, smallhash) consume the + * chunked strategies (task-local dense, radix, smallhash) consume the * selection IN PLACE: each worker walks its assigned SELECTED rows in fixed-size * chunks (AGG_SEL_CHUNK), decoding each chunk's ORIGINAL row indices into a * small reused stack buffer, gathering only that chunk's key/agg-input values @@ -659,7 +875,8 @@ static bool agg_dense_plan_sel(ray_t** key_cols, uint32_t n_keys, int64_t n_sel, case RAY_TIMESTAMP: case RAY_SYM: break; default: return false; } - if (kc->type != RAY_SYM && ray_vec_may_have_nulls(kc)) return false; + out->nullable[k] = kc->type != RAY_SYM && ray_vec_may_have_nulls(kc); + out->nulls[k] = agg_key_null(kc->type); out->mins[k] = INT64_MAX; out->ranges[k] = 0; /* sentinels; filled below */ } @@ -683,6 +900,7 @@ static bool agg_dense_plan_sel(ray_t** key_cols, uint32_t n_keys, int64_t n_sel, int64_t mn = mins[k], mx = maxs[k]; for (int64_t i = 0; i < cn; i++) { int64_t v = agg_read_key_i64(kc, d, rows[i]); + if (out->nullable[k] && v == out->nulls[k]) continue; if (v < mn) mn = v; if (v > mx) mx = v; } @@ -690,9 +908,7 @@ static bool agg_dense_plan_sel(ray_t** key_cols, uint32_t n_keys, int64_t n_sel, } } for (uint32_t k = 0; k < n_keys; k++) { - if (maxs[k] < mins[k]) { scratch_free(pre_hdr); return false; } - out->mins[k] = mins[k]; - out->ranges[k] = maxs[k] - mins[k] + 1; + if (!agg_dense_range(out, k, mins[k], maxs[k])) { scratch_free(pre_hdr); return false; } } scratch_free(pre_hdr); @@ -712,21 +928,9 @@ static bool agg_dense_plan_sel(ray_t** key_cols, uint32_t n_keys, int64_t n_sel, } /* ══════════════════════════════════════════ - * Parallel two-phase hash group-by (Phase 1c) + * Integer tuple hashing for radix and small-hash grouping * ══════════════════════════════════════════ */ -/* Per-worker (and global merge) local group table: open-addressing hash on the - * tuple-hash → local gid; AoS per-group state blocks of `block` bytes. */ -typedef struct { - int32_t* ht; /* [htcap] slot -> local gid, -1 empty */ - int64_t htcap; - uint64_t htmask; - int64_t* first_row; /* [cap] min row idx per local group */ - char* states; /* [cap*block] AoS group state */ - int64_t ng, cap; - int oom; -} agg_local_t; - /* Tuple FNV-1a hash over all keys at row r — identical to agg_group_keys. * n_keys is uint32_t: unbounded key count (carries the untruncated ext->n_keys * through every caller). */ @@ -762,302 +966,20 @@ static inline int agg_tuple_eq(ray_t** key_cols, const void** key_data, return 1; } -static int agg_local_init(agg_local_t* loc, int64_t cap, int64_t htcap, int64_t block) { - loc->ng = 0; loc->cap = cap; loc->oom = 0; - loc->htcap = htcap; loc->htmask = (uint64_t)htcap - 1; - loc->ht = ray_alloc_raw((size_t)htcap * sizeof(int32_t)); - loc->first_row = ray_alloc_raw((size_t)cap * sizeof(int64_t)); - loc->states = ray_alloc_raw((size_t)cap * (size_t)block); - if (!loc->ht || !loc->first_row || !loc->states) { loc->oom = 1; return -1; } - for (int64_t i = 0; i < htcap; i++) loc->ht[i] = -1; - return 0; -} - -static void agg_local_destroy(agg_local_t* loc) { - ray_free_raw(loc->ht); ray_free_raw(loc->first_row); ray_free_raw(loc->states); - loc->ht = NULL; loc->first_row = NULL; loc->states = NULL; -} - -/* Run vt->destroy on every init'd per-group state in `loc` for buffered - * accumulators (destroy != NULL), then free the slab. Safe to call once a - * local/global table's groups have been merged/finalized: streaming accs - * (destroy == NULL) are skipped, and the call is idempotent on the slab via - * agg_local_destroy nulling the pointers (but must not be invoked twice on the - * same live buffered state — each caller below destroys a given table once). */ -static void agg_table_destroy(agg_local_t* loc, const agg_vtable_t** vts, - const size_t* off, size_t block, uint32_t n_aggs) { - for (uint32_t a = 0; a < n_aggs; a++) - if (vts[a]->destroy) - for (int64_t gg = 0; gg < loc->ng; gg++) - vts[a]->destroy(loc->states + (size_t)gg * block + off[a]); - agg_local_destroy(loc); -} +/* Parallel dense streaming aggregation. Each logical task owns a local key + * occupancy bitmap, group states, and first-row indices. Only occupied + * states are initialized or merged. This fallback covers small domains and + * selected/composite keys; capability-based strategies below avoid replication. */ -/* Context shared (read-only) across workers; per-worker writes go to locals[wid]. */ +/* Per-task dense slab. */ typedef struct { - ray_t** key_cols; - const void** key_data; - uint32_t n_keys; - const agg_vtable_t** vts; - const size_t* off; /* [n_aggs] byte offset of agg a in a block */ - size_t block; /* bytes per group block */ - uint32_t n_aggs; - const void** val_data; /* [n_aggs] base ptr or NULL (COUNT) */ - const int8_t* val_types; /* [n_aggs] */ - const bool* val_hasnull;/* [n_aggs] */ - const uint8_t* val_esz; /* [n_aggs] element size or 0 (COUNT) */ - /* Binary aggregates (pearson): second value column. NULL/0 for unary. */ - const void** val2_data; /* [n_aggs] */ - const int8_t* val2_types; /* [n_aggs] */ - const bool* val2_hasnull;/* [n_aggs] */ - const uint8_t* val2_esz; /* [n_aggs] */ - agg_local_t* locals; /* [nw] */ -} agg_par_ctx_t; - -/* Radix output order. idx packs (part << 32 | local gid) so finalize can - * recover both the partition and the in-partition state offset. */ -typedef struct { int64_t idx; } agg_radix_order_t; - -/* Phase A: per-worker local group + accumulate over chunk [start,end). */ -static void agg_phaseA_fn(void* vctx, uint32_t wid, int64_t start, int64_t end) { - agg_par_ctx_t* c = (agg_par_ctx_t*)vctx; - agg_local_t* loc = &c->locals[wid]; - if (loc->oom) return; - int64_t n = end - start; - if (n <= 0) return; - - uint32_t* cgid = ray_alloc_raw((size_t)n * sizeof(uint32_t)); - if (!cgid) { loc->oom = 1; return; } - - for (int64_t r = start; r < end; r++) { - uint64_t h = agg_tuple_hash(c->key_cols, c->key_data, c->n_keys, r); - uint64_t slot = h & loc->htmask; - int32_t g; - for (;;) { - int32_t gp = loc->ht[slot]; - if (gp < 0) { /* new group */ - if (loc->ng == loc->cap) { loc->oom = 1; ray_free_raw(cgid); return; } - g = (int32_t)loc->ng; - loc->ht[slot] = g; - loc->first_row[g] = r; - for (uint32_t a = 0; a < c->n_aggs; a++) - c->vts[a]->init(loc->states + (size_t)g * c->block + c->off[a]); - loc->ng++; - break; - } - if (agg_tuple_eq(c->key_cols, c->key_data, c->n_keys, r, loc->first_row[gp])) { - g = gp; - if (r < loc->first_row[g]) loc->first_row[g] = r; /* MIN across chunks */ - break; - } - slot = (slot + 1) & loc->htmask; - } - cgid[r - start] = (uint32_t)g; - } - - for (uint32_t a = 0; a < c->n_aggs; a++) { - if (c->vts[a]->update_batch2) { /* binary agg (pearson) */ - const void* vx = (const char*)c->val_data[a] + (size_t)start * c->val_esz[a]; - const void* vy = (const char*)c->val2_data[a] + (size_t)start * c->val2_esz[a]; - ray_valid_t valid_x = { vx, c->val_types[a], c->val_hasnull[a] }; - ray_valid_t valid_y = { vy, c->val2_types[a], c->val2_hasnull[a] }; - c->vts[a]->update_batch2(loc->states + c->off[a], c->block, cgid, - vx, vy, &valid_x, &valid_y, n, NULL); - } else { - const void* vals = (c->val_data[a]) - ? (const void*)((const char*)c->val_data[a] + (size_t)start * c->val_esz[a]) - : NULL; - ray_valid_t valid = { vals, c->val_types[a], - c->val_data[a] ? c->val_hasnull[a] : false }; - c->vts[a]->update_batch(loc->states + c->off[a], c->block, - cgid, vals, &valid, n, NULL); - } - } - ray_free_raw(cgid); -} - -/* Parallel two-phase path. key_cols/key_syms resolved by caller. */ -static ray_t* exec_group_v2_parallel( - ray_graph_t* g, ray_op_t* op, ray_t* tbl, int64_t nrows, - ray_t** key_cols, int64_t* key_syms, - const agg_vtable_t** vts, const size_t* off, size_t block) { - ray_op_ext_t* ext = find_ext(g, op->id); - uint32_t n_keys = ext->n_keys, n_aggs = ext->n_aggs; - ray_pool_t* pool = ray_pool_get(); - uint32_t nw = ray_pool_total_workers(pool); - - /* key data bases + per-agg value column bases/types (resolve once). */ - agg_desc_t d; - if (!agg_desc_init(&d, g, ext, tbl, key_cols)) return ray_error("oom", NULL); - - /* per-worker ht sized to next_pow2(2*nrows): over-allocates (each worker - * sees only its morsels) but memory isn't gated in 1c — simple & correct. */ - int64_t htcap = 16; - while (htcap < nrows * 2) htcap <<= 1; - int64_t cap = nrows > 0 ? nrows : 1; /* a worker can't exceed nrows groups */ - - agg_local_t* locals = ray_calloc_raw((size_t)((size_t)nw) * (sizeof(agg_local_t))); - if (!locals) { agg_desc_free(&d); return ray_error("oom", NULL); } - - int alloc_oom = 0; - for (uint32_t w = 0; w < nw; w++) - if (agg_local_init(&locals[w], cap, htcap, (int64_t)block) != 0) { alloc_oom = 1; break; } - if (alloc_oom) { - for (uint32_t w = 0; w < nw; w++) agg_local_destroy(&locals[w]); - ray_free_raw(locals); - agg_desc_free(&d); - return ray_error("oom", NULL); - } - - agg_par_ctx_t ctx = { - .key_cols = key_cols, .key_data = d.key_data, .n_keys = n_keys, - .vts = vts, .off = off, .block = block, .n_aggs = n_aggs, - .val_data = d.val_data, .val_types = d.val_types, - .val_hasnull = d.val_hasnull, .val_esz = d.val_esz, - .val2_data = d.val2_data, .val2_types = d.val2_types, - .val2_hasnull = d.val2_hasnull, .val2_esz = d.val2_esz, .locals = locals, - }; - ray_pool_dispatch(pool, agg_phaseA_fn, &ctx, nrows); - - for (uint32_t w = 0; w < nw; w++) - if (locals[w].oom) { - /* Phase A ran: workers may hold init'd buffered group states. */ - for (uint32_t i = 0; i < nw; i++) - agg_table_destroy(&locals[i], vts, off, block, n_aggs); - ray_free_raw(locals); - agg_desc_free(&d); - return ray_error("oom", NULL); - } - - /* ── Phase B: merge per-worker locals into a global table (serial) ── */ - agg_local_t gt = {0}; - if (agg_local_init(>, cap, htcap, (int64_t)block) != 0) { - agg_local_destroy(>); /* gt init failed: no init'd states in gt */ - for (uint32_t i = 0; i < nw; i++) - agg_table_destroy(&locals[i], vts, off, block, n_aggs); - ray_free_raw(locals); - agg_desc_free(&d); - return ray_error("oom", NULL); - } - - for (uint32_t w = 0; w < nw; w++) { - agg_local_t* loc = &locals[w]; - for (int64_t lg = 0; lg < loc->ng; lg++) { - int64_t fr = loc->first_row[lg]; - uint64_t h = agg_tuple_hash(key_cols, d.key_data, n_keys, fr); - uint64_t slot = h & gt.htmask; - int64_t gg; - for (;;) { - int32_t gp = gt.ht[slot]; - if (gp < 0) { /* new global group */ - gg = gt.ng; - gt.ht[slot] = (int32_t)gg; - gt.first_row[gg] = fr; - for (uint32_t a = 0; a < n_aggs; a++) - vts[a]->init(gt.states + (size_t)gg * block + off[a]); - gt.ng++; - break; - } - if (agg_tuple_eq(key_cols, d.key_data, n_keys, fr, gt.first_row[gp])) { - gg = gp; - if (fr < gt.first_row[gg]) gt.first_row[gg] = fr; /* MIN */ - break; - } - slot = (slot + 1) & gt.htmask; - } - for (uint32_t a = 0; a < n_aggs; a++) - vts[a]->merge(gt.states + (size_t)gg * block + off[a], - loc->states + (size_t)lg * block + off[a], NULL); - } - } - int64_t ng = gt.ng; - - /* Phase A locals no longer needed past the merge. Destroy each init'd local - * per-group state for buffered accumulators (merge copied values into gt) so - * the local buffers don't leak; do it BEFORE freeing the slabs. After this - * point, only gt holds live buffered state — every later path (success + - * error) destroys gt exactly once and need not touch locals again. */ - for (uint32_t i = 0; i < nw; i++) { - for (uint32_t a = 0; a < n_aggs; a++) - if (vts[a]->destroy) - for (int64_t lg = 0; lg < locals[i].ng; lg++) - vts[a]->destroy(locals[i].states + (size_t)lg * block + off[a]); - agg_local_destroy(&locals[i]); - } - ray_free_raw(locals); - - /* ── Phase C: emit key + agg columns in natural global-group order ── - * Group-by output order is UNSPECIFIED (callers ORDER BY for order), so we - * emit groups in build/creation order (global hash-slot insertion order) - * rather than sorting by first_row. gt.first_row[g] is still the gather - * index (any member row works) for the key columns. */ - ray_t* result = ray_table_new(n_keys + n_aggs); - if (!result || RAY_IS_ERR(result)) { - agg_table_destroy(>, vts, off, block, n_aggs); - agg_desc_free(&d); - return result ? result : ray_error("oom", NULL); - } - - for (uint32_t k = 0; k < n_keys; k++) { - ray_t* kc = agg_gather_key_col(key_cols[k], gt.first_row, ng); - if (!kc || RAY_IS_ERR(kc)) { - agg_table_destroy(>, vts, off, block, n_aggs); - agg_desc_free(&d); - ray_release(result); return kc ? kc : ray_error("oom", NULL); - } - result = ray_table_add_col(result, key_syms[k], kc); - ray_release(kc); - } - - for (uint32_t a = 0; a < n_aggs; a++) { - bool is_list = (vts[a]->out_type == RAY_LIST); - ray_t* out = is_list ? ray_list_new(ng) - : ray_vec_new(vts[a]->out_type, ng); - if (!out || RAY_IS_ERR(out)) { - agg_table_destroy(>, vts, off, block, n_aggs); - agg_desc_free(&d); - ray_release(result); return out ? out : ray_error("oom", NULL); - } - out->len = ng; - int64_t kparam = (ext->agg_k ? ext->agg_k[a] : 0); - for (int64_t i = 0; i < ng; i++) { - ray_t* cell = vts[a]->finalize(gt.states + (size_t)i * block + off[a], NULL, kparam); - if (is_list) { - out = ray_list_set(out, i, cell); /* retains cell */ - ray_release(cell); /* drop our local ref */ - } else { - agg_put_cell(out, i, cell); - ray_release(cell); - } - } - int64_t agg_name = agg_result_col_name(d.agg_syms[a], ext->agg_ops[a]); - result = ray_table_add_col(result, agg_name, out); - ray_release(out); - } - - agg_table_destroy(>, vts, off, block, n_aggs); - agg_desc_free(&d); - return result; -} - -/* ══════════════════════════════════════════════════════════════════════ - * Parallel DENSE group-by (low-card int/SYM keys; streaming OR buffered aggs). - * Per-worker flat slabs of `total_slots` AoS group states (slot == gid via the - * mixed-radix packing) + a per-worker first_row[slot] (INT64_MAX = untouched). - * Phase A: each worker packs its chunk rows into slots and update_batch's into - * its own slab. Phase B: merge per-worker slabs into a global slab. Phase C: - * collect occupied slots in slot order and emit (output order unspecified). - * No hashing. Buffered aggs (median/top-k) malloc a per-group buffer in their - * state; every init'd slot of every slab carries the vt->destroy lifecycle: - * worker buffers are freed once after being merged into the global slab, and - * the global slab's buffers are freed after finalize — exactly-once, mirroring - * agg_table_destroy on the hash path. */ - -/* Per-worker dense slab. */ -typedef struct { - char* states; /* [total_slots * block] AoS, every slot init'd */ - int64_t* first_row; /* [total_slots] min row idx touching slot, INT64_MAX = none */ + char* states; /* [total_slots * block] AoS, only occupied slots initialized */ + int64_t* first_row; /* initialized only for occupied slots */ + uint64_t* occupied; /* one bit per initialized local state */ int oom; + bool ready; + bool eager; + int64_t slots; } agg_dense_local_t; typedef struct { @@ -1077,30 +999,24 @@ typedef struct { ray_t* sel; const int64_t* sel_prefix; agg_valdesc_t vd; + int64_t task_rows; + uint32_t n_tasks; } agg_dense_ctx_t; -/* Initialize every slot's agg states in a freshly-allocated slab (min/max need - * INT64_MAX/MIN seeds, NOT calloc-zero), and first_row to INT64_MAX. */ -static int agg_dense_local_init(agg_dense_local_t* loc, int64_t total_slots, - const agg_vtable_t** vts, const size_t* off, - size_t block, uint32_t n_aggs) { +/* Each task owns a slice of one query allocation. Only the bitmap is + * cleared; states and first rows are initialized on first touch. */ +static int agg_dense_local_init(agg_dense_local_t* loc, int64_t total_slots, size_t block) { loc->oom = 0; - loc->states = ray_alloc_raw((size_t)total_slots * block); - loc->first_row = ray_alloc_raw((size_t)total_slots * sizeof(int64_t)); - if (!loc->states || !loc->first_row) { loc->oom = 1; return -1; } - for (int64_t s = 0; s < total_slots; s++) { - loc->first_row[s] = INT64_MAX; - for (uint32_t a = 0; a < n_aggs; a++) - vts[a]->init(loc->states + (size_t)s * block + off[a]); - } + size_t state_bytes = (size_t)total_slots * block; + size_t row_bytes = (size_t)total_slots * sizeof(int64_t); + size_t bits_bytes = ((size_t)total_slots + 63) / 64 * sizeof(uint64_t); + loc->first_row = (int64_t*)(loc->states + state_bytes); + loc->occupied = (uint64_t*)(loc->states + state_bytes + row_bytes); + memset(loc->occupied, 0, bits_bytes); + loc->ready = true; return 0; } -static void agg_dense_local_destroy(agg_dense_local_t* loc) { - ray_free_raw(loc->states); ray_free_raw(loc->first_row); - loc->states = NULL; loc->first_row = NULL; -} - /* Run vt->destroy on EVERY slot's buffered agg state in a dense slab (states + * total_slots blocks). Empty slots hold the init'd state (median/top-k init → * buf=NULL), so destroy on them is ray_free_raw(NULL) — safe. No-op when no agg has a @@ -1116,17 +1032,120 @@ static void agg_dense_slab_destroy_states(char* states, int64_t total_slots, vts[a]->destroy(states + (size_t)s * block + off[a]); } +static void agg_dense_local_destroy_states(agg_dense_local_t* loc, + const agg_vtable_t** vts, const size_t* off, size_t block, uint32_t n_aggs) { + if (!loc->ready) return; + for (uint32_t a = 0; a < n_aggs; a++) + if (vts[a]->destroy) + for (int64_t s = 0; s < loc->slots; s++) + if (loc->eager || (loc->occupied[s / 64] & (UINT64_C(1) << (s % 64)))) + vts[a]->destroy(loc->states + (size_t)s * block + off[a]); +} + +typedef struct { + agg_dense_local_t* locals; + int64_t slots; + const agg_vtable_t** vts; + const size_t* off; + size_t block; + uint32_t n_aggs, nw; + char* states; + int64_t* first; +} agg_dense_merge_ctx_t; +static void agg_dense_merge_fn(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_dense_merge_ctx_t* c = raw; + /* A single streaming state is already the complete result. Copy it + * directly instead of initializing and merging an identical state. */ + bool copy = c->nw == 1; + for (uint32_t a = 0; a < c->n_aggs && copy; a++) copy = c->vts[a]->destroy == NULL; + if (copy) { + const agg_dense_local_t* loc = &c->locals[0]; + for (int64_t s = start; s < end; s++) { + if (loc->occupied[s / 64] & (UINT64_C(1) << (s % 64))) { + c->first[s] = loc->first_row[s]; + memcpy(c->states + (size_t)s * c->block, loc->states + (size_t)s * c->block, c->block); + } else { + c->first[s] = INT64_MAX; + for (uint32_t a = 0; a < c->n_aggs; a++) + c->vts[a]->init(c->states + (size_t)s * c->block + c->off[a]); + } + } + return; + } + for (int64_t s = start; s < end; s++) { + c->first[s] = INT64_MAX; + for (uint32_t a = 0; a < c->n_aggs; a++) + c->vts[a]->init(c->states + (size_t)s * c->block + c->off[a]); + } + for (uint32_t w = 0; w < c->nw; w++) { + agg_dense_local_t* loc = &c->locals[w]; + for (int64_t word = start / 64; word <= (end - 1) / 64; word++) { + uint64_t bits = loc->occupied[word]; + if (word == start / 64) bits &= UINT64_MAX << (start % 64); + if (word == (end - 1) / 64 && end % 64) bits &= (UINT64_C(1) << (end % 64)) - 1; + while (bits) { + int64_t s = word * 64 + __builtin_ctzll(bits); + bits &= bits - 1; + if (loc->first_row[s] < c->first[s]) c->first[s] = loc->first_row[s]; + for (uint32_t a = 0; a < c->n_aggs; a++) + c->vts[a]->merge(c->states + (size_t)s * c->block + c->off[a], + loc->states + (size_t)s * c->block + c->off[a], NULL); + } + } + } +} +static inline bool agg_put_cell_value(ray_t* out, int64_t i, ray_t* cell); +/* Caller owns null metadata: parallel emitters reduce this flag after the + * barrier, while serial emitters may set it immediately. LISTs retain their + * separate ownership-aware path. */ +static inline bool agg_finalize_value(const agg_vtable_t* vt, const void* state, + ray_t* out, int64_t i, int64_t param) { + if (vt->finalize_value) + return vt->finalize_value(state, (char*)ray_data(out) + (size_t)i * col_esz(out)); + ray_t* cell = vt->finalize(state, NULL, param); + bool is_null = agg_put_cell_value(out, i, cell); + ray_release(cell); + return is_null; +} + +typedef struct { + const agg_vtable_t* vt; + ray_t* out; + const char* states; + const int64_t* slots; + size_t block, off; + int64_t param; + _Atomic(bool) any_null; +} agg_dense_emit_ctx_t; +static void agg_dense_emit_fn(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_dense_emit_ctx_t* c = raw; + bool any = false; + for (int64_t i = start; i < end; i++) { + const void* st = c->states + (size_t)(c->slots ? c->slots[i] : i) * c->block + c->off; + any |= agg_finalize_value(c->vt, st, c->out, i, c->param); + } + if (any) atomic_store_explicit(&c->any_null, true, memory_order_relaxed); +} + /* Compute the dense slot for row r via mixed-radix packing. */ static inline int64_t agg_dense_slot(const agg_dense_ctx_t* c, int64_t r) { int64_t slot = 0; for (uint32_t k = 0; k < c->n_keys; k++) - slot += (agg_read_key_i64(c->key_cols[k], c->key_data[k], r) - c->dp->mins[k]) * c->dp->strides[k]; + slot += agg_dense_component(c->dp, k, agg_read_key_i64(c->key_cols[k], c->key_data[k], r)) * c->dp->strides[k]; return slot; } +static inline bool agg_dense_first(agg_dense_local_t* loc, int64_t slot) { + uint64_t bit = UINT64_C(1) << (slot % 64); + uint64_t* word = &loc->occupied[slot / 64]; + if (*word & bit) return false; + *word |= bit; + return true; +} + /* Phase A: per-worker dense accumulate over chunk [start,end). */ static void agg_dense_phaseA_fn(void* vctx, uint32_t wid, int64_t start, int64_t end) { - agg_dense_ctx_t* c = (agg_dense_ctx_t*)vctx; + agg_dense_ctx_t* c = vctx; agg_dense_local_t* loc = &c->locals[wid]; if (loc->oom) return; int64_t n = end - start; @@ -1149,6 +1168,11 @@ static void agg_dense_phaseA_fn(void* vctx, uint32_t wid, int64_t start, int64_t int64_t r = rows[i]; int64_t slot = agg_dense_slot(c, r); /* provably in [0,total_slots) */ gid[i] = (uint32_t)slot; + if (agg_dense_first(loc, slot)) { + for (uint32_t a = 0; a < c->n_aggs; a++) + c->vts[a]->init(loc->states + (size_t)slot * c->block + c->off[a]); + loc->first_row[slot] = r; + } if (r < loc->first_row[slot]) loc->first_row[slot] = r; } if (agg_sel_accum_chunk(&c->vd, &sc, loc->states, gid, rows, cn) != 0) { @@ -1159,8 +1183,7 @@ static void agg_dense_phaseA_fn(void* vctx, uint32_t wid, int64_t start, int64_t return; } - uint32_t* cgid = ray_alloc_raw((size_t)n * sizeof(uint32_t)); - if (!cgid) { loc->oom = 1; return; } + uint32_t cgid[8192]; /* Hoist the per-row key-type dispatch out of the hot loop. agg_dense_slot * calls agg_read_key_i64 — a switch(col->type) — on every row, and for SYM @@ -1173,14 +1196,37 @@ static void agg_dense_phaseA_fn(void* vctx, uint32_t wid, int64_t start, int64_t const void* kd = c->key_data[0]; int64_t kmin = c->dp->mins[0]; int64_t kstride = c->dp->strides[0]; + bool nullable = c->dp->nullable[0]; + int64_t null = c->dp->nulls[0], null_slot = c->dp->ranges[0] - 1; int64_t* fr = loc->first_row; uint32_t* cg = cgid; - #define DENSE_SLOT1(LD) \ + #define DENSE_SLOT1_LAZY(LD) \ for (int64_t r = start; r < end; r++) { \ - int64_t slot = ((int64_t)(LD) - kmin) * kstride; \ + int64_t v = (int64_t)(LD); \ + int64_t slot = nullable && v == null ? null_slot \ + : (v - kmin) * kstride; \ cg[r - start] = (uint32_t)slot; \ - if (r < fr[slot]) fr[slot] = r; \ + if (agg_dense_first(loc, slot)) { \ + for (uint32_t a = 0; a < c->n_aggs; a++) \ + c->vts[a]->init(loc->states + (size_t)slot * c->block + c->off[a]); \ + fr[slot] = r; \ + } \ } + /* For domains whose initialization amortizes across task rows, + * avoid bitmap bookkeeping in the source-row loop. */ + #define DENSE_SLOT1_EAGER(LD, SLOT) \ + for (int64_t r = start; r < end; r++) { \ + int64_t v = (int64_t)(LD); \ + int64_t slot = (SLOT); \ + cg[r - start] = (uint32_t)slot; \ + if (r < fr[slot]) fr[slot] = r; \ + } + #define DENSE_SLOT1(LD) do { \ + if (loc->eager) { \ + if (!nullable) { DENSE_SLOT1_EAGER(LD, v - kmin); } \ + else { DENSE_SLOT1_EAGER(LD, v == null ? null_slot : v - kmin); } \ + } else { DENSE_SLOT1_LAZY(LD); } \ + } while (0) switch (c->key_cols[0]->type) { case RAY_I64: case RAY_TIMESTAMP: DENSE_SLOT1(((const int64_t*)kd)[r]); break; case RAY_I32: case RAY_DATE: case RAY_TIME: DENSE_SLOT1(((const int32_t*)kd)[r]); break; @@ -1202,79 +1248,739 @@ static void agg_dense_phaseA_fn(void* vctx, uint32_t wid, int64_t start, int64_t } } #undef DENSE_SLOT1 + #undef DENSE_SLOT1_LAZY + #undef DENSE_SLOT1_EAGER } else { for (int64_t r = start; r < end; r++) { int64_t slot = agg_dense_slot(c, r); /* provably in [0,total_slots) */ cgid[r - start] = (uint32_t)slot; - if (r < loc->first_row[slot]) loc->first_row[slot] = r; + if (agg_dense_first(loc, slot)) { + for (uint32_t a = 0; a < c->n_aggs; a++) + c->vts[a]->init(loc->states + (size_t)slot * c->block + c->off[a]); + loc->first_row[slot] = r; + } + } + } + + for (uint32_t a = 0; a < c->n_aggs; a++) { + if (c->vts[a]->update_batch2) { /* binary agg (pearson) */ + const void* vx = (const char*)c->val_data[a] + (size_t)start * c->val_esz[a]; + const void* vy = (const char*)c->val2_data[a] + (size_t)start * c->val2_esz[a]; + ray_valid_t valid_x = { vx, c->val_types[a], c->val_hasnull[a] }; + ray_valid_t valid_y = { vy, c->val2_types[a], c->val2_hasnull[a] }; + c->vts[a]->update_batch2(loc->states + c->off[a], c->block, cgid, + vx, vy, &valid_x, &valid_y, n, NULL); + } else { + const void* vals = (c->val_data[a]) + ? (const void*)((const char*)c->val_data[a] + (size_t)start * c->val_esz[a]) + : NULL; + ray_valid_t valid = { vals, c->val_types[a], + c->val_data[a] ? c->val_hasnull[a] : false }; + c->vts[a]->update_batch(loc->states + c->off[a], c->block, + cgid, vals, &valid, n, NULL); + } + } +} + +/* One slab per logical task, independent of the physical worker executing it. + * A large pool can run a memory-bounded number of dense tasks safely. */ +static void agg_dense_task_fn(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; + agg_dense_ctx_t* c = raw; + int64_t chunk = c->task_rows / c->n_tasks; + for (int64_t task = start; task < end; task++) { + agg_dense_local_t* loc = &c->locals[task]; + /* Storage was allocated once by the coordinator. Initialize each + * slab on its consuming task, without another pool dispatch. */ + agg_dense_local_init(loc, loc->slots, c->block); + if (loc->eager) for (int64_t s = 0; s < loc->slots; s++) { + loc->first_row[s] = INT64_MAX; + for (uint32_t a = 0; a < c->n_aggs; a++) + c->vts[a]->init(loc->states + (size_t)s * c->block + c->off[a]); + } + int64_t begin = chunk * task; + int64_t limit = task + 1 == c->n_tasks ? c->task_rows : chunk * (task + 1); + if (c->sel) agg_dense_phaseA_fn(c, (uint32_t)task, begin, limit); + else for (int64_t row = begin; row < limit && !agg_cancelled(); row += 8192) + agg_dense_phaseA_fn(c, (uint32_t)task, row, limit - row < 8192 ? limit : row + 8192); + if (loc->eager) for (int64_t s = 0; s < loc->slots; s++) + if (loc->first_row[s] != INT64_MAX) + loc->occupied[s / 64] |= UINT64_C(1) << (s % 64); + } +} + +/* Parallel dense path. Precondition: dp->ok, all aggs ACC_STREAMING, per-worker + * budget already gated by the caller. */ +typedef struct { + const dense_plan_t* plan; + const int64_t* occupied; + int64_t part_slots; + uint32_t bits; + ray_t* out; + uint32_t component; +} agg_dense_key_emit_t; +static void agg_dense_key_emit(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; + agg_dense_key_emit_t* c = raw; + void* data = ray_data(c->out); + for (int64_t r = start; r < end; r++) { + int64_t index = c->occupied[r]; + int64_t slot = c->bits ? ((index % c->part_slots) << c->bits) + index / c->part_slots : index; + uint32_t k = c->component; + int64_t component = (slot / c->plan->strides[k]) % c->plan->ranges[k]; + int64_t value = c->plan->nullable[k] && component == c->plan->ranges[k] - 1 + ? c->plan->nulls[k] : c->plan->mins[k] + component; + write_col_i64(data, r, value, c->out->type, c->out->attrs); + } +} + +/* Shared output stage. Takes ownership of states/first, borrows the + * prepared aggregate layout and descriptors. */ +static ray_t* agg_dense_finish(ray_t** key_cols, int64_t* key_syms, ray_op_ext_t* ext, + ray_pool_t* pool, const agg_vo_t* vo, const agg_desc_t* d, + int64_t total_slots, char* gstates, int64_t* gfirst, + const dense_plan_t* key_plan, int64_t key_part_slots, uint32_t key_bits) { + uint32_t n_keys = ext->n_keys, n_aggs = ext->n_aggs; + const agg_vtable_t** vts = vo->vts; + const size_t* off = vo->off; + size_t block = vo->block; + const int64_t* agg_syms = d->agg_syms; + /* ── Phase C: collect occupied slots in slot order, emit. ── + * Group-by output order is UNSPECIFIED, so we emit in dense-slot order + * (the natural build order) rather than sorting by first_row. gfirst[s] is + * still the gather index (any member row) for the key columns. */ + int64_t ng = 0; + for (int64_t s = 0; s < total_slots; s++) if (gfirst[s] != INT64_MAX) ng++; + ray_profile_tick("dense: freed slabs and counted groups"); + + int64_t* occupied_slot = ray_alloc_raw((size_t)(ng > 0 ? ng : 1) * sizeof(int64_t)); + int64_t* first_row_ordered = key_plan ? NULL : ray_alloc_raw((size_t)(ng > 0 ? ng : 1) * sizeof(int64_t)); + if (!occupied_slot || (!key_plan && !first_row_ordered)) { + ray_free_raw(occupied_slot); ray_free_raw(first_row_ordered); + agg_dense_slab_destroy_states(gstates, total_slots, vts, off, block, n_aggs); + ray_free_raw(gstates); ray_free_raw(gfirst); + return ray_error(agg_cancelled() ? "cancel" : "oom", NULL); + } + { int64_t i = 0; + for (int64_t s = 0; s < total_slots; s++) + if (gfirst[s] != INT64_MAX) { + if (first_row_ordered) first_row_ordered[i] = gfirst[s]; + occupied_slot[i] = s; i++; + } + } + + ray_t* result = ray_table_new(n_keys + n_aggs); + if (!result || RAY_IS_ERR(result)) { + agg_dense_slab_destroy_states(gstates, total_slots, vts, off, block, n_aggs); + ray_free_raw(occupied_slot); ray_free_raw(first_row_ordered); ray_free_raw(gstates); ray_free_raw(gfirst); + return result ? result : ray_error(agg_cancelled() ? "cancel" : "oom", NULL); + } + + for (uint32_t k = 0; k < n_keys; k++) { + ray_t* kc = key_plan ? col_vec_new(key_cols[k], ng) + : ray_group_gather(key_cols[k], first_row_ordered, ng); + if (!kc || RAY_IS_ERR(kc)) { + agg_dense_slab_destroy_states(gstates, total_slots, vts, off, block, n_aggs); + ray_free_raw(occupied_slot); ray_free_raw(first_row_ordered); ray_free_raw(gstates); ray_free_raw(gfirst); + ray_release(result); return kc ? kc : ray_error(agg_cancelled() ? "cancel" : "oom", NULL); + } + if (key_plan) { + kc->len = ng; + if (ray_vec_may_have_nulls(key_cols[k])) kc->attrs |= RAY_ATTR_HAS_NULLS; + if (kc->type == RAY_SYM) ray_sym_vec_adopt_domain(kc, sym_domain_rep(key_cols[k])); + agg_dense_key_emit_t emit = {key_plan, occupied_slot, key_part_slots, key_bits, kc, k}; + ray_pool_dispatch(pool, agg_dense_key_emit, &emit, ng); + } + result = ray_table_add_col(result, key_syms[k], kc); + ray_release(kc); + } + + for (uint32_t a = 0; a < n_aggs; a++) { + /* Buffered top_n/bot_n produce a LIST cell per group (a native vector); + * median and all streaming aggs produce a scalar out_type cell. */ + bool is_list = (vts[a]->out_type == RAY_LIST); + ray_t* out = is_list ? ray_list_new(ng) + : ray_vec_new(vts[a]->out_type, ng); + if (!out || RAY_IS_ERR(out)) { + agg_dense_slab_destroy_states(gstates, total_slots, vts, off, block, n_aggs); + ray_free_raw(occupied_slot); ray_free_raw(first_row_ordered); ray_free_raw(gstates); ray_free_raw(gfirst); + ray_release(result); return out ? out : ray_error(agg_cancelled() ? "cancel" : "oom", NULL); + } + out->len = ng; + int64_t kparam = (ext->agg_k ? ext->agg_k[a] : 0); + if (!is_list) { + agg_dense_emit_ctx_t emit = { .vt = vts[a], .out = out, .states = gstates, + .slots = occupied_slot, .block = block, .off = off[a], .param = kparam, .any_null = false }; + ray_pool_dispatch(pool, agg_dense_emit_fn, &emit, ng); + if (atomic_load_explicit(&emit.any_null, memory_order_relaxed)) out->attrs |= RAY_ATTR_HAS_NULLS; + } else { + for (int64_t i = 0; i < ng; i++) { + ray_t* cell = vts[a]->finalize(gstates + (size_t)occupied_slot[i] * block + off[a], NULL, kparam); + out = ray_list_set(out, i, cell); /* retains cell */ + ray_release(cell); + } + } + int64_t agg_name = agg_result_col_name(agg_syms[a], ext->agg_ops[a]); + result = ray_table_add_col(result, agg_name, out); + ray_release(out); + } + + /* Finalize is done reading the global slab → destroy its buffered per-group + * state exactly once before freeing the slab. */ + agg_dense_slab_destroy_states(gstates, total_slots, vts, off, block, n_aggs); + ray_free_raw(occupied_slot); ray_free_raw(first_row_ordered); ray_free_raw(gstates); ray_free_raw(gfirst); + if (result && !RAY_IS_ERR(result) && agg_cancelled()) { ray_release(result); return ray_error("cancel", NULL); } + return result; +} + +/* Key-owned partitions: each group is reduced exactly once. Source tasks + * histogram/scatter row IDs, then independently scheduled key partitions + * reduce those rows directly into disjoint output state. No state merge. */ +typedef struct { + uint32_t part; + uint64_t begin, end; + int64_t base; +} agg_dense_partition_task_t; + +typedef struct { + ray_t* key; + ray_t** keys; + const void** key_data; + ray_t* selection_indices; + const int64_t* selected_rows; + const dense_plan_t* plan; + agg_valdesc_t values; + int64_t rows, part_slots; + uint32_t sources, parts, bits, n_tasks; + agg_dense_partition_task_t* tasks; + uint64_t* counts; + uint64_t* starts; + uint32_t* gids; + char* payload; + size_t record_size; + size_t* value_offsets; + size_t* value2_offsets; + char* states; + int64_t* first; + _Atomic(bool) failed; +} agg_dense_partition_ctx_t; + +static void agg_dense_partition_count(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; + agg_dense_partition_ctx_t* c = raw; + const void* data = ray_data(c->key); + for (int64_t task = start; task < end; task++) { + int64_t begin = c->rows / c->sources * task; + int64_t limit = task + 1 == c->sources ? c->rows : c->rows / c->sources * (task + 1); + uint64_t* count = c->counts + task * c->parts; + if (c->plan->n_keys > 1) { + for (int64_t r = begin; r < limit; r++) { + int64_t source = c->selected_rows ? c->selected_rows[r] : r; + int64_t slot = 0; + for (uint32_t k = 0; k < c->plan->n_keys; k++) + slot += agg_dense_component(c->plan, k, agg_read_key_i64(c->keys[k], c->key_data[k], source)) * c->plan->strides[k]; + c->gids[r] = (uint32_t)slot; + count[slot & (c->parts - 1)]++; + } + continue; + } + #define PART_COUNT(T) do { \ + const T* p = data; \ + for (int64_t r = begin; r < limit; r++) { \ + uint32_t slot = (uint32_t)agg_dense_component(c->plan, 0, (int64_t)p[c->selected_rows ? c->selected_rows[r] : r]); \ + c->gids[r] = slot; \ + count[slot & (c->parts - 1)]++; \ + } \ + } while (0) + switch (c->key->type) { + case RAY_I64: case RAY_TIMESTAMP: PART_COUNT(int64_t); break; + case RAY_I32: case RAY_DATE: case RAY_TIME: PART_COUNT(int32_t); break; + case RAY_I16: PART_COUNT(int16_t); break; + case RAY_U8: case RAY_BOOL: PART_COUNT(uint8_t); break; + case RAY_SYM: + switch (c->key->attrs & RAY_SYM_W_MASK) { + case RAY_SYM_W8: PART_COUNT(uint8_t); break; + case RAY_SYM_W16: PART_COUNT(uint16_t); break; + case RAY_SYM_W32: PART_COUNT(uint32_t); break; + default: PART_COUNT(int64_t); break; + } + break; + } + #undef PART_COUNT + } +} + +/* Resolve native widths outside the row loop. A runtime-sized memcpy per + * value dominated scatter for eight-byte inputs and mixed aggregates. */ +static void agg_dense_partition_field(agg_dense_partition_ctx_t* c, const void* data, + uint8_t width, size_t field, const uint64_t* starts, int64_t begin, int64_t end) { + uint64_t cursor[RAY_POOL_INIT_TASKS / 2]; + assert(c->parts <= RAY_POOL_INIT_TASKS / 2); + memcpy(cursor, starts, c->parts * sizeof(*cursor)); + #define SCATTER_FIELD(W) do { \ + for (int64_t r = begin; r < end; r++) { \ + uint32_t part = c->gids[r] & (c->parts - 1); \ + memcpy(c->payload + cursor[part]++ * c->record_size + field, \ + (const char*)data + (size_t)(c->selected_rows ? c->selected_rows[r] : r) * (W), (W)); \ + } \ + } while (0) + switch (width) { + case 1: SCATTER_FIELD(1); break; + case 2: SCATTER_FIELD(2); break; + case 4: SCATTER_FIELD(4); break; + case 8: SCATTER_FIELD(8); break; + } + #undef SCATTER_FIELD +} + +static void agg_dense_partition_scatter(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; + agg_dense_partition_ctx_t* c = raw; + for (int64_t task = start; task < end; task++) { + int64_t begin = c->rows / c->sources * task; + int64_t limit = task + 1 == c->sources ? c->rows : c->rows / c->sources * (task + 1); + const uint64_t* starts = c->counts + task * c->parts; + uint64_t cursor[RAY_POOL_INIT_TASKS / 2]; + assert(c->parts <= RAY_POOL_INIT_TASKS / 2); + memcpy(cursor, starts, c->parts * sizeof(*cursor)); + const agg_valdesc_t* vd = &c->values; + if (vd->n_aggs == 1 && vd->val_data[0] && !vd->val2_data[0]) { + #define SCATTER_UNARY(W) do { \ + for (int64_t r = begin; r < limit; r++) { \ + uint32_t slot = c->gids[r]; \ + uint32_t gid = slot >> c->bits, part = slot & (c->parts - 1); \ + char* dst = c->payload + cursor[part]++ * (4 + (W)); \ + memcpy(dst, &gid, 4); \ + memcpy(dst + 4, (const char*)vd->val_data[0] + (size_t)(c->selected_rows ? c->selected_rows[r] : r) * (W), (W)); \ + } \ + } while (0) + switch (vd->val_esz[0]) { + case 1: SCATTER_UNARY(1); break; + case 2: SCATTER_UNARY(2); break; + case 4: SCATTER_UNARY(4); break; + case 8: SCATTER_UNARY(8); break; + } + #undef SCATTER_UNARY + } else { + /* Complete nearby records before advancing through a source + * range. Whole-column passes repeatedly read/write the same large + * output buffer when several input fields share each record. */ + int64_t chunk_rows = (64 * 1024) / c->record_size; + if (chunk_rows > AGG_SEL_CHUNK) chunk_rows = AGG_SEL_CHUNK; + if (chunk_rows < 1) chunk_rows = 1; + uint64_t chunk_starts[RAY_POOL_INIT_TASKS / 2]; + for (int64_t chunk = begin; chunk < limit; chunk += chunk_rows) { + int64_t chunk_end = limit - chunk < chunk_rows ? limit : chunk + chunk_rows; + memcpy(chunk_starts, cursor, c->parts * sizeof(*cursor)); + for (int64_t r = chunk; r < chunk_end; r++) { + uint32_t slot = c->gids[r], part = slot & (c->parts - 1), gid = slot >> c->bits; + char* dst = c->payload + cursor[part]++ * c->record_size; + memcpy(dst, &gid, 4); + } + size_t emitted = sizeof(uint32_t); + for (uint32_t a = 0; a < vd->n_aggs; a++) { + if (vd->val_data[a] && c->value_offsets[a] >= emitted) { + agg_dense_partition_field(c, vd->val_data[a], vd->val_esz[a], c->value_offsets[a], chunk_starts, chunk, chunk_end); + emitted = c->value_offsets[a] + vd->val_esz[a]; + } + if (vd->val2_data[a] && c->value2_offsets[a] >= emitted) { + agg_dense_partition_field(c, vd->val2_data[a], vd->val2_esz[a], c->value2_offsets[a], chunk_starts, chunk, chunk_end); + emitted = c->value2_offsets[a] + vd->val2_esz[a]; + } + } + } + } + } +} + +static void agg_dense_payload_gather(char* dst, const char* src, size_t stride, uint8_t width, int64_t n) { + #define PAYLOAD_GATHER(W) for (int64_t i = 0; i < n; i++) \ + memcpy(dst + (size_t)i * (W), src + (size_t)i * stride, (W)) + switch (width) { + case 1: PAYLOAD_GATHER(1); break; + case 2: PAYLOAD_GATHER(2); break; + case 4: PAYLOAD_GATHER(4); break; + case 8: PAYLOAD_GATHER(8); break; + default: for (int64_t i = 0; i < n; i++) memcpy(dst + (size_t)i * width, src + (size_t)i * stride, width); + } + #undef PAYLOAD_GATHER +} +static int agg_dense_payload_accum(const agg_dense_partition_ctx_t* c, agg_sel_scratch_t* sc, + char* states, const uint32_t* gids, uint64_t start, int64_t n) { + const agg_valdesc_t* vd = &c->values; + const char* records = c->payload + start * c->record_size; + for (uint32_t a = 0; a < vd->n_aggs; a++) { + if (vd->val_data[a]) { + if (!sc->gv[a]) sc->gv[a] = ray_alloc_raw((size_t)AGG_SEL_CHUNK * vd->val_esz[a]); + if (!sc->gv[a]) return -1; + agg_dense_payload_gather(sc->gv[a], records + c->value_offsets[a], c->record_size, vd->val_esz[a], n); + } + if (vd->val2_data[a]) { + if (!sc->gy[a]) sc->gy[a] = ray_alloc_raw((size_t)AGG_SEL_CHUNK * vd->val2_esz[a]); + if (!sc->gy[a]) return -1; + agg_dense_payload_gather(sc->gy[a], records + c->value2_offsets[a], c->record_size, vd->val2_esz[a], n); + } + ray_valid_t vx = {sc->gv[a], vd->val_types[a], vd->val_data[a] && vd->val_hasnull[a]}; + ray_valid_t vy = {sc->gy[a], vd->val2_types[a], vd->val2_hasnull[a]}; + if (vd->vts[a]->update_batch2) + vd->vts[a]->update_batch2(states + vd->off[a], vd->block, gids, sc->gv[a], sc->gy[a], &vx, &vy, n, NULL); + else vd->vts[a]->update_batch(states + vd->off[a], vd->block, gids, sc->gv[a], &vx, n, NULL); + } + return 0; +} + +static void agg_dense_partition_reduce(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; + agg_dense_partition_ctx_t* c = raw; + const agg_valdesc_t* vd = &c->values; + agg_sel_scratch_t scratch; + if (!agg_sel_scratch_init(&scratch, vd->n_aggs)) { + atomic_store_explicit(&c->failed, true, memory_order_relaxed); + return; + } + uint32_t gids[AGG_SEL_CHUNK]; + for (int64_t task = start; task < end && !agg_cancelled(); task++) { + const agg_dense_partition_task_t* work = &c->tasks[task]; + int64_t base = work->base; + char* states = c->states + (size_t)base * vd->block; + int64_t* first = c->first + base; + for (int64_t slot = 0; slot < c->part_slots; slot++) { + first[slot] = INT64_MAX; + for (uint32_t a = 0; a < vd->n_aggs; a++) + vd->vts[a]->init(states + (size_t)slot * vd->block + vd->off[a]); + } + for (uint64_t at = work->begin; at < work->end; at += AGG_SEL_CHUNK) { + int64_t n = work->end - at < AGG_SEL_CHUNK ? work->end - at : AGG_SEL_CHUNK; + for (int64_t i = 0; i < n; i++) { + uint32_t gid; + memcpy(&gid, c->payload + (at + i) * c->record_size, 4); + gids[i] = gid; + first[gid] = 0; /* key is reconstructed from the partition/slot */ + } + if (agg_dense_payload_accum(c, &scratch, states, gids, at, n) != 0) { + atomic_store_explicit(&c->failed, true, memory_order_relaxed); + break; + } + } + } + agg_sel_scratch_free(&scratch); +} + +static void agg_dense_partition_merge(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; + agg_dense_partition_ctx_t* c = raw; + const agg_valdesc_t* vd = &c->values; + for (int64_t part = start; part < end; part++) { + int64_t dst = part * c->part_slots; + for (uint32_t task = 0; task < c->n_tasks; task++) { + const agg_dense_partition_task_t* work = &c->tasks[task]; + if (work->part != part || work->base == dst) continue; + for (int64_t slot = 0; slot < c->part_slots; slot++) { + int64_t src = work->base + slot, target = dst + slot; + if (c->first[src] == INT64_MAX) continue; + if (c->first[src] < c->first[target]) c->first[target] = c->first[src]; + for (uint32_t a = 0; a < vd->n_aggs; a++) + vd->vts[a]->merge(c->states + (size_t)target * vd->block + vd->off[a], + c->states + (size_t)src * vd->block + vd->off[a], NULL); + } + } + } +} + +/* Pack each input column once, even when several unary/binary aggregates + * consume it. Planning and execution use the same native-byte layout. */ +static size_t agg_partition_record(const agg_desc_t* d, uint32_t n, + size_t* x_offsets, size_t* y_offsets) { + size_t bytes = sizeof(uint32_t); + for (uint32_t f = 0; f < 2 * n; f++) { + uint32_t a = f / 2; + const void* data = f % 2 ? d->val2_data[a] : d->val_data[a]; + uint8_t width = f % 2 ? d->val2_esz[a] : d->val_esz[a]; + size_t offset = bytes; + bool found = false; + for (uint32_t prev = 0; data && prev < f; prev++) { + uint32_t b = prev / 2; + const void* other = prev % 2 ? d->val2_data[b] : d->val_data[b]; + uint8_t size = prev % 2 ? d->val2_esz[b] : d->val_esz[b]; + if (other == data && size == width) { + if (x_offsets) offset = prev % 2 ? y_offsets[b] : x_offsets[b]; + found = true; break; + } + } + if (data && !found) bytes += width; + if (x_offsets) (f % 2 ? y_offsets : x_offsets)[a] = offset; + } + return bytes; +} + +static uint32_t agg_dense_partition_parts(uint32_t sources, int64_t slots) { + /* Leave room for skew subtasks; dispatch_n must never truncate work. */ + _Static_assert(RAY_POOL_INIT_TASKS >= 2 && + (RAY_POOL_INIT_TASKS & (RAY_POOL_INIT_TASKS - 1)) == 0, + "dense partition cursor capacity must be a power of two"); + uint32_t parts = 1; + while (parts < RAY_POOL_INIT_TASKS / 2 && + (parts < sources * 4 || (int64_t)parts * 32768 < slots)) parts *= 2; + return parts; +} + +static ray_t* agg_dense_partitioned(ray_t** key_cols, int64_t* key_syms, ray_op_ext_t* ext, + ray_pool_t* pool, const dense_plan_t* plan, int64_t rows, + const agg_vo_t* vo, const agg_desc_t* d, ray_t* selection) { + uint32_t workers = ray_pool_total_workers(pool); + uint32_t sources = workers; + if (sources > RAY_POOL_INIT_TASKS / 2) sources = RAY_POOL_INIT_TASKS / 2; + uint32_t parts = agg_dense_partition_parts(workers, plan->total_slots); + uint32_t bits = (uint32_t)__builtin_ctz(parts); + uint32_t split_budget = sources * 4 < parts ? sources * 4 : parts; + int64_t part_slots = ((plan->total_slots + parts - 1) / parts + 7) & ~INT64_C(7); + int64_t slots = part_slots * parts; + agg_dense_partition_ctx_t c = { + .key = key_cols[0], .keys = key_cols, .key_data = d->key_data, + .plan = plan, .rows = rows, .part_slots = part_slots, + .sources = sources, .parts = parts, .bits = bits, .failed = false, + .values = { .n_aggs = ext->n_aggs, .vts = vo->vts, .off = vo->off, .block = vo->block, + .val_data = d->val_data, .val_types = d->val_types, .val_hasnull = d->val_hasnull, .val_esz = d->val_esz, + .val2_data = d->val2_data, .val2_types = d->val2_types, .val2_hasnull = d->val2_hasnull, .val2_esz = d->val2_esz } + }; + if (selection) { + c.selection_indices = ray_rowsel_to_indices(selection); + if (!c.selection_indices || RAY_IS_ERR(c.selection_indices)) goto failed; + c.selected_rows = ray_data(c.selection_indices); + } + c.counts = ray_calloc_raw(((size_t)sources * parts + parts + 1) * sizeof(uint64_t)); + c.gids = ray_alloc_raw((size_t)rows * sizeof(uint32_t)); + c.value_offsets = ray_alloc_raw((size_t)ext->n_aggs * 2 * sizeof(size_t)); + if (!c.value_offsets) goto failed; + c.value2_offsets = c.value_offsets + ext->n_aggs; + c.record_size = agg_partition_record(d, ext->n_aggs, c.value_offsets, c.value2_offsets); + c.payload = ray_alloc_raw((size_t)rows * c.record_size); + c.tasks = ray_alloc_raw((size_t)(parts + split_budget) * sizeof(*c.tasks)); + if (!c.counts || !c.gids || !c.tasks || !c.payload) goto failed; + c.starts = c.counts + (size_t)sources * parts; + ray_pool_dispatch_n(pool, agg_dense_partition_count, &c, sources); + if (agg_cancelled()) goto failed; + uint64_t offset = 0; + for (uint32_t part = 0; part < parts; part++) { + c.starts[part] = offset; + for (uint32_t task = 0; task < sources; task++) { + size_t at = (size_t)task * parts + part; + uint64_t n = c.counts[at]; c.counts[at] = offset; offset += n; + } + } + c.starts[parts] = offset; + uint64_t grain = ((uint64_t)rows + split_budget - 1) / split_budget; + int64_t extra_slots = 0; + for (uint32_t part = 0; part < parts; part++) { + uint64_t begin = c.starts[part], end = c.starts[part + 1]; + /* Even empty partitions own initialized output slots. */ + do { + uint64_t limit = end - begin < grain ? end : begin + grain; + int64_t base = begin == c.starts[part] ? (int64_t)part * part_slots : slots + extra_slots; + if (base >= slots) extra_slots += part_slots; + c.tasks[c.n_tasks++] = (agg_dense_partition_task_t){part, begin, limit, base}; + begin = limit; + } while (begin < end); + } + c.states = ray_alloc_raw((size_t)(slots + extra_slots) * vo->block); + c.first = ray_alloc_raw((size_t)(slots + extra_slots) * sizeof(int64_t)); + if (!c.states || !c.first) goto failed; + ray_profile_tick("dense partition: histogram"); + ray_pool_dispatch_n(pool, agg_dense_partition_scatter, &c, sources); + if (agg_cancelled()) goto failed; + ray_profile_tick("dense partition: scattered rows"); + ray_pool_dispatch_n(pool, agg_dense_partition_reduce, &c, c.n_tasks); + if (agg_cancelled() || atomic_load_explicit(&c.failed, memory_order_relaxed)) goto failed; + ray_profile_tick("dense partition: reduced groups"); + ray_pool_dispatch_n(pool, agg_dense_partition_merge, &c, parts); + if (agg_cancelled()) goto failed; + ray_profile_tick("dense partition: merged split partitions"); + ray_free_raw(c.counts); ray_free_raw(c.gids); ray_free_raw(c.tasks); + ray_free_raw(c.payload); ray_free_raw(c.value_offsets); ray_release(c.selection_indices); + route_stats.dense_strategy = AGG_DENSE_PARTITIONED; + route_stats.dense_tasks = c.n_tasks; + route_stats.dense_local_slots = slots + extra_slots; + return agg_dense_finish(key_cols, key_syms, ext, pool, vo, d, slots, c.states, c.first, plan, part_slots, bits); +failed: + ray_free_raw(c.counts); ray_free_raw(c.gids); ray_free_raw(c.tasks); + ray_free_raw(c.payload); ray_free_raw(c.value_offsets); ray_release(c.selection_indices); ray_free_raw(c.states); ray_free_raw(c.first); + return ray_error(agg_cancelled() ? "cancel" : "oom", NULL); +} + +typedef struct { + ray_t* key; + const dense_plan_t* plan; + const agg_vo_t* layout; + const agg_desc_t* desc; + uint32_t n_aggs; + char* states; + _Atomic(uint64_t)* occupied; + int64_t* first; +} agg_dense_shared_ctx_t; + +static void agg_dense_shared_init(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; + agg_dense_shared_ctx_t* c = raw; + const agg_vo_t* vo = c->layout; + for (int64_t s = start; s < end; s++) + for (uint32_t a = 0; a < c->n_aggs; a++) + vo->vts[a]->init(c->states + (size_t)s * vo->block + vo->off[a]); +} + +static void agg_dense_shared_rows(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; + agg_dense_shared_ctx_t* c = raw; + const agg_vo_t* vo = c->layout; + const agg_desc_t* d = c->desc; + const void* data = ray_data(c->key); + uint32_t gids[8192]; + for (int64_t begin = start; begin < end && !agg_cancelled(); begin += 8192) { + int64_t n = end - begin < 8192 ? end - begin : 8192; + #define SHARED_GIDS(T) do { \ + const T* p = data; \ + for (int64_t i = 0; i < n; i++) { \ + uint32_t slot = (uint32_t)agg_dense_component(c->plan, 0, (int64_t)p[begin + i]); \ + gids[i] = slot; \ + uint64_t bit = UINT64_C(1) << (slot % 64); \ + _Atomic(uint64_t)* word = &c->occupied[slot / 64]; \ + if (!(atomic_load_explicit(word, memory_order_relaxed) & bit)) \ + atomic_fetch_or_explicit(word, bit, memory_order_relaxed); \ + } \ + } while (0) + switch (c->key->type) { + case RAY_I64: case RAY_TIMESTAMP: SHARED_GIDS(int64_t); break; + case RAY_I32: case RAY_DATE: case RAY_TIME: SHARED_GIDS(int32_t); break; + case RAY_I16: SHARED_GIDS(int16_t); break; + case RAY_U8: case RAY_BOOL: SHARED_GIDS(uint8_t); break; + case RAY_SYM: + switch (c->key->attrs & RAY_SYM_W_MASK) { + case RAY_SYM_W8: SHARED_GIDS(uint8_t); break; + case RAY_SYM_W16: SHARED_GIDS(uint16_t); break; + case RAY_SYM_W32: SHARED_GIDS(uint32_t); break; + default: SHARED_GIDS(int64_t); break; + } + break; } - } - - for (uint32_t a = 0; a < c->n_aggs; a++) { - if (c->vts[a]->update_batch2) { /* binary agg (pearson) */ - const void* vx = (const char*)c->val_data[a] + (size_t)start * c->val_esz[a]; - const void* vy = (const char*)c->val2_data[a] + (size_t)start * c->val2_esz[a]; - ray_valid_t valid_x = { vx, c->val_types[a], c->val_hasnull[a] }; - ray_valid_t valid_y = { vy, c->val2_types[a], c->val2_hasnull[a] }; - c->vts[a]->update_batch2(loc->states + c->off[a], c->block, cgid, - vx, vy, &valid_x, &valid_y, n, NULL); - } else { - const void* vals = (c->val_data[a]) - ? (const void*)((const char*)c->val_data[a] + (size_t)start * c->val_esz[a]) - : NULL; - ray_valid_t valid = { vals, c->val_types[a], - c->val_data[a] ? c->val_hasnull[a] : false }; - c->vts[a]->update_batch(loc->states + c->off[a], c->block, - cgid, vals, &valid, n, NULL); + #undef SHARED_GIDS + for (uint32_t a = 0; a < c->n_aggs; a++) { + const void* vals = (const char*)d->val_data[a] + (size_t)begin * d->val_esz[a]; + ray_valid_t valid = {vals, d->val_types[a], d->val_hasnull[a]}; + vo->vts[a]->update_shared(c->states + vo->off[a], vo->block, gids, vals, &valid, n); } } - ray_free_raw(cgid); } -/* Parallel dense path. Precondition: dp->ok, all aggs ACC_STREAMING, per-worker - * budget already gated by the caller. */ +static void agg_dense_shared_occupied(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; + agg_dense_shared_ctx_t* c = raw; + for (int64_t s = start; s < end; s++) + c->first[s] = atomic_load_explicit(&c->occupied[s / 64], memory_order_relaxed) + & (UINT64_C(1) << (s % 64)) ? 0 : INT64_MAX; +} + +static ray_t* agg_dense_shared(ray_t** key_cols, int64_t* key_syms, ray_op_ext_t* ext, + ray_pool_t* pool, const dense_plan_t* plan, int64_t rows, + const agg_vo_t* vo, const agg_desc_t* d) { + int64_t slots = plan->total_slots; + agg_dense_shared_ctx_t c = {.key = key_cols[0], .plan = plan, .layout = vo, .desc = d, .n_aggs = ext->n_aggs}; + c.states = ray_alloc_raw((size_t)slots * vo->block); + c.first = ray_alloc_raw((size_t)slots * sizeof(int64_t)); + c.occupied = ray_alloc_raw(((size_t)slots + 63) / 64 * sizeof(*c.occupied)); + if (!c.states || !c.first || !c.occupied) goto failed; + for (int64_t w = 0; w < (slots + 63) / 64; w++) atomic_init(&c.occupied[w], 0); + ray_pool_dispatch(pool, agg_dense_shared_init, &c, slots); + ray_profile_tick("dense shared: initialized states"); + ray_pool_dispatch(pool, agg_dense_shared_rows, &c, rows); + if (agg_cancelled()) goto failed; + ray_profile_tick("dense shared: reduced rows"); + ray_pool_dispatch(pool, agg_dense_shared_occupied, &c, slots); + if (agg_cancelled()) goto failed; + ray_profile_tick("dense shared: collected occupancy"); + ray_free_raw(c.occupied); + route_stats.dense_strategy = AGG_DENSE_SHARED; + route_stats.dense_tasks = ray_pool_total_workers(pool); + route_stats.dense_local_slots = slots; + return agg_dense_finish(key_cols, key_syms, ext, pool, vo, d, slots, c.states, c.first, plan, slots, 0); +failed: + ray_free_raw(c.states); ray_free_raw(c.first); ray_free_raw(c.occupied); + return ray_error(agg_cancelled() ? "cancel" : "oom", NULL); +} + static ray_t* exec_group_v2_parallel_dense( ray_graph_t* g, ray_op_t* op, ray_t* tbl, ray_t** key_cols, int64_t* key_syms, ray_op_ext_t* ext, int64_t nrows, - ray_pool_t* pool, const dense_plan_t* dp, + ray_pool_t* pool, const dense_plan_t* dp, uint32_t nw, agg_dense_strategy_t strategy, ray_t* sel, const int64_t* sel_prefix, int64_t n_sel) { uint32_t n_keys = ext->n_keys, n_aggs = ext->n_aggs; int64_t total_slots = dp->total_slots; - uint32_t nw = ray_pool_total_workers(pool); /* Re-derive the AoS layout (same order as exec_group_v2). */ agg_vo_t vo; - if (!agg_vo_init(&vo, g, ext, tbl)) return ray_error("oom", NULL); + if (!agg_vo_init(&vo, g, ext, tbl)) return ray_error(agg_cancelled() ? "cancel" : "oom", NULL); const agg_vtable_t** vts = vo.vts; size_t* off = vo.off; size_t block = vo.block; agg_desc_t d; - if (!agg_desc_init(&d, g, ext, tbl, key_cols)) { agg_vo_free(&vo); return ray_error("oom", NULL); } + if (!agg_desc_init(&d, g, ext, tbl, key_cols)) { agg_vo_free(&vo); return ray_error(agg_cancelled() ? "cancel" : "oom", NULL); } const void** key_data = d.key_data; const void** val_data = d.val_data; const int8_t* val_types = d.val_types; const bool* val_hasnull = d.val_hasnull; const uint8_t* val_esz = d.val_esz; const void** val2_data = d.val2_data; const int8_t* val2_types = d.val2_types; const bool* val2_hasnull = d.val2_hasnull; const uint8_t* val2_esz = d.val2_esz; - const int64_t* agg_syms = d.agg_syms; - agg_dense_local_t* locals = ray_calloc_raw((size_t)((size_t)nw) * (sizeof(agg_dense_local_t))); - if (!locals) { agg_vo_free(&vo); agg_desc_free(&d); return ray_error("oom", NULL); } - uint32_t n_init = 0; /* slabs whose slots were fully init'd (destroy-safe) */ - int alloc_oom = 0; - for (uint32_t w = 0; w < nw; w++) { - if (agg_dense_local_init(&locals[w], total_slots, vts, off, block, n_aggs) != 0) { alloc_oom = 1; break; } - n_init = w + 1; + if (strategy == AGG_DENSE_SHARED) { + ray_t* result = agg_dense_shared(key_cols, key_syms, ext, pool, dp, nrows, &vo, &d); + agg_vo_free(&vo); agg_desc_free(&d); + return result; } - if (alloc_oom) { - /* Only the fully-init'd slabs carry valid (destroyable) buffered state; - * the slab that failed init never ran its per-slot init → its bytes are - * uninitialized and must NOT be destroy'd (would free a garbage ptr). */ - for (uint32_t w = 0; w < n_init; w++) - agg_dense_slab_destroy_states(locals[w].states, total_slots, vts, off, block, n_aggs); - for (uint32_t w = 0; w < nw; w++) agg_dense_local_destroy(&locals[w]); - ray_free_raw(locals); + if (strategy == AGG_DENSE_PARTITIONED) { + ray_t* result = agg_dense_partitioned(key_cols, key_syms, ext, pool, dp, sel ? n_sel : nrows, &vo, &d, sel); agg_vo_free(&vo); agg_desc_free(&d); - return ray_error("oom", NULL); + return result; + } + route_stats.dense_strategy = AGG_DENSE_TASK_LOCAL; + size_t metadata_bytes = ((size_t)nw * sizeof(agg_dense_local_t) + 63) & ~(size_t)63; + size_t local_bytes = metadata_bytes + 63; + for (uint32_t w = 0; w < nw; w++) { + size_t slots = total_slots; + size_t bytes = slots * (block + sizeof(int64_t)) + (slots + 63) / 64 * sizeof(uint64_t); + local_bytes += (bytes + 63) & ~(size_t)63; + } + /* One query-owned allocation avoids concurrent allocator contention and + * gives repeated queries one stable scratch size to reuse. Task slices are + * cache-line aligned and written only by their owning task. */ + agg_dense_local_t* locals = ray_alloc_raw(local_bytes); + if (!locals) { agg_vo_free(&vo); agg_desc_free(&d); return ray_error(agg_cancelled() ? "cancel" : "oom", NULL); } + memset(locals, 0, metadata_bytes); + char* cursor = (char*)(((uintptr_t)locals + metadata_bytes + 63) & ~(uintptr_t)63); + for (uint32_t w = 0; w < nw; w++) { + locals[w].slots = total_slots; + /* Eager initialization amortizes when a task processes at least + * two rows per possible slot. It preserves a tight row loop for + * densely occupied domains; sparse worker slices remain lazy. */ + locals[w].eager = !sel && n_keys == 1 && total_slots <= nrows / nw / 2; + if (locals[w].eager && total_slots > 1) { + /* A clustered source range may visit only a small part of the + * global domain. Sampling chooses initialization work only; + * every actual row still initializes its state on the lazy path. */ + int64_t begin = nrows / nw * w; + int64_t end = w + 1 == nw ? nrows : nrows / nw * (w + 1); + int64_t low = total_slots, high = 0; + for (int64_t sample = 0; sample < 64; sample++) { + int64_t row = begin + (end - begin - 1) * sample / 63; + int64_t slot = agg_dense_component(dp, 0, agg_read_key_i64(key_cols[0], key_data[0], row)); + if (slot < low) low = slot; + if (slot > high) high = slot; + } + locals[w].eager = high - low + 1 >= total_slots - total_slots / 4; + } + size_t slots = locals[w].slots; + size_t bytes = slots * (block + sizeof(int64_t)) + (slots + 63) / 64 * sizeof(uint64_t); + locals[w].states = cursor; + cursor += (bytes + 63) & ~(size_t)63; } + agg_dense_merge_ctx_t merge_ctx = { .locals = locals, .slots = total_slots, + .vts = vts, .off = off, .block = block, .n_aggs = n_aggs, .nw = nw }; + ray_profile_tick("dense: allocated slabs"); agg_dense_ctx_t ctx = { .key_cols = key_cols, .key_data = key_data, .n_keys = n_keys, .dp = dp, @@ -1282,6 +1988,7 @@ static ray_t* exec_group_v2_parallel_dense( .val_data = val_data, .val_types = val_types, .val_hasnull = val_hasnull, .val_esz = val_esz, .val2_data = val2_data, .val2_types = val2_types, .val2_hasnull = val2_hasnull, .val2_esz = val2_esz, .locals = locals, + .task_rows = sel ? n_sel : nrows, .n_tasks = nw, .sel = sel, .sel_prefix = sel_prefix, .vd = { .n_aggs = n_aggs, .vts = vts, .off = off, .block = block, .val_data = val_data, .val_types = val_types, .val_hasnull = val_hasnull, .val_esz = val_esz, @@ -1289,127 +1996,53 @@ static ray_t* exec_group_v2_parallel_dense( }; /* Sel mode dispatches over the SELECTED-row space [0,n_sel); each worker * range maps to a segment span via sel_prefix. Non-sel dispatches over rows. */ - ray_pool_dispatch(pool, agg_dense_phaseA_fn, &ctx, sel ? n_sel : nrows); + ray_pool_dispatch_n(pool, agg_dense_task_fn, &ctx, nw); + ray_profile_tick("dense: accumulated rows"); for (uint32_t w = 0; w < nw; w++) - if (locals[w].oom) { + if (locals[w].oom || agg_cancelled()) { for (uint32_t i = 0; i < nw; i++) - agg_dense_slab_destroy_states(locals[i].states, total_slots, vts, off, block, n_aggs); - for (uint32_t i = 0; i < nw; i++) agg_dense_local_destroy(&locals[i]); + agg_dense_local_destroy_states(&locals[i], vts, off, block, n_aggs); ray_free_raw(locals); agg_vo_free(&vo); agg_desc_free(&d); - return ray_error("oom", NULL); + return ray_error(agg_cancelled() ? "cancel" : "oom", NULL); } - /* ── Phase B: merge per-worker slabs into a global slab (serial) ── */ + /* dispatch_n has joined every task. If cancellation skipped a task, the + * check above returns before reading its bitmap; destroy also checks ready. + * Cancellation after this point cannot uninitialize a completed slab. */ + /* ── Phase B: merge task slabs into a global slab in parallel ── */ char* gstates = ray_alloc_raw((size_t)total_slots * block); int64_t* gfirst = ray_alloc_raw((size_t)total_slots * sizeof(int64_t)); if (!gstates || !gfirst) { ray_free_raw(gstates); ray_free_raw(gfirst); for (uint32_t i = 0; i < nw; i++) - agg_dense_slab_destroy_states(locals[i].states, total_slots, vts, off, block, n_aggs); - for (uint32_t i = 0; i < nw; i++) agg_dense_local_destroy(&locals[i]); + agg_dense_local_destroy_states(&locals[i], vts, off, block, n_aggs); ray_free_raw(locals); agg_vo_free(&vo); agg_desc_free(&d); - return ray_error("oom", NULL); - } - for (int64_t s = 0; s < total_slots; s++) { - gfirst[s] = INT64_MAX; - for (uint32_t a = 0; a < n_aggs; a++) - vts[a]->init(gstates + (size_t)s * block + off[a]); + return ray_error(agg_cancelled() ? "cancel" : "oom", NULL); } - for (uint32_t w = 0; w < nw; w++) { - agg_dense_local_t* loc = &locals[w]; - for (int64_t s = 0; s < total_slots; s++) { - if (loc->first_row[s] == INT64_MAX) continue; /* untouched by this worker */ - if (loc->first_row[s] < gfirst[s]) gfirst[s] = loc->first_row[s]; - for (uint32_t a = 0; a < n_aggs; a++) - vts[a]->merge(gstates + (size_t)s * block + off[a], - loc->states + (size_t)s * block + off[a], NULL); + merge_ctx.states = gstates; merge_ctx.first = gfirst; + ray_pool_dispatch(pool, agg_dense_merge_fn, &merge_ctx, total_slots); + ray_profile_tick("dense: merged slabs"); + if (agg_cancelled()) { + for (uint32_t i = 0; i < nw; i++) { + agg_dense_local_destroy_states(&locals[i], vts, off, block, n_aggs); } + /* A cancelled merge may leave global slots uninitialized. Streaming + * vtables own no separately allocated global state. */ + ray_free_raw(locals); ray_free_raw(gstates); ray_free_raw(gfirst); + agg_vo_free(&vo); agg_desc_free(&d); return ray_error("cancel", NULL); } /* Worker buffered state has been merged into the global slab → its per-group * buffers are now redundant. Destroy (free) them exactly once before * releasing the worker slabs. No-op for all-streaming. */ for (uint32_t i = 0; i < nw; i++) - agg_dense_slab_destroy_states(locals[i].states, total_slots, vts, off, block, n_aggs); - for (uint32_t i = 0; i < nw; i++) agg_dense_local_destroy(&locals[i]); + agg_dense_local_destroy_states(&locals[i], vts, off, block, n_aggs); ray_free_raw(locals); - /* ── Phase C: collect occupied slots in slot order, emit. ── - * Group-by output order is UNSPECIFIED, so we emit in dense-slot order - * (the natural build order) rather than sorting by first_row. gfirst[s] is - * still the gather index (any member row) for the key columns. */ - int64_t ng = 0; - for (int64_t s = 0; s < total_slots; s++) if (gfirst[s] != INT64_MAX) ng++; - - int64_t* occupied_slot = ray_alloc_raw((size_t)(ng > 0 ? ng : 1) * sizeof(int64_t)); - int64_t* first_row_ordered = ray_alloc_raw((size_t)(ng > 0 ? ng : 1) * sizeof(int64_t)); - if (!occupied_slot || !first_row_ordered) { - ray_free_raw(occupied_slot); ray_free_raw(first_row_ordered); - agg_dense_slab_destroy_states(gstates, total_slots, vts, off, block, n_aggs); - ray_free_raw(gstates); ray_free_raw(gfirst); - agg_vo_free(&vo); agg_desc_free(&d); - return ray_error("oom", NULL); - } - { int64_t i = 0; - for (int64_t s = 0; s < total_slots; s++) - if (gfirst[s] != INT64_MAX) { first_row_ordered[i] = gfirst[s]; occupied_slot[i] = s; i++; } - } - - ray_t* result = ray_table_new(n_keys + n_aggs); - if (!result || RAY_IS_ERR(result)) { - agg_dense_slab_destroy_states(gstates, total_slots, vts, off, block, n_aggs); - ray_free_raw(occupied_slot); ray_free_raw(first_row_ordered); ray_free_raw(gstates); ray_free_raw(gfirst); - agg_vo_free(&vo); agg_desc_free(&d); - return result ? result : ray_error("oom", NULL); - } - - for (uint32_t k = 0; k < n_keys; k++) { - ray_t* kc = agg_gather_key_col(key_cols[k], first_row_ordered, ng); - if (!kc || RAY_IS_ERR(kc)) { - agg_dense_slab_destroy_states(gstates, total_slots, vts, off, block, n_aggs); - ray_free_raw(occupied_slot); ray_free_raw(first_row_ordered); ray_free_raw(gstates); ray_free_raw(gfirst); - agg_vo_free(&vo); agg_desc_free(&d); - ray_release(result); return kc ? kc : ray_error("oom", NULL); - } - result = ray_table_add_col(result, key_syms[k], kc); - ray_release(kc); - } - - for (uint32_t a = 0; a < n_aggs; a++) { - /* Buffered top_n/bot_n produce a LIST cell per group (a native vector); - * median and all streaming aggs produce a scalar out_type cell. */ - bool is_list = (vts[a]->out_type == RAY_LIST); - ray_t* out = is_list ? ray_list_new(ng) - : ray_vec_new(vts[a]->out_type, ng); - if (!out || RAY_IS_ERR(out)) { - agg_dense_slab_destroy_states(gstates, total_slots, vts, off, block, n_aggs); - ray_free_raw(occupied_slot); ray_free_raw(first_row_ordered); ray_free_raw(gstates); ray_free_raw(gfirst); - agg_vo_free(&vo); agg_desc_free(&d); - ray_release(result); return out ? out : ray_error("oom", NULL); - } - out->len = ng; - int64_t kparam = (ext->agg_k ? ext->agg_k[a] : 0); - for (int64_t i = 0; i < ng; i++) { - ray_t* cell = vts[a]->finalize(gstates + (size_t)occupied_slot[i] * block + off[a], NULL, kparam); - if (is_list) { - out = ray_list_set(out, i, cell); /* retains cell */ - ray_release(cell); /* drop our local ref */ - } else { - agg_put_cell(out, i, cell); - ray_release(cell); - } - } - int64_t agg_name = agg_result_col_name(agg_syms[a], ext->agg_ops[a]); - result = ray_table_add_col(result, agg_name, out); - ray_release(out); - } - - /* Finalize is done reading the global slab → destroy its buffered per-group - * state exactly once before freeing the slab. */ - agg_dense_slab_destroy_states(gstates, total_slots, vts, off, block, n_aggs); - ray_free_raw(occupied_slot); ray_free_raw(first_row_ordered); ray_free_raw(gstates); ray_free_raw(gfirst); + ray_t* result = agg_dense_finish(key_cols, key_syms, ext, pool, &vo, &d, + total_slots, gstates, gfirst, NULL, 0, 0); agg_vo_free(&vo); agg_desc_free(&d); return result; } @@ -1629,6 +2262,7 @@ static ray_t* exec_group_v2_parallel_smallhash( ray_t** key_cols, int64_t* key_syms, const agg_vtable_t** vts, const size_t* off, size_t block, ray_t* sel, const int64_t* sel_prefix, int64_t n_sel) { + agg_route_record(AGG_ROUTE_V2_SMALLHASH); ray_op_ext_t* ext = find_ext(g, op->id); uint32_t n_keys = ext->n_keys, n_aggs = ext->n_aggs; ray_pool_t* pool = ray_pool_get(); @@ -1699,7 +2333,7 @@ static ray_t* exec_group_v2_parallel_smallhash( if (gg < 0) { agg_sh_destroy(>); for (uint32_t i = 0; i < nw; i++) agg_sh_destroy(&locals[i]); - ray_free_raw(locals); + ray_free_raw(locals); agg_desc_free(&d); return ray_error("oom", NULL); } @@ -1721,7 +2355,7 @@ static ray_t* exec_group_v2_parallel_smallhash( return result ? result : ray_error("oom", NULL); } for (uint32_t k = 0; k < n_keys; k++) { - ray_t* kc = agg_gather_key_col(key_cols[k], gt.first_row, ng); + ray_t* kc = ray_group_gather(key_cols[k], gt.first_row, ng); if (!kc || RAY_IS_ERR(kc)) { agg_sh_destroy(>); agg_desc_free(&d); ray_release(result); return kc ? kc : ray_error("oom", NULL); @@ -1738,9 +2372,8 @@ static ray_t* exec_group_v2_parallel_smallhash( out->len = ng; int64_t kparam = (ext->agg_k ? ext->agg_k[a] : 0); for (int64_t i = 0; i < ng; i++) { - ray_t* cell = vts[a]->finalize(gt.states + (size_t)i * block + off[a], NULL, kparam); - agg_put_cell(out, i, cell); - ray_release(cell); + if (agg_finalize_value(vts[a], gt.states + (size_t)i * block + off[a], out, i, kparam)) + out->attrs |= RAY_ATTR_HAS_NULLS; } int64_t agg_name = agg_result_col_name(agg_syms[a], ext->agg_ops[a]); result = ray_table_add_col(result, agg_name, out); @@ -1854,10 +2487,10 @@ static void agg_radix_parts_destroy(agg_radix_part_t* parts, uint32_t nparts, * * agg_read_key_i64 widened each key into int64 (sign-extend for signed types, * zero-extend for U8/BOOL/SYM intern ids); write_col_i64 is its exact inverse, - * so the round-trip stores byte-identical payload to what agg_gather_key_col's + * so the round-trip stores byte-identical payload to what ray_group_gather's * raw memcpy of the original column produced — for SYM it routes through * ray_write_sym at the matching width, and the domain is adopted from src_col - * just like agg_gather_key_col. Caller owns the returned column. + * just like ray_group_gather. Caller owns the returned column. * Keys are admitted only as fixed-width integer/temporal/SYM columns * (agg_v2_can_handle) — there is no STR/variable-width emit path here — but the * key COUNT is unbounded on the radix strategy (only dense direct-index routing @@ -2002,7 +2635,7 @@ static inline int agg_radix_scatter_one(agg_radix_ctx_t* c, agg_pay_buf_t* my, } } if (c->needs_row) - *(int64_t*)(rec + c->row_off) = input_order; + memcpy(rec + c->row_off, &input_order, sizeof(input_order)); return 0; } @@ -2129,7 +2762,8 @@ static void agg_radix_group_fn(void* vctx, uint32_t wid, int64_t start, int64_t const char* rec = b->buf; for (uint32_t i = 0; i < b->n; i++, rec += c->rec) { const int64_t* keys = (const int64_t*)rec; - int64_t r = c->needs_row ? *(const int64_t*)(rec + c->row_off) : 0; + int64_t r = 0; + if (c->needs_row) memcpy(&r, rec + c->row_off, sizeof(r)); /* Gather this row's agg values into the dense per-agg buffers * (sequential record read → sequential dense write). */ for (uint32_t a = 0; a < n_aggs; a++) { @@ -2245,7 +2879,7 @@ static void agg_radix_group_fn(void* vctx, uint32_t wid, int64_t start, int64_t * - Each worker writes disjoint output element ranges → no payload contention. * - Per-group buffered states are read (finalize) here; the buffered destroy * stays SERIAL afterward (agg_radix_parts_destroy) → exactly-once, no race. - * - agg_put_cell would OR RAY_ATTR_HAS_NULLS into the SHARED out->attrs (a + * - a serial emitter would OR RAY_ATTR_HAS_NULLS into the SHARED out->attrs (a * racy read-modify-write). So the worker writes the null sentinel directly * (disjoint idx, safe) and records a per-worker "saw null" flag; the caller * ORs RAY_ATTR_HAS_NULLS once, serially, after the parallel pass. @@ -2344,15 +2978,20 @@ typedef struct { /* Write a finalized scalar cell's payload at disjoint index i WITHOUT touching * the shared out->attrs flag; report null via the return value. Byte-identical - * to the serial agg_put_cell + ray_vec_set_null pair: a null cell stores the + * to serial emission followed by ray_vec_set_null: a null cell stores the * type's NULL sentinel (overwriting cell->f64/i64), matching ray_vec_set_null's * payload write — only the RAY_ATTR_HAS_NULLS flag set is deferred to the * caller (set once serially) to avoid a racy shared read-modify-write. */ static inline bool agg_put_cell_value(ray_t* out, int64_t i, ray_t* cell) { bool is_null = RAY_ATOM_IS_NULL(cell); switch (out->type) { - case RAY_F64: ((double*)ray_data(out))[i] = is_null ? NULL_F64 : cell->f64; break; - default: ((int64_t*)ray_data(out))[i] = is_null ? NULL_I64 : cell->i64; break; + case RAY_BOOL: case RAY_U8: ((uint8_t*)ray_data(out))[i] = cell->u8; break; + case RAY_I16: ((int16_t*)ray_data(out))[i] = is_null ? NULL_I16 : cell->i16; break; + case RAY_I32: case RAY_DATE: case RAY_TIME: + ((int32_t*)ray_data(out))[i] = is_null ? NULL_I32 : cell->i32; break; + case RAY_F32: ((float*)ray_data(out))[i] = is_null ? NULL_F32 : (float)cell->f64; break; + case RAY_F64: ((double*)ray_data(out))[i] = is_null ? NULL_F64 : cell->f64; break; + default: ((int64_t*)ray_data(out))[i] = is_null ? NULL_I64 : cell->i64; break; } return is_null; } @@ -2369,10 +3008,8 @@ static void agg_radix_finalize_fn(void* vctx, uint32_t wid, int64_t start, int64 for (int64_t i = start; i < end; i++) { uint32_t p = (uint32_t)(c->pairs[i].idx >> 32); uint32_t gg = (uint32_t)(c->pairs[i].idx & 0xffffffffu); - ray_t* cell = c->vts[a]->finalize( - c->parts[p].states + (size_t)gg * c->block + c->off[a], NULL, kparam); - if (agg_put_cell_value(out, i, cell)) any_null = true; - ray_release(cell); + const void* state = c->parts[p].states + (size_t)gg * c->block + c->off[a]; + any_null |= agg_finalize_value(c->vts[a], state, out, i, kparam); } if (any_null) c->saw_null[(size_t)wid * n_aggs + a] = 1; } @@ -2537,6 +3174,7 @@ static ray_t* exec_group_v2_parallel_radix( /* Phase 1: scatter. Sel mode dispatches over selected-row space [0,n_sel); * each worker decodes its slice of selected rows to ORIGINAL indices. */ ray_pool_dispatch(pool, agg_radix_scatter_fn, &ctx, sel ? n_sel : nrows); + ray_profile_tick("radix: scattered rows"); /* Phase 2: per-partition group+accumulate. */ int oom = ctx.phase1_oom; @@ -2546,6 +3184,8 @@ static ray_t* exec_group_v2_parallel_radix( for (uint32_t p = 0; p < n_parts; p++) if (parts[p].oom) { oom = 1; break; } + ray_profile_tick("radix: reduced partitions"); + /* Phases 1+2 done (both dispatches joined); val_off/val2_off no longer read. */ scratch_free(off_hdr); @@ -2614,8 +3254,21 @@ static ray_t* exec_group_v2_parallel_radix( bool order_ok = true; int64_t ordered = 0; bool ord_parallel_done = false; - if (pool && nw > 1 && input_count >= (1 << 20)) { - const int64_t ORD_CHUNK = 1 << 17; + /* The compaction below writes into `pairs` in place. Its safety + * argument (see agg_ord_compact_fn) only shows that an EARLIER chunk + * cannot clobber a LATER chunk's unread input; the reverse is not + * true, because a later chunk writes at its prefix offset, which sits + * far below its own input range whenever groups are sparse relative + * to rows — i.e. any ordinary group-by with duplicates. Running the + * chunks in ascending order on one task is what makes that safe, and + * ray_pool_dispatch only ever produced one task here because its + * grain is 8192 ELEMENTS and the extent is n_chunks. Say so instead + * of depending on it: above the grain the compaction runs serially + * rather than racing (#556). */ + const int64_t ORD_CHUNK = 1 << 17; + const int64_t ORD_MAX_CHUNKS = RAY_DISPATCH_MORSELS * RAY_MORSEL_ELEMS; + if (pool && nw > 1 && input_count >= (1 << 20) && + (input_count + ORD_CHUNK - 1) / ORD_CHUNK <= ORD_MAX_CHUNKS) { int64_t n_chunks = (input_count + ORD_CHUNK - 1) / ORD_CHUNK; ray_t* ordcnt_hdr = NULL; int64_t* ord_counts = (int64_t*)scratch_alloc(&ordcnt_hdr, @@ -2677,6 +3330,7 @@ static ray_t* exec_group_v2_parallel_radix( return ray_error("group", "failed to order radix groups"); } } /* end full-order path */ + ray_profile_tick("radix: ordered groups"); ray_t* result = ray_table_new(n_keys + n_aggs); if (!result || RAY_IS_ERR(result)) { @@ -2690,7 +3344,7 @@ static ray_t* exec_group_v2_parallel_radix( /* Emit key columns by sequential un-pack from the contiguous per-partition * packed-key buffers (cache-friendly), NOT a scattered gather of the - * original columns at first_row[]. Byte-identical to agg_gather_key_col + * original columns at first_row[]. Byte-identical to ray_group_gather * (see agg_unpack_key_col_new), incl. SYM payload + domain. * * All n_keys destinations are built BEFORE the fill so one dispatch covers @@ -2821,13 +3475,13 @@ static ray_t* exec_group_v2_parallel_radix( for (int64_t i = 0; i < n_emit; i++) { uint32_t p = (uint32_t)(pairs[i].idx >> 32); uint32_t gg = (uint32_t)(pairs[i].idx & 0xffffffffu); - ray_t* cell = vts[a]->finalize(parts[p].states + (size_t)gg * block + off[a], NULL, kparams[a]); + const void* state = parts[p].states + (size_t)gg * block + off[a]; if (is_list) { + ray_t* cell = vts[a]->finalize(state, NULL, kparams[a]); out = ray_list_set(out, i, cell); /* retains cell */ - ray_release(cell); /* drop our local ref */ - } else { - agg_put_cell(out, i, cell); ray_release(cell); + } else if (agg_finalize_value(vts[a], state, out, i, kparams[a])) { + out->attrs |= RAY_ATTR_HAS_NULLS; } } outs[a] = out; /* ray_list_set may COW-realloc */ @@ -2850,6 +3504,553 @@ static ray_t* exec_group_v2_parallel_radix( static ray_t* agg_build_compact(ray_graph_t* g, ray_op_t* op, ray_t* tbl, int64_t* idx, int64_t n_sel); +/* Stable row slices shared by ordered and streaming consumers. Large group + * directories use O(groups) counters; replicated small-directory counters + * have a fixed memory budget independent of input size. */ +typedef struct { + const uint32_t* gids; + int64_t nrows; + uint32_t tasks, parts, bits, slices; + int64_t slots, local_stride; + int64_t* local; + const int64_t* offsets; + uint32_t part_slices[257]; + int64_t slice_begin[512], slice_end[512]; + int64_t* hist; + int64_t* starts; + uint64_t* packed; + int64_t* counts; + int64_t* cursor; + int64_t* rows; +} agg_index_layout_t; +static void agg_index_hist(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_index_layout_t* c = raw; + for (int64_t task = start; task < end; task++) { + int64_t* hist = c->hist + task * c->parts; + int64_t begin = c->nrows / c->tasks * task; + int64_t limit = task + 1 == c->tasks ? c->nrows : c->nrows / c->tasks * (task + 1); + for (int64_t r = begin; r < limit; r++) hist[c->gids[r] >> c->bits]++; + } +} +static void agg_index_pack(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_index_layout_t* c = raw; + for (int64_t task = start; task < end; task++) { + int64_t* cursor = c->hist + task * c->parts; + int64_t begin = c->nrows / c->tasks * task; + int64_t limit = task + 1 == c->tasks ? c->nrows : c->nrows / c->tasks * (task + 1); + for (int64_t r = begin; r < limit; r++) + c->packed[cursor[c->gids[r] >> c->bits]++] = ((uint64_t)c->gids[r] << 32) | (uint32_t)r; + } +} +static void agg_index_count(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_index_layout_t* c = raw; + for (int64_t task = start; task < end; task++) { + int64_t* counts = c->local + task * c->local_stride; + for (int64_t i = c->slice_begin[task]; i < c->slice_end[task]; i++) + counts[(c->packed[i] >> 32) & (c->slots - 1)]++; + } +} +static void agg_index_merge_counts(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_index_layout_t* c = raw; + for (int64_t g = start; g < end; g++) { + uint32_t part = g >> c->bits; + int64_t count = 0; + for (uint32_t task = c->part_slices[part]; task < c->part_slices[part + 1]; task++) + count += c->local[task * c->local_stride + (g & (c->slots - 1))]; + c->counts[g] = count; + } +} +static void agg_index_slice_cursors(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_index_layout_t* c = raw; + for (int64_t g = start; g < end; g++) { + uint32_t part = g >> c->bits; + int64_t offset = c->offsets[g]; + for (uint32_t task = c->part_slices[part]; task < c->part_slices[part + 1]; task++) { + int64_t* slot = &c->local[task * c->local_stride + (g & (c->slots - 1))]; + int64_t count = *slot; *slot = offset; offset += count; + } + } +} +static void agg_index_fill(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_index_layout_t* c = raw; + for (int64_t task = start; task < end; task++) { + int64_t* cursor = c->local + task * c->local_stride; + for (int64_t i = c->slice_begin[task]; i < c->slice_end[task]; i++) { + uint64_t record = c->packed[i]; + c->rows[cursor[(record >> 32) & (c->slots - 1)]++] = (uint32_t)record; + } + } +} +/* For a small group directory, bounded task-local counters avoid packing + * another row buffer. Source-task prefixes preserve order within each group. */ +static void agg_index_direct_count(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_index_layout_t* c = raw; + for (int64_t task = start; task < end; task++) { + int64_t* counts = c->local + task * c->slots; + int64_t begin = c->nrows / c->tasks * task; + int64_t limit = task + 1 == c->tasks ? c->nrows : c->nrows / c->tasks * (task + 1); + for (int64_t r = begin; r < limit; r++) counts[c->gids[r]]++; + } +} +static void agg_index_direct_fill(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_index_layout_t* c = raw; + for (int64_t task = start; task < end; task++) { + int64_t* cursor = c->local + task * c->slots; + int64_t begin = c->nrows / c->tasks * task; + int64_t limit = task + 1 == c->tasks ? c->nrows : c->nrows / c->tasks * (task + 1); + for (int64_t r = begin; r < limit; r++) c->rows[cursor[c->gids[r]]++] = r; + } +} +static int agg_index_layout(const agg_groups_t* groups, int64_t nrows, + int64_t* counts, int64_t* offsets, int64_t* cursor, int64_t* rows) { + ray_pool_t* pool = ray_pool_get(); + int64_t ng = groups->ngroups; + uint32_t direct_tasks = pool ? ray_pool_total_workers(pool) * 4 : 0; + if (direct_tasks > RAY_POOL_INIT_TASKS) direct_tasks = RAY_POOL_INIT_TASKS; + /* Cap replicated counters at 256 KiB, including cache-line padding. */ + int64_t direct_slots = ((ng + 7) & ~INT64_C(7)) + 8; + if (ray_pool_par_dispatch_ok(pool, nrows, RAY_PARALLEL_THRESHOLD) && direct_slots > 0 && + direct_tasks && direct_slots <= 32768 / direct_tasks) { + agg_index_layout_t direct = {.gids = groups->gids, .nrows = nrows, + .tasks = direct_tasks, .slots = direct_slots, .rows = rows}; + direct.local = ray_calloc_raw((size_t)direct_tasks * direct_slots * sizeof(int64_t)); + if (!direct.local) return -1; + ray_pool_dispatch_n(pool, agg_index_direct_count, &direct, direct_tasks); + if (agg_cancelled()) { ray_free_raw(direct.local); return -1; } + for (int64_t g = 0; g < ng; g++) { + int64_t at = offsets[g]; + for (uint32_t task = 0; task < direct_tasks; task++) { + int64_t* slot = direct.local + task * direct_slots + g; + int64_t count = *slot; *slot = at; at += count; + } + counts[g] = at - offsets[g]; offsets[g + 1] = at; + } + ray_pool_dispatch_n(pool, agg_index_direct_fill, &direct, direct_tasks); + ray_free_raw(direct.local); + return agg_cancelled() ? -1 : 0; + } + /* Packed row addresses use 32 bits; retain full-width indices above it. */ + /* Histogram/packing adds two full row passes. With fewer than four + * workers, the direct count/fill layout uses less time and memory. */ + if (nrows > UINT32_MAX || !ray_pool_par_dispatch_ok(pool, nrows, RAY_PARALLEL_THRESHOLD) || + ray_pool_total_workers(pool) < 4) { + for (int64_t r = 0; r < nrows; r++) counts[groups->gids[r]]++; + for (int64_t i = 0; i < ng; i++) offsets[i + 1] = offsets[i] + counts[i]; + memcpy(cursor, offsets, (size_t)ng * sizeof(int64_t)); + for (int64_t r = 0; r < nrows; r++) rows[cursor[groups->gids[r]]++] = r; + return 0; + } + /* One source range per worker avoids scattering tiny adjacent ranges + * into the same pages. Partition slices supply additional skew tasks. */ + uint32_t tasks = ray_pool_total_workers(pool); + if (tasks > RAY_POOL_INIT_TASKS) tasks = RAY_POOL_INIT_TASKS; + uint32_t parts = 1; + while (parts < tasks && parts < 256) parts *= 2; + /* Adjacent group ids own adjacent output ranges. Partition by the high + * bits so different workers do not scatter interleaved groups into the + * same output pages. Hot partitions still split into source-order slices. */ + uint32_t slots = 1; + while (slots < (ng + parts - 1) / parts) slots *= 2; + agg_index_layout_t c = {.gids = groups->gids, .nrows = nrows, .tasks = tasks, + .parts = parts, .bits = (uint32_t)__builtin_ctz(slots), .slots = slots, .local_stride = slots + 8, + .offsets = offsets, .counts = counts, .cursor = cursor, .rows = rows}; + c.hist = ray_calloc_raw(((size_t)tasks * parts + parts + 1) * sizeof(int64_t)); + c.packed = ray_alloc_raw((size_t)nrows * sizeof(uint64_t)); + if (!c.hist || !c.packed) { ray_free_raw(c.hist); ray_free_raw(c.packed); return -1; } + c.starts = c.hist + (size_t)tasks * parts; + ray_pool_dispatch_n(pool, agg_index_hist, &c, tasks); + int64_t offset = 0; + for (uint32_t part = 0; part < parts; part++) { + c.starts[part] = offset; + for (uint32_t task = 0; task < tasks; task++) { + size_t at = (size_t)task * parts + part; + int64_t count = c.hist[at]; c.hist[at] = offset; offset += count; + } + } + c.starts[parts] = offset; + ray_profile_tick("indexed slices: histogram"); + ray_pool_dispatch_n(pool, agg_index_pack, &c, tasks); + ray_profile_tick("indexed slices: packed"); + int rc = -1; + if (agg_cancelled()) goto done; + /* Split hot partitions by rows. There are at most 2*parts slices, each + * storing a power-of-two group range plus a cache-line gap. This uses + * at most four group slabs plus bounded task padding. + * Packed rows and slice prefixes both retain source order. */ + int64_t grain = (nrows + parts - 1) / parts; + for (uint32_t part = 0; part < parts; part++) { + c.part_slices[part] = c.slices; + for (int64_t begin = c.starts[part]; begin < c.starts[part + 1];) { + int64_t end = c.starts[part + 1] - begin < grain ? c.starts[part + 1] : begin + grain; + c.slice_begin[c.slices] = begin; c.slice_end[c.slices++] = end; + begin = end; + } + } + c.part_slices[parts] = c.slices; + c.local = ray_calloc_raw((size_t)c.slices * c.local_stride * sizeof(int64_t)); + if (!c.local) goto done; + ray_pool_dispatch_n(pool, agg_index_count, &c, c.slices); + ray_pool_dispatch(pool, agg_index_merge_counts, &c, ng); + if (agg_cancelled()) goto done; + ray_profile_tick("indexed slices: counted groups"); + for (int64_t i = 0; i < ng; i++) offsets[i + 1] = offsets[i] + counts[i]; + ray_pool_dispatch(pool, agg_index_slice_cursors, &c, ng); + ray_pool_dispatch_n(pool, agg_index_fill, &c, c.slices); + ray_profile_tick("indexed slices: filled"); + if (!agg_cancelled()) rc = 0; +done: + ray_free_raw(c.local); ray_free_raw(c.hist); ray_free_raw(c.packed); + return rc; +} + +/* Streaming consumers of the shared index use row-balanced tasks. Only + * groups crossing task boundaries need partial states (at most two per task), + * so a dominant group can use every worker without replicated group slabs. */ +typedef struct { + const agg_vtable_t* vt; + ray_t* x; + ray_t* y; + const int64_t* rows; + const int64_t* offsets; + int64_t ng, nrows; + uint32_t tasks; + char* states; + int64_t* partial_groups; + _Atomic(bool) failed; +} agg_index_stream_t; +static void agg_index_stream_init(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_index_stream_t* c = raw; + for (int64_t g = start; g < end; g++) c->vt->init(c->states + g * c->vt->state_size); +} +static void agg_index_stream_reduce(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_index_stream_t* c = raw; + uint8_t ex = c->x ? col_esz(c->x) : 0, ey = c->y ? col_esz(c->y) : 0; + char* x = ex ? ray_alloc_raw((size_t)AGG_SEL_CHUNK * ex) : NULL; + char* y = ey ? ray_alloc_raw((size_t)AGG_SEL_CHUNK * ey) : NULL; + if ((ex && !x) || (ey && !y)) { atomic_store(&c->failed, true); goto done; } + uint32_t gids[AGG_SEL_CHUNK] = {0}; + ray_valid_t vx = {x, c->x ? c->x->type : RAY_I64, c->x && ray_vec_may_have_nulls(c->x)}; + ray_valid_t vy = {y, c->y ? c->y->type : RAY_I64, c->y && ray_vec_may_have_nulls(c->y)}; + for (int64_t task = start; task < end; task++) { + int64_t begin = c->nrows / c->tasks * task; + int64_t limit = task + 1 == c->tasks ? c->nrows : c->nrows / c->tasks * (task + 1); + int64_t lo = 0, hi = c->ng; + while (lo < hi) { int64_t mid = lo + (hi - lo) / 2; if (c->offsets[mid + 1] <= begin) lo = mid + 1; else hi = mid; } + int partial = 0; + for (int64_t group = lo, at = begin; at < limit && !agg_cancelled(); group++) { + int64_t stop = c->offsets[group + 1] < limit ? c->offsets[group + 1] : limit; + bool whole = at == c->offsets[group] && stop == c->offsets[group + 1]; + int64_t slot = whole ? group : c->ng + 2 * task + partial; + char* state = c->states + slot * c->vt->state_size; + if (!whole) { + c->partial_groups[2 * task + partial++] = group; + c->vt->init(state); + } + while (at < stop) { + int64_t n = stop - at < AGG_SEL_CHUNK ? stop - at : AGG_SEL_CHUNK; + if (ex) agg_sel_gather_vals(x, ray_data(c->x), ex, c->rows + at, n); + if (ey) agg_sel_gather_vals(y, ray_data(c->y), ey, c->rows + at, n); + if (c->y) c->vt->update_batch2(state, c->vt->state_size, gids, x, y, &vx, &vy, n, NULL); + else c->vt->update_batch(state, c->vt->state_size, gids, x, &vx, n, NULL); + at += n; + } + } + } +done: + ray_free_raw(x); ray_free_raw(y); +} +static ray_t* agg_index_streaming(const agg_vtable_t* vt, ray_t* x, ray_t* y, + const agg_groups_t* groups, const int64_t* rows, const int64_t* offsets, + int64_t nrows, int64_t param) { + ray_pool_t* pool = ray_pool_get(); + if (!ray_pool_par_dispatch_ok(pool, nrows, RAY_PARALLEL_THRESHOLD)) + return y ? agg_run_one_bin(vt, x, y, groups->gids, nrows, groups->ngroups, param) + : agg_run_one(vt, x, groups->gids, nrows, groups->ngroups, param); + uint32_t tasks = ray_pool_total_workers(pool) * 4; + if (tasks > RAY_POOL_INIT_TASKS) tasks = RAY_POOL_INIT_TASKS; + agg_index_stream_t c = {.vt = vt, .x = x, .y = y, .rows = rows, .offsets = offsets, + .ng = groups->ngroups, .nrows = nrows, .tasks = tasks, .failed = false}; + c.states = ray_alloc_raw((size_t)(c.ng + 2 * tasks) * vt->state_size); + c.partial_groups = ray_alloc_raw((size_t)2 * tasks * sizeof(int64_t)); + ray_t* result = NULL; + if (!c.states || !c.partial_groups) goto done; + for (uint32_t t = 0; t < 2 * tasks; t++) c.partial_groups[t] = -1; + ray_pool_dispatch(pool, agg_index_stream_init, &c, c.ng); + ray_pool_dispatch_n(pool, agg_index_stream_reduce, &c, tasks); + if (agg_cancelled() || atomic_load(&c.failed)) goto done; + for (uint32_t t = 0; t < 2 * tasks; t++) if (c.partial_groups[t] >= 0) + vt->merge(c.states + c.partial_groups[t] * vt->state_size, + c.states + (c.ng + t) * vt->state_size, NULL); + result = ray_vec_new(vt->out_type, c.ng); + if (!result || RAY_IS_ERR(result)) goto done; + result->len = c.ng; + agg_dense_emit_ctx_t emit = {.vt = vt, .out = result, .states = c.states, + .block = vt->state_size, .param = param, .any_null = false}; + ray_pool_dispatch(pool, agg_dense_emit_fn, &emit, c.ng); + if (atomic_load(&emit.any_null)) result->attrs |= RAY_ATTR_HAS_NULLS; +done: + ray_free_raw(c.states); ray_free_raw(c.partial_groups); + if (agg_cancelled()) { ray_release(result); return ray_error("cancel", NULL); } + return result ? result : ray_error("oom", NULL); +} + +typedef struct { + ray_t* src; + uint16_t kind; + ray_group_sym_view_t symbols; +} agg_index_winners_t; +static int64_t agg_index_winner(void* raw, const int64_t* rows, int64_t count) { + agg_index_winners_t* c = raw; + const void* data = ray_data(c->src); + if (c->kind == OP_FIRST || c->kind == OP_LAST) { + bool last = c->kind == OP_LAST; + if (!count || ray_interrupted()) return -1; + if (!ray_vec_may_have_nulls(c->src) && c->src->type != RAY_SYM && c->src->type != RAY_STR) + return rows[last ? count - 1 : 0]; +#define INDEX_FIRST_VALID(TYPE, VALID) do { \ + const TYPE* values = data; \ + for (int64_t j = 0; j < count; j++) { \ + if ((j & 65535) == 0 && ray_interrupted()) return -1; \ + int64_t row = rows[last ? count - 1 - j : j]; \ + TYPE value = values[row]; \ + if (VALID) return row; \ + } \ + return -1; \ + } while (0) + switch (c->src->type) { + case RAY_I16: INDEX_FIRST_VALID(int16_t, value != NULL_I16); + case RAY_I32: case RAY_DATE: case RAY_TIME: + INDEX_FIRST_VALID(int32_t, value != NULL_I32); + case RAY_I64: case RAY_TIMESTAMP: + INDEX_FIRST_VALID(int64_t, value != NULL_I64); + case RAY_F32: INDEX_FIRST_VALID(float, value == value); + case RAY_F64: INDEX_FIRST_VALID(double, value == value); + default: break; + } +#undef INDEX_FIRST_VALID + } + int64_t best = -1; + for (int64_t j = 0; j < count; j++) { + if ((j & 65535) == 0 && ray_interrupted()) return -1; + int64_t r = rows[c->kind == OP_LAST ? count - 1 - j : j]; + if (ray_vec_is_null(c->src, r)) continue; + if (best < 0) best = r; + if (c->kind == OP_FIRST || c->kind == OP_LAST) break; + ray_t* x = ray_group_sym_read(&c->symbols, ray_sym_vec_domain(c->src), ray_read_sym(data, r, c->src->type, c->src->attrs)); + ray_t* y = ray_group_sym_read(&c->symbols, ray_sym_vec_domain(c->src), ray_read_sym(data, best, c->src->type, c->src->attrs)); + int cmp = ray_str_cmp(x, y); + if (c->kind == OP_MIN ? cmp < 0 : cmp > 0) best = r; + } + return best; +} + +typedef struct { + const int64_t* rows; + const int64_t* offsets; + const int64_t* counts; + ray_t** values; + _Atomic(bool) failed; +} agg_index_vectors_t; +static void agg_index_vectors_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_index_vectors_t* c = raw; + for (int64_t g = start; g < end && !agg_cancelled(); g++) { + ray_t* value = ray_vec_new(RAY_I64, c->counts[g]); + if (!value || RAY_IS_ERR(value)) { ray_release(value); atomic_store(&c->failed, true); return; } + value->len = c->counts[g]; + memcpy(ray_data(value), c->rows + c->offsets[g], (size_t)value->len * sizeof(int64_t)); + c->values[g] = value; + } +} +ray_t* agg_group_indices(ray_t* source) { + ray_pool_t* pool = ray_pool_get(); + int64_t n = source->len; + if (n > INT32_MAX || !ray_pool_par_dispatch_ok(pool, n, RAY_PARALLEL_THRESHOLD) || + ray_pool_total_workers(pool) < 4) return NULL; + switch (source->type) { + case RAY_BOOL: case RAY_U8: case RAY_I16: case RAY_I32: case RAY_I64: + case RAY_F32: case RAY_F64: case RAY_DATE: case RAY_TIME: case RAY_TIMESTAMP: + case RAY_SYM: case RAY_GUID: case RAY_STR: case RAY_LIST: break; + default: return NULL; + } + agg_groups_t groups = {0}; + dense_plan_t plan; + bool dense = agg_dense_plan(&source, 1, NULL, 0, n, &plan); + int rc = dense ? agg_group_keys_dense(&source, n, &plan, &groups) + : agg_group_keys(&source, 1, n, &groups); + if (rc) return ray_error(agg_cancelled() ? "cancel" : "oom", NULL); + int64_t ng = groups.ngroups; + int64_t* scratch = ray_calloc_raw((size_t)(n + 3 * ng + 1) * sizeof(int64_t)); + if (!scratch) { agg_groups_free(&groups); return ray_error("oom", NULL); } + int64_t* offsets = scratch + ng; + int64_t* cursor = offsets + ng + 1; + int64_t* rows = cursor + ng; + ray_t* keys = NULL; + ray_t* values = NULL; + ray_t* result = NULL; + if (agg_index_layout(&groups, n, scratch, offsets, cursor, rows)) goto failed; + keys = ray_group_gather(source, groups.first_row, ng); + if (!keys || RAY_IS_ERR(keys)) goto failed; + values = ray_list_new(ng); + if (!values || RAY_IS_ERR(values)) goto failed; + memset(ray_data(values), 0, (size_t)ng * sizeof(ray_t*)); + values->len = ng; + agg_index_vectors_t emit = {rows, offsets, scratch, ray_data(values), false}; + ray_group_dispatch(agg_index_vectors_run, &emit, scratch, ng); + if (agg_cancelled() || atomic_load(&emit.failed)) goto failed; + result = ray_dict_new(keys, values); + keys = values = NULL; +failed: + ray_release(keys); ray_release(values); ray_free_raw(scratch); agg_groups_free(&groups); + return result ? result : ray_error(agg_cancelled() ? "cancel" : "oom", NULL); +} + +/* O(rows + groups) shared layout. Holistic helpers allocate disjoint group + * scratch; no worker owns a second full input or a second grouping. */ +static ray_t* agg_indexed_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, + ray_t** keys, int64_t* key_syms, int64_t nrows) { + ray_op_ext_t* ext = find_ext(g, op->id); + agg_groups_t groups = {0}; + dense_plan_t dp; + bool dense = agg_dense_plan(keys, ext->n_keys, NULL, 0, nrows, &dp); + route_stats.dense_plan_available = dense; + int rc = dense ? agg_group_keys_dense(keys, nrows, &dp, &groups) + : agg_group_keys(keys, ext->n_keys, nrows, &groups); + if (rc) return ray_error(agg_cancelled() ? "cancel" : "oom", NULL); + if (agg_cancelled()) { agg_groups_free(&groups); return ray_error("cancel", NULL); } + int64_t ng = groups.ngroups; + int64_t* counts = ray_calloc_raw((size_t)(ng + 1) * sizeof(int64_t)); + int64_t* offsets = ray_calloc_raw((size_t)(ng + 1) * sizeof(int64_t)); + int64_t* cursor = ray_calloc_raw((size_t)(ng + 1) * sizeof(int64_t)); + int64_t* rows = ray_alloc_raw((size_t)(nrows + 1) * sizeof(int64_t)); + ray_t* result = NULL; + if (!counts || !offsets || !cursor || !rows) { result = ray_error("oom", NULL); goto done; } + ray_profile_tick("indexed: grouped keys"); + if (agg_index_layout(&groups, nrows, counts, offsets, cursor, rows)) { + result = ray_error(agg_cancelled() ? "cancel" : "oom", NULL); goto done; + } + ray_profile_tick("indexed: stable row slices"); + result = ray_table_new(ext->n_keys + ext->n_aggs); + if (!result || RAY_IS_ERR(result)) goto done; + for (uint32_t k = 0; k < ext->n_keys; k++) { + ray_t* col = ray_group_gather(keys[k], groups.first_row, ng); + if (!col || RAY_IS_ERR(col)) { ray_release(result); result = col; goto done; } + if (col->type == RAY_F32 || col->type == RAY_F64) { + for (int64_t i = 0; i < ng; i++) { + if (col->type == RAY_F32) { + float* d = ray_data(col); if (d[i] == 0) memset(&d[i], 0, sizeof(float)); + } else { + double* d = ray_data(col); if (d[i] == 0) memset(&d[i], 0, sizeof(double)); + } + } + } + result = ray_table_add_col(result, key_syms[k], col); ray_release(col); + if (!result || RAY_IS_ERR(result)) goto done; + } + ray_profile_tick("indexed: emitted keys"); + for (uint32_t a = 0; a < ext->n_aggs; a++) { + if (agg_cancelled()) { ray_release(result); result = ray_error("cancel", NULL); goto done; } + uint16_t kind = ext->agg_ops[a]; + ray_op_ext_t* ie = find_ext(g, ext->agg_ins[a]); + ray_t* src = ie ? ray_table_get_col(tbl, ie->sym) : NULL; + int64_t param = ext->agg_k ? ext->agg_k[a] : 0; + ray_t* col = NULL; + if (kind == OP_MEDIAN) col = ray_median_per_group_buf(src, rows, offsets, counts, ng); + else if (kind == OP_QUANTILE) { + double q = 0.5; if (ext->agg_k) memcpy(&q, ¶m, sizeof(q)); + col = ray_quantile_per_group_buf(src, rows, offsets, counts, ng, q); + } else if (kind == OP_MODE) col = ray_mode_per_group_buf(src, rows, offsets, counts, ng); + else if (kind == OP_TOP_N || kind == OP_BOT_N) + col = ray_topk_per_group_buf(src, param, kind == OP_TOP_N, rows, offsets, counts, ng); + else if (agg_indexed_supported(kind, src ? src->type : RAY_I64)) { + col = ray_wide_minmax_per_group_buf(src, kind, rows, offsets, counts, ng); + if (!col) { + /* Native first/last and lexical SYM extrema retain the source + * column's domain by gathering winning original row indices. */ + int64_t* winners = cursor; + agg_index_winners_t work = {src, kind, {0}}; + if (src->type == RAY_SYM) + ray_sym_strings_borrow(&work.symbols.strings, &work.symbols.count); + ray_group_winners(agg_index_winner, &work, rows, offsets, counts, ng, winners); + if (agg_cancelled()) { ray_release(result); result = ray_error("cancel", NULL); goto done; } + ray_profile_tick("indexed: selected winning rows"); + col = ray_group_gather(src, winners, ng); + /* F32 reduction results follow the existing F64 contract. */ + if (col && !RAY_IS_ERR(col) && src->type == RAY_F32) { + ray_t* widened = ray_vec_new(RAY_F64, ng); + if (widened && !RAY_IS_ERR(widened)) { + widened->len = ng; + for (int64_t i = 0; i < ng; i++) ((double*)ray_data(widened))[i] = ((float*)ray_data(col))[i]; + widened->attrs |= col->attrs & RAY_ATTR_HAS_NULLS; + } + ray_release(col); col = widened; + } + } + } else if (ext->agg_ins2 && ext->agg_ins2[a] != RAY_OP_NONE) { + ray_op_ext_t* ye = find_ext(g, ext->agg_ins2[a]); + col = agg_index_streaming(agg_resolve(kind, src->type), src, + ray_table_get_col(tbl, ye->sym), &groups, rows, offsets, nrows, param); + } else if (kind == OP_COUNT) { + col = ray_vec_new(RAY_I64, ng); + if (col && !RAY_IS_ERR(col)) { col->len = ng; memcpy(ray_data(col), counts, (size_t)ng * sizeof(int64_t)); } + } else col = agg_index_streaming(agg_resolve(kind, src ? src->type : RAY_I64), + src, NULL, &groups, rows, offsets, nrows, param); + if (!col || RAY_IS_ERR(col)) { ray_release(result); result = col; goto done; } + ray_profile_tick("indexed: emitted aggregate"); + result = ray_table_add_col(result, agg_result_col_name(ie ? ie->sym : 0, kind), col); + ray_release(col); + if (!result || RAY_IS_ERR(result)) goto done; + } +done: + if (result && !RAY_IS_ERR(result) && agg_cancelled()) { ray_release(result); result = ray_error("cancel", NULL); } + ray_free_raw(counts); ray_free_raw(offsets); ray_free_raw(cursor); ray_free_raw(rows); + agg_groups_free(&groups); + return result ? result : ray_error("oom", NULL); +} + +/* Shared extrema help when repeated keys let workers skip state writes. + * A deterministic, stratified sample exercises the registered capability with + * its own state, so this decision needs no aggregate/type-specific predicates. + * Predominantly changing states use owned partitions instead of contended CAS. + * The sample is query-local and never supplies an answer to execution. */ +static bool agg_shared_sample(ray_graph_t* g, ray_op_ext_t* ext, ray_t* tbl, + ray_t** keys, const agg_vo_t* vo, int64_t rows) { + if (!ext->n_aggs) return true; + if (rows < 1024) return false; + enum { SAMPLES = 1024, CAP = 2048 }; + agg_desc_t d; + if (!agg_desc_init(&d, g, ext, tbl, keys)) return false; + char* states = ray_alloc_raw((SAMPLES + 1) * vo->block); + if (!states) { agg_desc_free(&d); return false; } + char* previous = states + SAMPLES * vo->block; + int16_t map[CAP]; int64_t values[SAMPLES]; + memset(map, -1, sizeof(map)); + uint32_t gid = 0; + int groups = 0, unchanged = 0; + int64_t step = rows / SAMPLES; + for (int i = 0; i < SAMPLES; i++) { + uint64_t jitter = ray_hash_i64(i); + int64_t row = i * step + jitter % step; + int64_t value = agg_read_key_i64(keys[0], ray_data(keys[0]), row); + uint32_t at = ray_hash_i64(value) & (CAP - 1); + while (map[at] >= 0 && values[map[at]] != value) at = (at + 1) & (CAP - 1); + if (map[at] < 0) { + map[at] = groups; values[groups] = value; + for (uint32_t a = 0; a < ext->n_aggs; a++) vo->vts[a]->init(states + groups * vo->block + vo->off[a]); + groups++; + } + char* state = states + map[at] * vo->block; + memcpy(previous, state, vo->block); + for (uint32_t a = 0; a < ext->n_aggs; a++) { + const void* input = d.val_data[a] ? (const char*)d.val_data[a] + (size_t)row * d.val_esz[a] : NULL; + ray_valid_t valid = {input, d.val_types[a], d.val_hasnull[a]}; + vo->vts[a]->update_shared(state + vo->off[a], vo->block, &gid, input, &valid, 1); + } + unchanged += memcmp(previous, state, vo->block) == 0; + } + ray_free_raw(states); agg_desc_free(&d); + return unchanged >= SAMPLES / 4; +} + /* Core of exec_group_v2: resolution + strategy dispatch over (tbl, nrows). * * Selection handling. `sel` is the pushed WHERE filter's rowsel (or NULL). @@ -2872,6 +4073,11 @@ static ray_t* exec_group_v2_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, int64_t nrows, ray_t* sel, const int64_t* sel_prefix, int64_t n_sel, int64_t group_limit) { + agg_route_reason(AGG_V2_ADMITTED); + route_stats.nullable_key = false; + route_stats.dense_plan_available = false; + route_stats.dense_worker_budget = false; + route_stats.dense_tasks = 0; ray_op_ext_t* ext = find_ext(g, op->id); /* Exact-size carve for the per-key column pointers + syms (one block, both @@ -2887,6 +4093,33 @@ static ray_t* exec_group_v2_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, ray_op_ext_t* kext = find_ext(g, ext->keys[k]); key_cols[k] = ray_table_get_col(tbl, kext->sym); key_syms[k] = kext->sym; + if (key_cols[k]->type != RAY_SYM && ray_vec_may_have_nulls(key_cols[k])) + route_stats.nullable_key = true; + } + + bool indexed = false; + for (uint32_t k = 0; k < ext->n_keys; k++) { + int8_t t = key_cols[k]->type; + if (t == RAY_F32 || t == RAY_F64 || t == RAY_GUID || t == RAY_STR || t == RAY_LIST) indexed = true; + } + for (uint32_t a = 0; a < ext->n_aggs; a++) { + ray_op_ext_t* ie = find_ext(g, ext->agg_ins[a]); + ray_t* col = ie ? ray_table_get_col(tbl, ie->sym) : NULL; + if (col && agg_indexed_supported(ext->agg_ops[a], col->type)) indexed = true; + } + if (indexed) { + if (sel) { + ray_t* idx = ray_rowsel_to_indices(sel); + if (!idx || RAY_IS_ERR(idx)) { scratch_free(kc_hdr); return idx ? idx : ray_error("oom", NULL); } + ray_t* compact = agg_build_compact(g, op, tbl, ray_data(idx), n_sel); + ray_t* result = compact && !RAY_IS_ERR(compact) + ? exec_group_v2_run(g, op, compact, n_sel, NULL, NULL, 0, group_limit) : compact; + if (compact && !RAY_IS_ERR(compact)) ray_release(compact); + ray_release(idx); scratch_free(kc_hdr); return result; + } + agg_route_record(AGG_ROUTE_V2_INDEXED); + ray_t* result = agg_indexed_run(g, op, tbl, key_cols, key_syms, nrows); + scratch_free(kc_hdr); return result; } /* Precompute AoS state layout for the admitted aggregates. */ @@ -2903,6 +4136,7 @@ static ray_t* exec_group_v2_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, dense_plan_t dp; bool dense = sel ? agg_dense_plan_sel(key_cols, ext->n_keys, n_sel, sel, sel_prefix, &dp) : agg_dense_plan(key_cols, ext->n_keys, vts, ext->n_aggs, nrows, &dp); + route_stats.dense_plan_available = dense; /* Compact-fallback helper for the non-chunked shapes (hash-fallback keys, * and the serial path): gather selected rows into a compact table once and @@ -2931,8 +4165,8 @@ static ray_t* exec_group_v2_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, /* Buffered accumulators retain every contributing value per group. Running * them through the parallel radix strategy overlaps those buffers with the * full scatter payload and can exhaust the heap on large inputs. Keep - * buffered shapes on the serial v2 driver; streaming shapes retain all - * parallel strategies below. */ + * buffered shapes on the shared indexed driver; streaming shapes retain + * the dense/radix strategies below. */ bool all_streaming = true; for (uint32_t a = 0; a < ext->n_aggs && all_streaming; a++) if (vts[a]->kind != ACC_STREAMING) all_streaming = false; @@ -2943,8 +4177,101 @@ static ray_t* exec_group_v2_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, * when the aggregate slot count across all workers remains O(input); * this is data-derived and independent of cache or RAM size. */ uint32_t dense_workers = ray_pool_total_workers(pool); - bool dense_par_ok = dp.ok && dense_workers > 0 - && dp.total_slots <= eff_n / (int64_t)dense_workers; + double scatter_budget = (double)eff_n * 2 * (8.0 * (ext->n_keys + ext->n_aggs + 1)); + int64_t watermark = ray_heap_anon_watermark(); + double dense_budget = scatter_budget; + if (dense_budget > (double)SIZE_MAX) dense_budget = (double)SIZE_MAX; + if (watermark > 0 && dense_budget > (double)watermark / 4) dense_budget = (double)watermark / 4; + double slab_bytes = dp.ok ? (double)dp.total_slots * (block + sizeof(int64_t) + 1) : 0; + /* A bounded group emit selects first-seen groups. Dense slot order + * cannot satisfy that contract; retain radix's bounded selection. */ + bool shared_plan = dp.ok && group_limit <= 0 && !sel && ext->n_keys == 1 && dp.total_slots >= 4096; + for (uint32_t a = 0; a < ext->n_aggs && shared_plan; a++) + shared_plan = vts[a]->update_shared != NULL; + if (shared_plan && slab_bytes <= dense_budget && agg_shared_sample(g, ext, tbl, key_cols, &vo, nrows)) { + route_stats.dense_worker_budget = false; + agg_route_record(AGG_ROUTE_V2_DENSE); + ray_t* result = exec_group_v2_parallel_dense(g, op, tbl, key_cols, key_syms, ext, + nrows, pool, &dp, dense_workers, AGG_DENSE_SHARED, sel, sel_prefix, n_sel); + agg_vo_free(&vo); scratch_free(kc_hdr); + return result; + } + /* Partition ownership amortizes scatter through concurrent reducers. + * With one worker, direct task-local updates avoid that extra payload. */ + if (dp.ok && dense_workers > 1 && group_limit <= 0 && eff_n <= UINT32_MAX && dp.total_slots >= 4096) { + uint32_t sources = dense_workers; + if (sources > RAY_POOL_INIT_TASKS / 2) sources = RAY_POOL_INIT_TASKS / 2; + uint32_t parts = agg_dense_partition_parts(dense_workers, dp.total_slots); + uint32_t split_budget = sources * 4 < parts ? sources * 4 : parts; + int64_t part_slots = ((dp.total_slots + parts - 1) / parts + 7) & ~INT64_C(7); + /* Splitting adds at most one global slab. Shared input columns + * occupy one field in the payload, including mixed binary uses. */ + agg_desc_t payload; + double record_size = (double)SIZE_MAX; + if (agg_desc_init(&payload, g, ext, tbl, key_cols)) { + record_size = agg_partition_record(&payload, ext->n_aggs, NULL, NULL); + agg_desc_free(&payload); + } + double bytes = 2.0 * part_slots * parts * (block + sizeof(int64_t)) + + (double)eff_n * (sizeof(uint32_t) + record_size + (sel ? sizeof(int64_t) : 0)) + + (double)(sources + 1) * parts * sizeof(uint64_t) + + (double)(parts + split_budget) * sizeof(agg_dense_partition_task_t); + /* Small per-worker slabs are cheaper than another full payload + * pass. Prefer partition ownership only once replicated state + * traffic exceeds its row traffic; large pools/ranges still use + * bounded shared storage. */ + double local_traffic = dense_workers * slab_bytes; + double partition_traffic = (double)eff_n * (sizeof(uint32_t) + record_size); + /* Compare the complete partition allocation against radix's + * payload plus its worst-case per-row group state. A payload-only + * budget unnecessarily rejects dense high-cardinality domains, + * although radix must allocate the same aggregate states too. */ + double partition_budget = scatter_budget + (double)eff_n * (block + sizeof(int64_t)); + if (partition_budget > (double)SIZE_MAX) partition_budget = (double)SIZE_MAX; + if (watermark > 0 && partition_budget > (double)watermark / 4) + partition_budget = (double)watermark / 4; + if (bytes <= partition_budget && local_traffic > partition_traffic) { + route_stats.dense_worker_budget = false; + agg_route_record(AGG_ROUTE_V2_DENSE); + ray_t* result = exec_group_v2_parallel_dense(g, op, tbl, key_cols, key_syms, ext, + nrows, pool, &dp, dense_workers, AGG_DENSE_PARTITIONED, sel, sel_prefix, n_sel); + agg_vo_free(&vo); scratch_free(kc_hdr); + return result; + } + } + if (dp.ok && slab_bytes > 0) { + double fit = (dense_budget - (double)eff_n * sizeof(uint32_t)) / slab_bytes - 1; + if (fit < dense_workers) dense_workers = fit >= 2 ? (uint32_t)fit : 0; + } + bool dense_par_ok = dp.ok && group_limit <= 0 && dense_workers > 0; + /* Allocation size alone misses repeated wide-range worker updates. + * Estimate touched slot traffic from evenly spaced key samples in each + * worker-sized input range. Prefer radix when duplicated state traffic + * exceeds one scatter payload. This affects strategy only, never values; + * selections keep their existing exact selected-range planning. */ + if (dense_par_ok && !sel && ext->n_keys == 1) { + const void* data = ray_data(key_cols[0]); + double touched_slots = 0; + for (uint32_t w = 0; w < dense_workers; w++) { + int64_t start = nrows / dense_workers * w; + int64_t end = w + 1 == dense_workers ? nrows : nrows / dense_workers * (w + 1); + int64_t samples = end - start < 1024 ? end - start : 1024; + int64_t lo = INT64_MAX, hi = INT64_MIN; + for (int64_t i = 0; i < samples; i++) { + int64_t r = start + i * ((end - start - 1) / (samples > 1 ? samples - 1 : 1)); + int64_t v = agg_read_key_i64(key_cols[0], data, r); + if (dp.nullable[0] && v == dp.nulls[0]) continue; + if (v < lo) lo = v; + if (v > hi) hi = v; + } + if (lo <= hi) touched_slots += (double)((uint64_t)hi - (uint64_t)lo) + 1; + } + if (touched_slots * (block + sizeof(int64_t) + 1) > scatter_budget / 2) + dense_par_ok = false; + } + route_stats.dense_worker_budget = dp.ok && !dense_par_ok; + if (route_stats.nullable_key) ray_profile_tick("group: nullable key"); + if (route_stats.dense_worker_budget) ray_profile_tick("group: dense worker budget exceeded"); /* RADIX eligibility: every key an int/SYM type with no nulls (same * type-set check as agg_dense_plan). Radix takes the high-card @@ -2959,17 +4286,30 @@ static ray_t* exec_group_v2_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, case RAY_TIMESTAMP: case RAY_SYM: break; default: keys_intsym = false; } - if (kc->type != RAY_SYM && ray_vec_may_have_nulls(kc)) keys_intsym = false; + /* Signed null sentinels are distinct canonical radix keys. */ } if (dense_par_ok) { - ray_t* r = exec_group_v2_parallel_dense(g, op, tbl, key_cols, key_syms, ext, nrows, pool, &dp, + /* Small domains can afford more independent source tasks. This + * lets work stealing absorb uneven worker throughput without + * multiplying large state slabs or retaining prior query state. */ + uint32_t extra_tasks = dense_workers * 4; + if (extra_tasks > RAY_POOL_INIT_TASKS) extra_tasks = RAY_POOL_INIT_TASKS; + if (dense_workers > 1 && extra_tasks * slab_bytes <= scatter_budget / 8 && + (extra_tasks + 1.0) * slab_bytes + (double)eff_n * sizeof(uint32_t) <= dense_budget) + dense_workers = extra_tasks; + route_stats.dense_tasks = dense_workers; + route_stats.dense_local_slots = 0; + for (uint32_t w = 0; w < dense_workers; w++) + route_stats.dense_local_slots += dp.total_slots; + agg_route_record(AGG_ROUTE_V2_DENSE); + ray_t* r = exec_group_v2_parallel_dense(g, op, tbl, key_cols, key_syms, ext, nrows, pool, &dp, dense_workers, AGG_DENSE_TASK_LOCAL, sel, sel_prefix, n_sel); agg_vo_free(&vo); scratch_free(kc_hdr); return r; } if (keys_intsym) { - /* Sparse integer/SYM ranges use radix deterministically. No sampled - * cardinality or cache-size crossover is baked into routing. */ + agg_route_record(AGG_ROUTE_V2_RADIX); + /* Sparse ranges and excessive dense worker traffic use radix. */ ray_t* r = exec_group_v2_parallel_radix(g, op, tbl, nrows, key_cols, key_syms, vts, off, block, sel, sel_prefix, n_sel, group_limit); @@ -2977,7 +4317,8 @@ static ray_t* exec_group_v2_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, } /* Hash fallback (F64 / STR keys): not a chunked strategy — compact. */ if (sel) AGG_RUN_COMPACT_FALLBACK(); - { ray_t* r = exec_group_v2_parallel(g, op, tbl, nrows, key_cols, key_syms, vts, off, block); + agg_route_record(AGG_ROUTE_V2_INDEXED); + { ray_t* r = agg_indexed_run(g, op, tbl, key_cols, key_syms, nrows); agg_vo_free(&vo); scratch_free(kc_hdr); return r; } } @@ -2989,6 +4330,7 @@ static ray_t* exec_group_v2_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, * count is below the parallel threshold here → small; not a perf blocker). */ if (sel) AGG_RUN_COMPACT_FALLBACK(); + agg_route_record(dense ? AGG_ROUTE_V2_SERIAL_DENSE : AGG_ROUTE_V2_SERIAL_HASH); agg_groups_t groups = {0}; int grp_rc = dense ? agg_group_keys_dense(key_cols, nrows, &dp, &groups) : agg_group_keys(key_cols, ext->n_keys, nrows, &groups); @@ -2998,7 +4340,7 @@ static ray_t* exec_group_v2_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, if (!result || RAY_IS_ERR(result)) { scratch_free(kc_hdr); agg_groups_free(&groups); return ray_error("oom", NULL); } for (uint32_t k = 0; k < ext->n_keys; k++) { - ray_t* kc = agg_gather_key_col(key_cols[k], groups.first_row, groups.ngroups); + ray_t* kc = ray_group_gather(key_cols[k], groups.first_row, groups.ngroups); if (!kc || RAY_IS_ERR(kc)) { scratch_free(kc_hdr); agg_groups_free(&groups); ray_release(result); return kc ? kc : ray_error("oom", NULL); } result = ray_table_add_col(result, key_syms[k], kc); ray_release(kc); @@ -3117,6 +4459,7 @@ static ray_t* agg_build_compact(ray_graph_t* g, ray_op_t* op, ray_t* tbl, * output order is unspecified per the v2 contract. */ ray_t* exec_group_v2(ray_graph_t* g, ray_op_t* op, ray_t* tbl, int64_t group_limit) { + if (agg_cancelled()) return ray_error("cancel", NULL); if (!g || !g->selection) return exec_group_v2_run(g, op, tbl, ray_table_nrows(tbl), NULL, NULL, 0, group_limit); @@ -3158,19 +4501,6 @@ static inline int64_t agg_read_key_i64(ray_t* col, const void* data, int64_t row } } -/* Write a finalized scalar cell into output column slot i, marking nulls. - * Shared by agg_run_one (serial) and the parallel finalize. */ -static void agg_put_cell(ray_t* out, int64_t i, ray_t* cell) { - switch (out->type) { - case RAY_F64: - ((double*)ray_data(out))[i] = cell->f64; break; - default: /* RAY_I64 (and temporal widths if they arise) */ - ((int64_t*)ray_data(out))[i] = cell->i64; break; - } - if (RAY_ATOM_IS_NULL(cell)) - ray_vec_set_null(out, i, true); /* also sets RAY_ATTR_HAS_NULLS */ -} - ray_t* agg_run_one(const agg_vtable_t* vt, ray_t* val_col, const uint32_t* gids, int64_t nrows, int64_t ngroups, int64_t kparam) { @@ -3195,13 +4525,13 @@ ray_t* agg_run_one(const agg_vtable_t* vt, ray_t* val_col, } out->len = ngroups; for (int64_t gi = 0; gi < ngroups; gi++) { - ray_t* cell = vt->finalize(states + (size_t)gi * vt->state_size, NULL, kparam); + const void* state = states + (size_t)gi * vt->state_size; if (is_list) { + ray_t* cell = vt->finalize(state, NULL, kparam); out = ray_list_set(out, gi, cell); /* retains cell */ - ray_release(cell); /* drop our local ref */ - } else { - agg_put_cell(out, gi, cell); ray_release(cell); + } else if (agg_finalize_value(vt, state, out, gi, kparam)) { + out->attrs |= RAY_ATTR_HAS_NULLS; } if (vt->destroy) vt->destroy(states + (size_t)gi * vt->state_size); } @@ -3236,9 +4566,8 @@ ray_t* agg_run_one_bin(const agg_vtable_t* vt, ray_t* x_col, ray_t* y_col, } out->len = ngroups; for (int64_t gi = 0; gi < ngroups; gi++) { - ray_t* cell = vt->finalize(states + (size_t)gi * vt->state_size, NULL, kparam); - agg_put_cell(out, gi, cell); - ray_release(cell); + if (agg_finalize_value(vt, states + (size_t)gi * vt->state_size, out, gi, kparam)) + out->attrs |= RAY_ATTR_HAS_NULLS; if (vt->destroy) vt->destroy(states + (size_t)gi * vt->state_size); } ray_free_raw(states); @@ -3252,6 +4581,11 @@ ray_t* agg_run_one_bin(const agg_vtable_t* vt, ray_t* x_col, ray_t* y_col, * first-occurrence order) so the downstream emit/assembler is unchanged. */ static int agg_group_keys_dense(ray_t** key_cols, int64_t nrows, const dense_plan_t* dp, agg_groups_t* out) { + /* Direct dense lookup needs one row pass; shared first-row IDs need four. + * Small pools cannot amortize the extra traffic and barriers. */ + if (ray_pool_par_dispatch_ok(ray_pool_get(), nrows, RAY_PARALLEL_THRESHOLD) && + ray_pool_total_workers(ray_pool_get()) >= 4) + return agg_group_keys_parallel(key_cols, dp->n_keys, nrows, dp, out); const void* data[16]; /* [16]: dense path is <=16-key by construction (agg_dense_plan * rejects wider shapes to v2's hash/radix; dp->n_keys is that <=16) */ for (uint32_t k = 0; k < dp->n_keys; k++) data[k] = ray_data(key_cols[k]); @@ -3267,7 +4601,7 @@ static int agg_group_keys_dense(ray_t** key_cols, int64_t nrows, for (int64_t r = 0; r < nrows; r++) { int64_t slot = 0; for (uint32_t k = 0; k < dp->n_keys; k++) - slot += (agg_read_key_i64(key_cols[k], data[k], r) - dp->mins[k]) * dp->strides[k]; + slot += agg_dense_component(dp, k, agg_read_key_i64(key_cols[k], data[k], r)) * dp->strides[k]; /* slot is provably in [0,total_slots): each key in [min_k,max_k] so * (key-min) in [0,range_k), and the composite is a mixed-radix index * < total_slots (dp->ok from the same prescan). */ @@ -3289,7 +4623,79 @@ static int agg_group_keys_dense(ray_t** key_cols, int64_t nrows, * keys hash/compare their int64 cell; STR keys hash/compare their bytes. The * per-key type branch is invariant across rows (predicted), so the all-int/SYM * path is unaffected. */ -static inline uint64_t agg_key_hash_at(ray_t* col, const void* data, int64_t r) { +static uint64_t agg_float_key(ray_t* col, const void* data, int64_t r) { + if (col->type == RAY_F32) { + float v = ((const float*)data)[r]; + if (v != v && ray_vec_may_have_nulls(col)) return UINT64_C(0x7fc00000); + if (v == 0) return 0; + uint32_t bits; memcpy(&bits, &v, sizeof(bits)); return bits; + } + double v = ((const double*)data)[r]; + if (v != v && ray_vec_may_have_nulls(col)) return UINT64_C(0x7ff8000000000000); + if (v == 0) return 0; + uint64_t bits; memcpy(&bits, &v, sizeof(bits)); return bits; +} +/* Domain/owner-aware vector comparison for nested LIST keys. Raw descriptor + * equality is unsuitable for narrow SYM vectors and separately pooled strings. */ +static bool agg_list_key_eq(ray_t* a, ray_t* b, const ray_group_sym_view_t* view) { + if (a == b) return true; + if (!a || !b) return false; + if (a->type == RAY_LIST && b->type == RAY_LIST) { + if (a->len != b->len) return false; + for (int64_t i = 0; i < a->len; i++) + if (!agg_list_key_eq(ray_list_get(a, i), ray_list_get(b, i), view)) return false; + return true; + } + if (a->type == b->type && (a->type == RAY_SYM || a->type == RAY_STR)) { + if (a->len != b->len) return false; + for (int64_t i = 0; i < a->len; i++) { + if (a->type == RAY_SYM) { + ray_t* x = ray_group_sym_read(view, ray_sym_vec_domain(a), ray_read_sym(ray_data(a), i, a->type, a->attrs)); + ray_t* y = ray_group_sym_read(view, ray_sym_vec_domain(b), ray_read_sym(ray_data(b), i, b->type, b->attrs)); + if (ray_str_cmp(x, y)) return false; + } else { + size_t nx = 0, ny = 0; + const char* x = ray_str_vec_get(a, i, &nx); + const char* y = ray_str_vec_get(b, i, &ny); + if (nx != ny || (nx && memcmp(x, y, nx))) return false; + } + } + return true; + } + return atom_eq(a, b); +} + +/* Match atom_eq's structural LIST / byte-exact typed-vector contract. */ +static uint64_t agg_list_key_hash(ray_t* value, const ray_group_sym_view_t* view) { + if (!value || ray_is_atom(value)) return ray_atom_hash(value); + if (value->type == RAY_LIST) { + uint64_t hash = ray_hash_i64(value->len); + ray_t* const* children = ray_data(value); + for (int64_t i = 0; i < value->len; i++) hash = ray_hash_combine(hash, agg_list_key_hash(children[i], view)); + return hash; + } + if (value->type == RAY_SYM || value->type == RAY_STR) { + uint64_t hash = ray_hash_i64(value->type); + for (int64_t i = 0; i < value->len; i++) { + size_t len = 0; const char* str; + if (value->type == RAY_SYM) { + ray_t* atom = ray_group_sym_read(view, ray_sym_vec_domain(value), ray_read_sym(ray_data(value), i, value->type, value->attrs)); + str = ray_str_ptr(atom); len = ray_str_len(atom); + } else str = ray_str_vec_get(value, i, &len); + hash = ray_hash_combine(hash, ray_hash_bytes(str ? str : "", len)); + } + return hash; + } + if (ray_is_vec(value)) { + size_t bytes = (size_t)value->len * ray_elem_size(value->type); + return ray_hash_combine(ray_hash_i64(value->type), ray_hash_bytes(ray_data(value), bytes)); + } + return ray_atom_hash(value); +} +static inline uint64_t agg_key_hash_at(ray_t* col, const void* data, int64_t r, const ray_group_sym_view_t* view) { + if (col->type == RAY_LIST) return agg_list_key_hash(((ray_t* const*)data)[r], view); + if (col->type == RAY_F32 || col->type == RAY_F64) return ray_hash_i64((int64_t)agg_float_key(col, data, r)); + if (col->type == RAY_GUID) return ray_hash_bytes((const char*)data + (size_t)r * 16, 16); if (col->type == RAY_STR) { size_t len = 0; const char* s = ray_str_vec_get(col, r, &len); @@ -3297,7 +4703,10 @@ static inline uint64_t agg_key_hash_at(ray_t* col, const void* data, int64_t r) } return (uint64_t)agg_read_key_i64(col, data, r); } -static inline int agg_key_eq_at(ray_t* col, const void* data, int64_t a, int64_t b) { +static inline int agg_key_eq_at(ray_t* col, const void* data, int64_t a, int64_t b, const ray_group_sym_view_t* view) { + if (col->type == RAY_LIST) return agg_list_key_eq(((ray_t* const*)data)[a], ((ray_t* const*)data)[b], view); + if (col->type == RAY_F32 || col->type == RAY_F64) return agg_float_key(col, data, a) == agg_float_key(col, data, b); + if (col->type == RAY_GUID) return memcmp((const char*)data + (size_t)a * 16, (const char*)data + (size_t)b * 16, 16) == 0; if (col->type == RAY_STR) { size_t la = 0, lb = 0; const char* sa = ray_str_vec_get(col, a, &la); @@ -3307,7 +4716,243 @@ static inline int agg_key_eq_at(ray_t* col, const void* data, int64_t a, int64_t return agg_read_key_i64(col, data, a) == agg_read_key_i64(col, data, b); } +typedef struct { + ray_t* source; + const void* data; + const int64_t* rows; + const int64_t* offsets; + const int64_t* counts; + int64_t* output; + _Atomic(bool) failed; + ray_group_sym_view_t symbols; +} agg_distinct_indexed_t; +static void agg_distinct_indexed_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_distinct_indexed_t* c = raw; + int64_t largest = 0; + for (int64_t g = start; g < end; g++) if (c->counts[g] > largest) largest = c->counts[g]; + uint64_t capacity = 16; + while (capacity < (uint64_t)largest * 2) { + if (capacity > SIZE_MAX / sizeof(int64_t) / 2) { atomic_store(&c->failed, true); return; } + capacity *= 2; + } + int64_t* entries = ray_alloc_raw((size_t)capacity * sizeof(int64_t)); + if (!entries) { atomic_store(&c->failed, true); return; } + for (int64_t g = start; g < end && !agg_cancelled(); g++) { + uint64_t size = 16; + while (size < (uint64_t)c->counts[g] * 2) size *= 2; + memset(entries, 0, (size_t)size * sizeof(int64_t)); + int64_t distinct = 0; + for (int64_t i = 0; i < c->counts[g]; i++) { + int64_t row = c->rows[c->offsets[g] + i]; + uint64_t slot = agg_key_hash_at(c->source, c->data, row, &c->symbols) & (size - 1); + while (entries[slot] && !agg_key_eq_at(c->source, c->data, row, entries[slot] - 1, &c->symbols)) + slot = (slot + 1) & (size - 1); + if (!entries[slot]) { entries[slot] = row + 1; distinct++; } + } + c->output[g] = distinct; + } + ray_free_raw(entries); +} +ray_t* agg_count_distinct_indexed(ray_t* src, const int64_t* rows, + const int64_t* offsets, const int64_t* counts, int64_t groups) { + if (src->type != RAY_STR && src->type != RAY_GUID && src->type != RAY_LIST) return NULL; + ray_t* out = ray_vec_new(RAY_I64, groups); + if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL); + out->len = groups; + agg_distinct_indexed_t c = {src, ray_data(src), rows, offsets, counts, ray_data(out), false, {0}}; + if (src->type == RAY_LIST) ray_sym_strings_borrow(&c.symbols.strings, &c.symbols.count); + ray_group_dispatch(agg_distinct_indexed_run, &c, counts, groups); + if (agg_cancelled() || atomic_load(&c.failed)) { + ray_release(out); return ray_error(agg_cancelled() ? "cancel" : "oom", NULL); + } + return out; +} + +/* One shared key directory, with earliest source row as the representative. + * Concurrent insertion never changes a key's identity: competing rows compare + * immutable input columns, then atomically lower its representative. Separate + * barriers assign deterministic first-occurrence group IDs and remap rows. + * Memory is O(rows + directory), independent of the number of workers. */ +typedef struct { + ray_t** keys; + const void** data; + uint32_t nkeys, tasks, init_tasks; + int64_t rows, capacity, init_pages; + const dense_plan_t* dense; + void* first; + bool narrow; + uint8_t* unique; + int64_t* offsets; + agg_groups_t* out; + ray_group_sym_view_t symbols; +} agg_key_build_t; + +/* Coarse aligned ranges avoid concurrent first writes to common 2 MiB + * huge pages. Small directories initialize inline; large ones retain + * enough independent ranges to use the worker pool. */ +enum { AGG_DIRECTORY_INIT_BYTES = 2 * 1024 * 1024 }; +static void agg_key_directory_init(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_key_build_t* c = raw; +#define KEY_DIRECTORY_INIT(TYPE, EMPTY) do { \ + _Atomic TYPE* first_slots = c->first; \ + uint64_t offset = (uintptr_t)c->first & (AGG_DIRECTORY_INIT_BYTES - 1); \ + for (int64_t task = start; task < end; task++) { \ + uint64_t lo = (uint64_t)(c->init_pages * task / c->init_tasks) * AGG_DIRECTORY_INIT_BYTES; \ + uint64_t hi = (uint64_t)(c->init_pages * (task + 1) / c->init_tasks) * AGG_DIRECTORY_INIT_BYTES; \ + int64_t begin = lo < offset ? 0 : (int64_t)((lo - offset) / sizeof(*first_slots)); \ + int64_t limit = (int64_t)((hi - offset) / sizeof(*first_slots)); \ + if (limit > c->capacity) limit = c->capacity; \ + for (int64_t i = begin; i < limit; i++) atomic_init(&first_slots[i], EMPTY); \ + } \ + } while (0) + if (c->narrow) KEY_DIRECTORY_INIT(int32_t, INT32_MAX); + else KEY_DIRECTORY_INIT(int64_t, INT64_MAX); +#undef KEY_DIRECTORY_INIT +} +static void agg_key_directory_insert(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_key_build_t* c = raw; +#define KEY_DIRECTORY_INSERT(TYPE, EMPTY) do { \ + _Atomic TYPE* first_slots = c->first; \ + for (int64_t r = start; r < end; r++) { \ + uint64_t slot = 0; \ + if (c->dense) { \ + for (uint32_t k = 0; k < c->nkeys; k++) \ + slot += agg_dense_component(c->dense, k, agg_read_key_i64(c->keys[k], c->data[k], r)) \ + * c->dense->strides[k]; \ + } else { \ + uint64_t hash = UINT64_C(1469598103934665603); \ + for (uint32_t k = 0; k < c->nkeys; k++) { \ + hash ^= agg_key_hash_at(c->keys[k], c->data[k], r, &c->symbols); \ + hash *= UINT64_C(1099511628211); \ + } \ + slot = hash & (c->capacity - 1); \ + } \ + for (;;) { \ + TYPE first = atomic_load_explicit(&first_slots[slot], memory_order_relaxed); \ + if (!c->dense && first != EMPTY) { \ + bool equal = true; \ + for (uint32_t k = 0; k < c->nkeys && equal; k++) \ + equal = agg_key_eq_at(c->keys[k], c->data[k], r, first, &c->symbols); \ + if (!equal) { slot = (slot + 1) & (c->capacity - 1); continue; } \ + } \ + if (r < first && !atomic_compare_exchange_weak_explicit(&first_slots[slot], &first, (TYPE)r, \ + memory_order_relaxed, memory_order_relaxed)) continue; \ + c->out->gids[r] = (uint32_t)slot; \ + break; \ + } \ + } \ + } while (0) + if (c->narrow) KEY_DIRECTORY_INSERT(int32_t, INT32_MAX); + else KEY_DIRECTORY_INSERT(int64_t, INT64_MAX); +#undef KEY_DIRECTORY_INSERT +} +static void agg_key_directory_count(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_key_build_t* c = raw; +#define KEY_DIRECTORY_COUNT(TYPE, EMPTY) do { \ + _Atomic TYPE* first_slots = c->first; \ + for (int64_t task = start; task < end; task++) { \ + int64_t begin = c->rows / c->tasks * task; \ + int64_t limit = task + 1 == c->tasks ? c->rows : c->rows / c->tasks * (task + 1); \ + int64_t count = 0; \ + for (int64_t r = begin; r < limit; r++) { \ + bool first = atomic_load_explicit(&first_slots[c->out->gids[r]], memory_order_relaxed) == r; \ + c->unique[r] = first; \ + count += first; \ + } \ + c->offsets[task + 1] = count; \ + } \ + } while (0) + if (c->narrow) KEY_DIRECTORY_COUNT(int32_t, INT32_MAX); + else KEY_DIRECTORY_COUNT(int64_t, INT64_MAX); +#undef KEY_DIRECTORY_COUNT +} +static void agg_key_directory_compact(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_key_build_t* c = raw; +#define KEY_DIRECTORY_COMPACT(TYPE, EMPTY) do { \ + _Atomic TYPE* first_slots = c->first; \ + for (int64_t task = start; task < end; task++) { \ + int64_t begin = c->rows / c->tasks * task; \ + int64_t limit = task + 1 == c->tasks ? c->rows : c->rows / c->tasks * (task + 1); \ + int64_t gid = c->offsets[task]; \ + for (int64_t r = begin; r < limit; r++) if (c->unique[r]) { \ + c->out->first_row[gid] = r; \ + atomic_store_explicit(&first_slots[c->out->gids[r]], (TYPE)gid++, memory_order_relaxed); \ + } \ + } \ + } while (0) + if (c->narrow) KEY_DIRECTORY_COMPACT(int32_t, INT32_MAX); + else KEY_DIRECTORY_COMPACT(int64_t, INT64_MAX); +#undef KEY_DIRECTORY_COMPACT +} +static void agg_key_directory_remap(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; agg_key_build_t* c = raw; +#define KEY_DIRECTORY_REMAP(TYPE, EMPTY) do { \ + _Atomic TYPE* first_slots = c->first; \ + for (int64_t r = start; r < end; r++) \ + c->out->gids[r] = (uint32_t)atomic_load_explicit(&first_slots[c->out->gids[r]], memory_order_relaxed); \ + } while (0) + if (c->narrow) KEY_DIRECTORY_REMAP(int32_t, INT32_MAX); + else KEY_DIRECTORY_REMAP(int64_t, INT64_MAX); +#undef KEY_DIRECTORY_REMAP +} +static int agg_group_keys_parallel(ray_t** keys, uint32_t nkeys, int64_t rows, + const dense_plan_t* dp, agg_groups_t* out) { + ray_pool_t* pool = ray_pool_get(); + uint32_t tasks = ray_pool_total_workers(pool) * 4; + if (tasks > RAY_POOL_INIT_TASKS) tasks = RAY_POOL_INIT_TASKS; + int64_t cap = dp ? dp->total_slots : 16; + if (!dp) while (cap < rows * 2) cap *= 2; + agg_key_build_t c = {.keys = keys, .nkeys = nkeys, .tasks = tasks, .rows = rows, + .capacity = cap, .dense = dp, .out = out, .narrow = rows <= INT32_MAX}; + for (uint32_t k = 0; k < nkeys; k++) if (keys[k]->type == RAY_LIST) { + ray_sym_strings_borrow(&c.symbols.strings, &c.symbols.count); break; + } + c.data = ray_alloc_raw((size_t)nkeys * sizeof(*c.data)); + /* Row and group ids below INT32_MAX leave its maximum value available + * as the empty sentinel. Larger dense inputs retain full-width entries. */ + c.first = ray_alloc_raw((size_t)cap * (c.narrow ? sizeof(_Atomic(int32_t)) : sizeof(_Atomic(int64_t)))); + c.unique = ray_alloc_raw((size_t)rows); + c.offsets = ray_calloc_raw((tasks + 1) * sizeof(*c.offsets)); + out->gids = ray_alloc_raw((size_t)rows * sizeof(*out->gids)); + out->first_row = NULL; + int rc = -1; + if (!c.data || !c.first || !c.unique || !c.offsets || !out->gids) goto done; + for (uint32_t k = 0; k < nkeys; k++) c.data[k] = ray_data(keys[k]); + ray_profile_tick("directory: allocated"); + size_t entry_size = c.narrow ? sizeof(_Atomic(int32_t)) : sizeof(_Atomic(int64_t)); + uint64_t init_bytes = ((uintptr_t)c.first & (AGG_DIRECTORY_INIT_BYTES - 1)) + (uint64_t)cap * entry_size; + c.init_pages = (init_bytes + AGG_DIRECTORY_INIT_BYTES - 1) / AGG_DIRECTORY_INIT_BYTES; + c.init_tasks = c.init_pages < tasks ? (uint32_t)c.init_pages : tasks; + if (c.init_tasks == 1) agg_key_directory_init(&c, 0, 0, 1); + else ray_pool_dispatch_n(pool, agg_key_directory_init, &c, c.init_tasks); + ray_profile_tick("directory: initialized"); + ray_pool_dispatch(pool, agg_key_directory_insert, &c, rows); + ray_profile_tick("directory: inserted keys"); + if (agg_cancelled()) goto done; + ray_pool_dispatch_n(pool, agg_key_directory_count, &c, tasks); + ray_profile_tick("directory: counted groups"); + if (agg_cancelled()) goto done; + for (uint32_t t = 0; t < tasks; t++) c.offsets[t + 1] += c.offsets[t]; + out->ngroups = c.offsets[tasks]; + /* Representatives need one entry per group, not per input row. Delay + * allocation until the prefix counts give the exact output capacity. */ + out->first_row = ray_alloc_raw((size_t)(out->ngroups ? out->ngroups : 1) * sizeof(*out->first_row)); + if (!out->first_row) goto done; + ray_pool_dispatch_n(pool, agg_key_directory_compact, &c, tasks); + ray_profile_tick("directory: compacted groups"); + ray_pool_dispatch(pool, agg_key_directory_remap, &c, rows); + ray_profile_tick("directory: remapped rows"); + if (!agg_cancelled()) rc = 0; +done: + ray_free_raw(c.data); ray_free_raw(c.first); ray_free_raw(c.unique); ray_free_raw(c.offsets); + if (rc) agg_groups_free(out); + return rc; +} + int agg_group_keys(ray_t** key_cols, uint32_t n_keys, int64_t nrows, agg_groups_t* out) { + if (nrows < 0 || nrows > INT32_MAX) return -1; + if (ray_pool_par_dispatch_ok(ray_pool_get(), nrows, RAY_PARALLEL_THRESHOLD)) + return agg_group_keys_parallel(key_cols, n_keys, nrows, NULL, out); /* Unbounded keys: cut-3 lifted both admission gates (the GROUP path and the * keys-only DISTINCT path via agg_select_distinct), so the key-data pointer * table is an exact carve, not a fixed [16]. */ @@ -3316,6 +4961,11 @@ int agg_group_keys(ray_t** key_cols, uint32_t n_keys, int64_t nrows, agg_groups_ if (!data) return -1; for (uint32_t k = 0; k < n_keys; k++) data[k] = ray_data(key_cols[k]); + ray_group_sym_view_t symbols = {0}; + for (uint32_t k = 0; k < n_keys; k++) if (key_cols[k]->type == RAY_LIST) { + ray_sym_strings_borrow(&symbols.strings, &symbols.count); break; + } + /* hash table capacity: next pow2 >= 2*nrows, min 16 */ int64_t cap = 16; while (cap < nrows * 2) cap <<= 1; @@ -3335,7 +4985,7 @@ int agg_group_keys(ray_t** key_cols, uint32_t n_keys, int64_t nrows, agg_groups_ for (int64_t r = 0; r < nrows; r++) { uint64_t h = 1469598103934665603ULL; for (uint32_t k = 0; k < n_keys; k++) { - h ^= agg_key_hash_at(key_cols[k], data[k], r); h *= 1099511628211ULL; + h ^= agg_key_hash_at(key_cols[k], data[k], r, &symbols); h *= 1099511628211ULL; } uint64_t slot = h & mask; for (;;) { @@ -3350,7 +5000,7 @@ int agg_group_keys(ray_t** key_cols, uint32_t n_keys, int64_t nrows, agg_groups_ int64_t fr = out->first_row[gptr]; int eq = 1; for (uint32_t k = 0; k < n_keys; k++) { - if (!agg_key_eq_at(key_cols[k], data[k], r, fr)) { eq = 0; break; } + if (!agg_key_eq_at(key_cols[k], data[k], r, fr, &symbols)) { eq = 0; break; } } if (eq) { out->gids[r] = (uint32_t)gptr; break; } slot = (slot + 1) & mask; /* linear probe */ @@ -3370,7 +5020,7 @@ void agg_groups_free(agg_groups_t* out) { /* Gather one column's first-of-group values (first_row[gi]) by type: STR via the * string-vec path (null-preserving), LIST via retained borrows, everything else - * (fixed-width + SYM) via agg_gather_key_col — which adopts a SYM column's source + * (fixed-width + SYM) via ray_group_gather — which adopts a SYM column's source * domain, so SYM columns are NEVER interned into the global table. Returns a new * column of n rows, or an error/NULL on failure. */ static ray_t* agg_gather_col_at(ray_t* sc, const int64_t* first_row, int64_t n) { @@ -3403,7 +5053,7 @@ static ray_t* agg_gather_col_at(ray_t* sc, const int64_t* first_row, int64_t n) for (int64_t gi = 0; gi < n; gi++) { dout[gi] = sitems[first_row[gi]]; ray_retain(dout[gi]); } return dst; } - return agg_gather_key_col(sc, first_row, n); + return ray_group_gather(sc, first_row, n); } /* Multi-key `select {by: {keys}}` with NO aggregates. Group on each key diff --git a/src/ops/agg_engine.h b/src/ops/agg_engine.h index 69058b471..7cd84d755 100644 --- a/src/ops/agg_engine.h +++ b/src/ops/agg_engine.h @@ -7,9 +7,70 @@ #include "ops/agg_acc.h" /* agg_vtable_t */ /* Test/feature knob: route OP_GROUP through the v2 engine when it can handle - * the query (see agg_v2_can_handle). Default false → zero behavioral change. */ + * the query (see agg_v2_can_handle). Enabled by default. */ extern bool ray_agg_engine_v2; +/* Admission is pure: inspecting a plan must not change execution diagnostics. */ +typedef enum { + AGG_V2_ADMITTED, + AGG_V2_SHAPE, + AGG_V2_KEY_EXPRESSION, + AGG_V2_KEY_TYPE, + AGG_V2_AGG_EXPRESSION, + AGG_V2_AGG_TYPE, + AGG_V2_BUFFERED, + AGG_V2_PARAMETER, + AGG_V2_DISABLED, + AGG_V2_EMIT_FILTER, + AGG_V2_PARALLEL_WIDE, +} agg_v2_reason_t; + +agg_v2_reason_t agg_v2_admission(ray_graph_t* g, ray_op_t* op, ray_t* tbl); + +typedef enum { + AGG_ROUTE_NONE, + AGG_ROUTE_LEGACY, + AGG_ROUTE_SLICES, + AGG_ROUTE_PARTED, + AGG_ROUTE_V2_SERIAL_DENSE, + AGG_ROUTE_V2_SERIAL_HASH, + AGG_ROUTE_V2_DENSE, + AGG_ROUTE_V2_RADIX, + AGG_ROUTE_V2_HASH, + AGG_ROUTE_V2_SMALLHASH, + AGG_ROUTE_V2_INDEXED, + AGG_ROUTE_COUNT, +} agg_route_t; + +typedef enum { + AGG_DENSE_NONE, + AGG_DENSE_TASK_LOCAL, + AGG_DENSE_PARTITIONED, + AGG_DENSE_SHARED, +} agg_dense_strategy_t; + +/* Per-calling-thread dispatch counts since reset, not a whole-query trace. + * Nested/partitioned groups may record multiple routes. Incremented only at + * dispatch boundaries, never inside worker row loops. A count records an + * attempted dispatch (which may subsequently fail), not successful completion. + * last_v2_reason describes the most recent legacy/v2 admission decision. */ +typedef struct { + uint64_t routes[AGG_ROUTE_COUNT]; + agg_v2_reason_t last_v2_reason; + bool nullable_key; /* last v2 run: non-SYM key may contain nulls */ + bool dense_plan_available; /* last v2 run: bounded dense range exists */ + bool dense_worker_budget; /* worker allocation or sampled traffic budget exceeded */ + agg_dense_strategy_t dense_strategy; + uint64_t dense_local_slots; /* allocated group-state slots, including partials */ + uint32_t dense_tasks; /* local/partition tasks; worker count for shared updates */ + uint64_t key_domain_evals; /* computed keys evaluated once per distinct symbol */ +} agg_route_stats_t; +void agg_route_reset(void); +void agg_route_note_key_domain(void); +agg_route_stats_t agg_route_stats(void); +void agg_route_record(agg_route_t route); +void agg_route_reason(agg_v2_reason_t reason); + /* True iff the v2 engine fully supports this group node over this table. * Conservative: any uncertainty → false → caller uses the existing engine. */ bool agg_v2_can_handle(ray_graph_t* g, ray_op_t* op, ray_t* tbl); @@ -29,8 +90,9 @@ typedef struct { int64_t ngroups; } agg_groups_t; -/* Multi-key grouping, key count unbounded. Reads each key as an int64 - * (intern id for SYM) and hashes the tuple. Assigns gids incrementally on first sight → gid +/* Multi-key grouping, key count unbounded. Uses native integer/SYM, + * canonical float, byte/string, and structural LIST hash/equality. Assigns + * gids incrementally on first sight → gid * order == first-occurrence order; first_row[gid] records the row where the * group first appeared. Returns 0 on success (caller releases out via * agg_groups_free()), -1 on allocation failure. @@ -38,6 +100,12 @@ typedef struct { * (the GROUP path and the keys-only DISTINCT path) and the fixed data[16] * inside became an exact carve, so any key count groups correctly. */ int agg_group_keys(ray_t** key_cols, uint32_t n_keys, int64_t nrows, agg_groups_t* out); +/* Exact wide-value distinct counts over an existing stable group index. */ +ray_t* agg_count_distinct_indexed(ray_t* src, const int64_t* rows, + const int64_t* offsets, const int64_t* counts, int64_t groups); +/* Large flat grouping: return first-occurrence keys and stable index vectors, + * or NULL when the existing serial implementation should handle the input. */ +ray_t* agg_group_indices(ray_t* source); /* Release the buffers an agg_groups_t holds (buddy-backed, NOT libc malloc — so * callers must use this, not free()). Idempotent; NULLs the pointers. */ @@ -64,6 +132,9 @@ ray_t* agg_select_distinct(ray_t* tbl, ray_t** key_cols, const int64_t* key_syms ray_t* agg_run_one(const agg_vtable_t* vt, ray_t* val_col, const uint32_t* gids, int64_t nrows, int64_t ngroups, int64_t kparam); +ray_t* agg_run_one_bin(const agg_vtable_t* vt, ray_t* x_col, ray_t* y_col, + const uint32_t* gids, int64_t nrows, int64_t ngroups, + int64_t kparam); /* ── Dense grouping eligibility selector (compact-range int/SYM keys) ── * When dense applies, a group id is the packed key offset (O(1) direct index) @@ -72,6 +143,8 @@ ray_t* agg_run_one(const agg_vtable_t* vt, ray_t* val_col, typedef struct { bool ok; uint32_t n_keys; /* mirrors ext->n_keys' width; value stays 1..16 (dense self-limit) */ + bool nullable[16]; + int64_t nulls[16]; int64_t mins[16]; /* [16]: dense direct-index routing self-limits to <=16 keys (agg_dense_plan) */ int64_t ranges[16]; /* [16]: dense direct-index routing self-limits to <=16 keys (agg_dense_plan) */ int64_t strides[16]; /* [16]: dense self-limit <=16; composite packing: slot = sum_k (key_k - min_k)*strides[k] */ @@ -79,7 +152,7 @@ typedef struct { } dense_plan_t; /* Decide if dense grouping applies to (key_cols, aggs). Eligible iff: - * - every key type in {I64,I32,I16,U8,BOOL,DATE,TIME,TIMESTAMP,SYM} with no nullable non-SYM keys + * - every key type in {I64,I32,I16,U8,BOOL,DATE,TIME,TIMESTAMP,SYM} with a dedicated slot for nullable keys * - product of per-key ranges is no larger than the contributing row count * (so dense state is O(input), never controlled by a machine-size budget) * Does one min/max prescan over the key columns. Sets out->ok accordingly. diff --git a/src/ops/agg_stream.c b/src/ops/agg_stream.c index c53cd15d9..8912d1860 100644 --- a/src/ops/agg_stream.c +++ b/src/ops/agg_stream.c @@ -10,6 +10,19 @@ #include /* realloc/free for the buffered median accumulator */ #include /* memcpy for the top_n/bot_n native buffer */ +/* Registry dispatch already fixes each native kernel's input representation. + * Select its sentinel reader at compile time, preserving the validity view's + * base pointer without repeating a runtime type switch for every row. */ +static inline bool agg_live_i16(const void* p, int64_t i) { return ((const int16_t*)p)[i] != NULL_I16; } +static inline bool agg_live_i32(const void* p, int64_t i) { return ((const int32_t*)p)[i] != NULL_I32; } +static inline bool agg_live_i64(const void* p, int64_t i) { return ((const int64_t*)p)[i] != NULL_I64; } +static inline bool agg_live_f32(const void* p, int64_t i) { float v = ((const float*)p)[i]; return v == v; } +static inline bool agg_live_f64(const void* p, int64_t i) { double v = ((const double*)p)[i]; return v == v; } +static inline bool agg_live_u8(const void* p, int64_t i) { (void)p; (void)i; return true; } +#define AGG_NATIVE_LIVE(data, validity, row) _Generic(*(data), \ + int16_t: agg_live_i16, int32_t: agg_live_i32, int64_t: agg_live_i64, \ + float: agg_live_f32, double: agg_live_f64, uint8_t: agg_live_u8)((validity)->base, row) + /* No-null fast path. has_nulls is loop-invariant, but the compiler does NOT * reliably hoist ray_valid_at out of the per-row update — it shows up as ~12% of * a sum group-by in profiling. Branch on it ONCE: the common non-null column @@ -21,12 +34,25 @@ for (int64_t i = 0; i < (n); i++) { BODY; } \ } else { \ for (int64_t i = 0; i < (n); i++) { \ - if (!ray_valid_at((valid), i)) continue; \ + if (!AGG_NATIVE_LIVE(d, (valid), i)) continue; \ BODY; \ } \ } \ } while (0) +/* Both output APIs share one primitive result calculation. Streaming group + * emission writes that payload directly, avoiding one allocation per group. */ +#define AGG_SCALAR_FINAL(NAME, TYPE, BOX, IS_NULL) \ +static bool NAME##_value(const void* state, void* dst) { \ + TYPE value = NAME##_result(state); \ + memcpy(dst, &value, sizeof(value)); \ + return (IS_NULL); \ +} \ +static ray_t* NAME(const void* state, acc_arena_t* arena, int64_t param) { \ + (void)arena; (void)param; \ + return BOX(NAME##_result(state)); \ +} + /* ---- sum, I64 -------------------------------------------------------- */ typedef struct { int64_t sum; } sum_i64_state; @@ -49,14 +75,15 @@ static void sum_i64_merge(void* dst, const void* src, acc_arena_t* a) { + (uint64_t)((const sum_i64_state*)src)->sum); } -static ray_t* sum_i64_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; return ray_i64(((const sum_i64_state*)s)->sum); +static int64_t sum_i64_final_result(const void* s) { + return ((const sum_i64_state*)s)->sum; } +AGG_SCALAR_FINAL(sum_i64_final, int64_t, ray_i64, value == NULL_I64) static const agg_vtable_t SUM_I64 = { .state_size = sizeof(sum_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, .init = sum_i64_init, .update_batch = sum_i64_update, - .merge = sum_i64_merge, .finalize = sum_i64_final, + .merge = sum_i64_merge, .finalize = sum_i64_final, .finalize_value = sum_i64_final_value, }; /* ---- count (type-agnostic over live rows) ---------------------------- */ @@ -65,24 +92,75 @@ static void count_init(void* s) { ((count_state*)s)->n = 0; } static void count_update(void* base, size_t stride, const uint32_t* gids, const void* vals, const ray_valid_t* valid, int64_t n, acc_arena_t* a) { - (void)vals; (void)a; - AGG_UPDATE_LOOP(valid, n, - ((count_state*)((char*)base + (size_t)gids[i]*stride))->n++); + (void)vals; (void)a; (void)valid; + for (int64_t i = 0; i < n; i++) + ((count_state*)((char*)base + (size_t)gids[i]*stride))->n++; } static void count_merge(void* d, const void* s, acc_arena_t* a) { (void)a; ((count_state*)d)->n += ((const count_state*)s)->n; } -static ray_t* count_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; return ray_i64(((const count_state*)s)->n); +static int64_t count_final_result(const void* s) { + return ((const count_state*)s)->n; } +AGG_SCALAR_FINAL(count_final, int64_t, ray_i64, value == NULL_I64) static const agg_vtable_t COUNT_ANY = { .state_size = sizeof(count_state), .kind = ACC_STREAMING, .out_type = RAY_I64, .init = count_init, .update_batch = count_update, - .merge = count_merge, .finalize = count_final, + .merge = count_merge, .finalize = count_final, .finalize_value = count_final_value, }; /* ---- min / max I64 (empty group → typed null) ------------------------ */ typedef struct { int64_t v; int64_t cnt; } ext_i64_state; +/* Narrow native extrema need a value and validity, not a 64-bit count. */ +typedef struct { _Alignas(8) int32_t v; uint32_t seen; } ext_i32_state; +_Static_assert(sizeof(ext_i32_state) == 8, "shared extrema require one aligned 64-bit state"); +static void min_i32_init(void* s) { *(ext_i32_state*)s = (ext_i32_state){ INT32_MAX, 0 }; } +static void max_i32_init(void* s) { *(ext_i32_state*)s = (ext_i32_state){ INT32_MIN, 0 }; } +static void min_i32_merge(void* d, const void* s, acc_arena_t* a) { + (void)a; const ext_i32_state* src = s; ext_i32_state* dst = d; + if (src->seen && src->v < dst->v) dst->v = src->v; + dst->seen |= src->seen; +} +static void max_i32_merge(void* d, const void* s, acc_arena_t* a) { + (void)a; const ext_i32_state* src = s; ext_i32_state* dst = d; + if (src->seen && src->v > dst->v) dst->v = src->v; + dst->seen |= src->seen; +} +/* Extrema only write when a value improves the result. Repeated hot keys + * therefore become shared reads once their current bound is established. */ +#if ATOMIC_LLONG_LOCK_FREE == 2 +#define NARROW_SHARED(NAME, T, CMP) \ +static void NAME(void* base, size_t stride, const uint32_t* gids, \ + const void* vals, const ray_valid_t* valid, int64_t n) { \ + const T* d = vals; \ + AGG_UPDATE_LOOP(valid, n, { \ + ext_i32_state* state = (ext_i32_state*)((char*)base + (size_t)gids[i] * stride); \ + ext_i32_state old; \ + __atomic_load(state, &old, __ATOMIC_RELAXED); \ + while (!old.seen || d[i] CMP old.v) { \ + ext_i32_state next; next.v = d[i]; next.seen = 1; \ + if (__atomic_compare_exchange(state, &old, &next, true, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) break; \ + } \ + }); \ +} +NARROW_SHARED(min_bool_shared, uint8_t, <) +NARROW_SHARED(max_bool_shared, uint8_t, >) +NARROW_SHARED(min_u8_shared, uint8_t, <) +NARROW_SHARED(max_u8_shared, uint8_t, >) +NARROW_SHARED(min_i16_shared, int16_t, <) +NARROW_SHARED(max_i16_shared, int16_t, >) +NARROW_SHARED(min_i32_shared, int32_t, <) +NARROW_SHARED(max_i32_shared, int32_t, >) +NARROW_SHARED(min_date_shared, int32_t, <) +NARROW_SHARED(max_date_shared, int32_t, >) +NARROW_SHARED(min_time_shared, int32_t, <) +NARROW_SHARED(max_time_shared, int32_t, >) +#undef NARROW_SHARED +#define SHARED_UPDATE(NAME) .update_shared = NAME, +#else +#define SHARED_UPDATE(NAME) +#endif + static void min_i64_init(void* s) { ((ext_i64_state*)s)->v = INT64_MAX; ((ext_i64_state*)s)->cnt = 0; } static void max_i64_init(void* s) { ((ext_i64_state*)s)->v = INT64_MIN; ((ext_i64_state*)s)->cnt = 0; } static void min_i64_update(void* base, size_t stride, const uint32_t* gids, @@ -115,19 +193,20 @@ static void max_i64_merge(void* d, const void* s, acc_arena_t* a) { if (src->cnt && src->v > dst->v) { dst->v = src->v; } dst->cnt += src->cnt; } -static ray_t* ext_i64_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const ext_i64_state* st = s; - return st->cnt ? ray_i64(st->v) : ray_typed_null(-RAY_I64); +static int64_t ext_i64_final_result(const void* s) { + const ext_i64_state* st = s; + return st->cnt ? st->v : NULL_I64; } +AGG_SCALAR_FINAL(ext_i64_final, int64_t, ray_i64, value == NULL_I64) static const agg_vtable_t MIN_I64 = { .state_size = sizeof(ext_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, .init = min_i64_init, .update_batch = min_i64_update, - .merge = min_i64_merge, .finalize = ext_i64_final, + .merge = min_i64_merge, .finalize = ext_i64_final, .finalize_value = ext_i64_final_value, }; static const agg_vtable_t MAX_I64 = { .state_size = sizeof(ext_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, .init = max_i64_init, .update_batch = max_i64_update, - .merge = max_i64_merge, .finalize = ext_i64_final, + .merge = max_i64_merge, .finalize = ext_i64_final, .finalize_value = ext_i64_final_value, }; /* ---- sum, F64 -------------------------------------------------------- */ @@ -143,13 +222,14 @@ static void sum_f64_update(void* base, size_t stride, const uint32_t* gids, static void sum_f64_merge(void* d, const void* s, acc_arena_t* a) { (void)a; ((sum_f64_state*)d)->sum += ((const sum_f64_state*)s)->sum; } -static ray_t* sum_f64_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; return ray_f64(ray_f64_fin(((const sum_f64_state*)s)->sum)); +static double sum_f64_final_result(const void* s) { + return ray_f64_fin(((const sum_f64_state*)s)->sum); } +AGG_SCALAR_FINAL(sum_f64_final, double, ray_f64, value != value) static const agg_vtable_t SUM_F64 = { .state_size = sizeof(sum_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = sum_f64_init, .update_batch = sum_f64_update, - .merge = sum_f64_merge, .finalize = sum_f64_final, + .merge = sum_f64_merge, .finalize = sum_f64_final, .finalize_value = sum_f64_final_value, }; /* ---- min / max F64 (empty group → typed null) ------------------------ */ @@ -186,21 +266,22 @@ static void max_f64_merge(void* d, const void* s, acc_arena_t* a) { if (src->cnt && src->v > dst->v) { dst->v = src->v; } dst->cnt += src->cnt; } -static ray_t* ext_f64_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const ext_f64_state* st = s; +static double ext_f64_final_result(const void* s) { + const ext_f64_state* st = s; /* min/max of finite inputs is finite, but ray_f64_fin guards against an * ±Inf that may have leaked in from a not-yet-canonicalized source. */ - return st->cnt ? ray_f64(ray_f64_fin(st->v)) : ray_typed_null(-RAY_F64); + return st->cnt ? ray_f64_fin(st->v) : NULL_F64; } +AGG_SCALAR_FINAL(ext_f64_final, double, ray_f64, value != value) static const agg_vtable_t MIN_F64 = { .state_size = sizeof(ext_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = min_f64_init, .update_batch = min_f64_update, - .merge = min_f64_merge, .finalize = ext_f64_final, + .merge = min_f64_merge, .finalize = ext_f64_final, .finalize_value = ext_f64_final_value, }; static const agg_vtable_t MAX_F64 = { .state_size = sizeof(ext_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = max_f64_init, .update_batch = max_f64_update, - .merge = max_f64_merge, .finalize = ext_f64_final, + .merge = max_f64_merge, .finalize = ext_f64_final, .finalize_value = ext_f64_final_value, }; /* ---- avg, F64 -------------------------------------------------------- */ @@ -219,140 +300,192 @@ static void avg_f64_merge(void* d, const void* s, acc_arena_t* a) { (void)a; ((avg_f64_state*)d)->sum += ((const avg_f64_state*)s)->sum; ((avg_f64_state*)d)->cnt += ((const avg_f64_state*)s)->cnt; } -static ray_t* avg_f64_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const avg_f64_state* st = s; - return st->cnt ? ray_f64(ray_f64_fin(st->sum / (double)st->cnt)) : ray_typed_null(-RAY_F64); +static double avg_f64_final_result(const void* s) { + const avg_f64_state* st = s; + return st->cnt ? ray_f64_fin(st->sum / (double)st->cnt) : NULL_F64; } +AGG_SCALAR_FINAL(avg_f64_final, double, ray_f64, value != value) static const agg_vtable_t AVG_F64 = { .state_size = sizeof(avg_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = avg_f64_init, .update_batch = avg_f64_update, - .merge = avg_f64_merge, .finalize = avg_f64_final, + .merge = avg_f64_merge, .finalize = avg_f64_final, .finalize_value = avg_f64_final_value, }; /* ---- variance family, I64 (sumsq as int64 unsigned-wrap; formula group.c:2190) -- */ -typedef struct { double sum; int64_t sumsq; int64_t cnt; } var_i64_state; -static void var_i64_init(void* s) { var_i64_state* st = s; st->sum = 0; st->sumsq = 0; st->cnt = 0; } +/* Shifted-data accumulator: sums are of (v - k), where k is the first + * value this state sees. The textbook one-pass form sumsq/n - mean^2 + * subtracts two nearly equal large numbers once values reach ~1e9, and + * cancels to zero; the clamp then reported a clean 0.0, so the wrong + * answer looked plausible (#554). Centring on k keeps both terms small + * whatever the magnitude, and the result is translation-invariant as + * variance is defined to be. An integer sum of squares wrapped on top of + * that, which is why TIMESTAMP (~8e17 ns) failed on the first row; the + * shifted squares are doubles for every input type. */ +typedef struct { int64_t k; double sum; double sumsq; int64_t cnt; } var_i64_state; +static void var_i64_init(void* s) { var_i64_state* st = s; st->k = 0; st->sum = 0; st->sumsq = 0; st->cnt = 0; } +/* One accumulate step. Integer inputs take the difference in INTEGER + * arithmetic before converting: a TIMESTAMP near 8e17 ns has a double ULP + * of 128, so converting first would quantise every value to 128 ns and + * leave a relative error around 1e-7 in the result. The subtraction is + * done on unsigned to be wrap-defined; for any span under 2^63 it is + * exact, and beyond that the shift cannot help anyway. Float inputs have + * no exact form to preserve, so they subtract in double. */ +#define VAR_ACC_STEP_INT(st, val) do { \ + int64_t v_ = (int64_t)(val); \ + if ((st)->cnt == 0) (st)->k = v_; \ + double d_ = (double)(int64_t)((uint64_t)v_ - (uint64_t)(st)->k); \ + (st)->sum += d_; (st)->sumsq += d_ * d_; (st)->cnt++; \ + } while (0) + +#define VAR_ACC_STEP_FLT(st, val) do { \ + double v_ = (double)(val); \ + if ((st)->cnt == 0) (st)->k = v_; \ + double d_ = v_ - (st)->k; \ + (st)->sum += d_; (st)->sumsq += d_ * d_; (st)->cnt++; \ + } while (0) + static void var_i64_update(void* base, size_t stride, const uint32_t* gids, const void* vals, const ray_valid_t* valid, int64_t n, acc_arena_t* a) { (void)a; const int64_t* d = (const int64_t*)vals; AGG_UPDATE_LOOP(valid, n, { var_i64_state* st = (var_i64_state*)((char*)base + (size_t)gids[i]*stride); - int64_t v = d[i]; st->sum += (double)v; - st->sumsq = (int64_t)((uint64_t)st->sumsq + (uint64_t)v*(uint64_t)v); /* wrap: group.c:185 */ - st->cnt++; + VAR_ACC_STEP_INT(st, d[i]); }); } +/* Combining two shifted states means re-centring one on the other's k: + * sum' = sum + n*dk + * sumsq' = sumsq + 2*dk*sum + n*dk^2 (dk = k_src - k_dst) + * which is exact in the same sense the update is. */ static void var_i64_merge(void* dd, const void* ss, acc_arena_t* a) { (void)a; var_i64_state* d = dd; const var_i64_state* s = ss; - d->sum += s->sum; d->sumsq = (int64_t)((uint64_t)d->sumsq + (uint64_t)s->sumsq); d->cnt += s->cnt; + if (s->cnt == 0) return; + if (d->cnt == 0) { *d = *s; return; } + double dk = (double)(int64_t)((uint64_t)s->k - (uint64_t)d->k), sn = (double)s->cnt; + d->sumsq += s->sumsq + 2.0*dk*s->sum + sn*dk*dk; + d->sum += s->sum + sn*dk; + d->cnt += s->cnt; } static inline double var_i64_varpop(const var_i64_state* st) { - double mean = st->sum / (double)st->cnt; - double vp = (double)st->sumsq / (double)st->cnt - mean*mean; - return vp < 0 ? 0 : vp; -} -static ray_t* fin_var_pop_i64(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const var_i64_state* st = s; - if (st->cnt <= 0) return ray_typed_null(-RAY_F64); - return ray_f64(ray_f64_fin(var_i64_varpop(st))); -} -static ray_t* fin_var_i64(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const var_i64_state* st = s; - if (st->cnt <= 1) return ray_typed_null(-RAY_F64); - return ray_f64(ray_f64_fin(var_i64_varpop(st) * (double)st->cnt / ((double)st->cnt - 1.0))); -} -static ray_t* fin_stddev_pop_i64(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const var_i64_state* st = s; - if (st->cnt <= 0) return ray_typed_null(-RAY_F64); - return ray_f64(ray_f64_fin(sqrt(var_i64_varpop(st)))); -} -static ray_t* fin_stddev_i64(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const var_i64_state* st = s; - if (st->cnt <= 1) return ray_typed_null(-RAY_F64); - return ray_f64(ray_f64_fin(sqrt(var_i64_varpop(st) * (double)st->cnt / ((double)st->cnt - 1.0)))); + double n = (double)st->cnt; + double vp = (st->sumsq - st->sum*st->sum/n) / n; + return vp < 0 ? 0 : vp; /* -0.0 / tiny negative residue */ +} +static double fin_var_pop_i64_result(const void* s) { + const var_i64_state* st = s; + if (st->cnt <= 0) return NULL_F64; + return ray_f64_fin(var_i64_varpop(st)); +} +AGG_SCALAR_FINAL(fin_var_pop_i64, double, ray_f64, value != value) +static double fin_var_i64_result(const void* s) { + const var_i64_state* st = s; + if (st->cnt <= 1) return NULL_F64; + return ray_f64_fin(var_i64_varpop(st) * (double)st->cnt / ((double)st->cnt - 1.0)); } +AGG_SCALAR_FINAL(fin_var_i64, double, ray_f64, value != value) +static double fin_stddev_pop_i64_result(const void* s) { + const var_i64_state* st = s; + if (st->cnt <= 0) return NULL_F64; + return ray_f64_fin(sqrt(var_i64_varpop(st))); +} +AGG_SCALAR_FINAL(fin_stddev_pop_i64, double, ray_f64, value != value) +static double fin_stddev_i64_result(const void* s) { + const var_i64_state* st = s; + if (st->cnt <= 1) return NULL_F64; + return ray_f64_fin(sqrt(var_i64_varpop(st) * (double)st->cnt / ((double)st->cnt - 1.0))); +} +AGG_SCALAR_FINAL(fin_stddev_i64, double, ray_f64, value != value) static const agg_vtable_t VAR_I64 = { .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = var_i64_init, .update_batch = var_i64_update, - .merge = var_i64_merge, .finalize = fin_var_i64, + .merge = var_i64_merge, .finalize = fin_var_i64, .finalize_value = fin_var_i64_value, }; static const agg_vtable_t VAR_POP_I64 = { .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = var_i64_init, .update_batch = var_i64_update, - .merge = var_i64_merge, .finalize = fin_var_pop_i64, + .merge = var_i64_merge, .finalize = fin_var_pop_i64, .finalize_value = fin_var_pop_i64_value, }; static const agg_vtable_t STDDEV_I64 = { .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = var_i64_init, .update_batch = var_i64_update, - .merge = var_i64_merge, .finalize = fin_stddev_i64, + .merge = var_i64_merge, .finalize = fin_stddev_i64, .finalize_value = fin_stddev_i64_value, }; static const agg_vtable_t STDDEV_POP_I64 = { .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = var_i64_init, .update_batch = var_i64_update, - .merge = var_i64_merge, .finalize = fin_stddev_pop_i64, + .merge = var_i64_merge, .finalize = fin_stddev_pop_i64, .finalize_value = fin_stddev_pop_i64_value, }; /* ---- variance family, F64 (sumsq as double) -------------------------- */ -typedef struct { double sum; double sumsq; int64_t cnt; } var_f64_state; -static void var_f64_init(void* s) { var_f64_state* st = s; st->sum = 0; st->sumsq = 0; st->cnt = 0; } +/* Same shifted accumulator as the integer family — see VAR_ACC_STEP. */ +typedef struct { double k; double sum; double sumsq; int64_t cnt; } var_f64_state; +static void var_f64_init(void* s) { var_f64_state* st = s; st->k = 0; st->sum = 0; st->sumsq = 0; st->cnt = 0; } static void var_f64_update(void* base, size_t stride, const uint32_t* gids, const void* vals, const ray_valid_t* valid, int64_t n, acc_arena_t* a) { (void)a; const double* d = (const double*)vals; AGG_UPDATE_LOOP(valid, n, { var_f64_state* st = (var_f64_state*)((char*)base + (size_t)gids[i]*stride); - double v = d[i]; st->sum += v; st->sumsq += v*v; st->cnt++; + VAR_ACC_STEP_FLT(st, d[i]); }); } static void var_f64_merge(void* dd, const void* ss, acc_arena_t* a) { (void)a; var_f64_state* d = dd; const var_f64_state* s = ss; - d->sum += s->sum; d->sumsq += s->sumsq; d->cnt += s->cnt; + if (s->cnt == 0) return; + if (d->cnt == 0) { *d = *s; return; } + double dk = s->k - d->k, sn = (double)s->cnt; + d->sumsq += s->sumsq + 2.0*dk*s->sum + sn*dk*dk; + d->sum += s->sum + sn*dk; + d->cnt += s->cnt; } static inline double var_f64_varpop(const var_f64_state* st) { - double mean = st->sum / (double)st->cnt; - double vp = st->sumsq / (double)st->cnt - mean*mean; - return vp < 0 ? 0 : vp; -} -static ray_t* fin_var_pop_f64(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const var_f64_state* st = s; - if (st->cnt <= 0) return ray_typed_null(-RAY_F64); - return ray_f64(ray_f64_fin(var_f64_varpop(st))); -} -static ray_t* fin_var_f64(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const var_f64_state* st = s; - if (st->cnt <= 1) return ray_typed_null(-RAY_F64); - return ray_f64(ray_f64_fin(var_f64_varpop(st) * (double)st->cnt / ((double)st->cnt - 1.0))); -} -static ray_t* fin_stddev_pop_f64(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const var_f64_state* st = s; - if (st->cnt <= 0) return ray_typed_null(-RAY_F64); - return ray_f64(ray_f64_fin(sqrt(var_f64_varpop(st)))); -} -static ray_t* fin_stddev_f64(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const var_f64_state* st = s; - if (st->cnt <= 1) return ray_typed_null(-RAY_F64); - return ray_f64(ray_f64_fin(sqrt(var_f64_varpop(st) * (double)st->cnt / ((double)st->cnt - 1.0)))); + double n = (double)st->cnt; + double vp = (st->sumsq - st->sum*st->sum/n) / n; + return vp < 0 ? 0 : vp; /* -0.0 / tiny negative residue */ +} +static double fin_var_pop_f64_result(const void* s) { + const var_f64_state* st = s; + if (st->cnt <= 0) return NULL_F64; + return ray_f64_fin(var_f64_varpop(st)); +} +AGG_SCALAR_FINAL(fin_var_pop_f64, double, ray_f64, value != value) +static double fin_var_f64_result(const void* s) { + const var_f64_state* st = s; + if (st->cnt <= 1) return NULL_F64; + return ray_f64_fin(var_f64_varpop(st) * (double)st->cnt / ((double)st->cnt - 1.0)); } +AGG_SCALAR_FINAL(fin_var_f64, double, ray_f64, value != value) +static double fin_stddev_pop_f64_result(const void* s) { + const var_f64_state* st = s; + if (st->cnt <= 0) return NULL_F64; + return ray_f64_fin(sqrt(var_f64_varpop(st))); +} +AGG_SCALAR_FINAL(fin_stddev_pop_f64, double, ray_f64, value != value) +static double fin_stddev_f64_result(const void* s) { + const var_f64_state* st = s; + if (st->cnt <= 1) return NULL_F64; + return ray_f64_fin(sqrt(var_f64_varpop(st) * (double)st->cnt / ((double)st->cnt - 1.0))); +} +AGG_SCALAR_FINAL(fin_stddev_f64, double, ray_f64, value != value) static const agg_vtable_t VAR_F64 = { .state_size = sizeof(var_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = var_f64_init, .update_batch = var_f64_update, - .merge = var_f64_merge, .finalize = fin_var_f64, + .merge = var_f64_merge, .finalize = fin_var_f64, .finalize_value = fin_var_f64_value, }; static const agg_vtable_t VAR_POP_F64 = { .state_size = sizeof(var_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = var_f64_init, .update_batch = var_f64_update, - .merge = var_f64_merge, .finalize = fin_var_pop_f64, + .merge = var_f64_merge, .finalize = fin_var_pop_f64, .finalize_value = fin_var_pop_f64_value, }; static const agg_vtable_t STDDEV_F64 = { .state_size = sizeof(var_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = var_f64_init, .update_batch = var_f64_update, - .merge = var_f64_merge, .finalize = fin_stddev_f64, + .merge = var_f64_merge, .finalize = fin_stddev_f64, .finalize_value = fin_stddev_f64_value, }; static const agg_vtable_t STDDEV_POP_F64 = { .state_size = sizeof(var_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = var_f64_init, .update_batch = var_f64_update, - .merge = var_f64_merge, .finalize = fin_stddev_pop_f64, + .merge = var_f64_merge, .finalize = fin_stddev_pop_f64, .finalize_value = fin_stddev_pop_f64_value, }; /* ---- pairwise numeric aggregates, F64 output (binary input: x,y) ------- */ @@ -365,6 +498,7 @@ static void pearson_init(void* s) { * caller to materialize an F64 copy. */ static inline double pearson_read_f64(const ray_valid_t* v, int64_t i) { switch (v->type) { + case RAY_F32: return ((const float*)v->base)[i]; case RAY_F64: return ((const double*)v->base)[i]; case RAY_I64: case RAY_TIMESTAMP: return (double)((const int64_t*)v->base)[i]; case RAY_I32: case RAY_DATE: case RAY_TIME: @@ -390,64 +524,76 @@ static void pearson_merge(void* dd, const void* ss, acc_arena_t* a) { (void)a; pearson_state* d = dd; const pearson_state* s = ss; d->sx += s->sx; d->sy += s->sy; d->sxx += s->sxx; d->syy += s->syy; d->sxy += s->sxy; d->n += s->n; } -static ray_t* pearson_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const pearson_state* st = s; +static double pearson_final_result(const void* s) { + const pearson_state* st = s; double dn = (double)st->n; double num = dn*st->sxy - st->sx*st->sy, dx = dn*st->sxx - st->sx*st->sx, dy = dn*st->syy - st->sy*st->sy; - /* Single-null float model: undefined pearson (n<2 → 0/0, or a constant - * side → sqrt(≤0)) yields NaN/Inf → canonicalize to NULL_F64. agg_put_cell - * sees the NaN sentinel and sets HAS_NULLS. */ - return ray_f64(ray_f64_fin(num / sqrt(dx*dy))); -} -static ray_t* cov_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const pearson_state* st = s; - if (st->n <= 0) return ray_typed_null(-RAY_F64); + /* dx and dy are >= 0 mathematically (Cauchy-Schwarz), but a CONSTANT + * column cancels to a small NEGATIVE residue in doubles. Relying on + * sqrt(<=0) to produce NaN only works when exactly ONE side is + * negative: with both negative the product is positive, the root is + * finite and a garbage "correlation" outside [-1,1] is emitted + * (#555). Test the two denominators separately, as the legacy keyed + * path does (group.c), and report the undefined case as null. + * + * n < 2 leaves dx = dy = 0, so the same guard covers it. */ + if (dx <= 0.0 || dy <= 0.0) return NULL_F64; + return ray_f64_fin(num / sqrt(dx*dy)); +} +AGG_SCALAR_FINAL(pearson_final, double, ray_f64, value != value) +static double cov_final_result(const void* s) { + const pearson_state* st = s; + if (st->n <= 0) return NULL_F64; double dn = (double)st->n; double v = (st->sxy - (st->sx * st->sy) / dn) / dn; - return ray_f64(ray_f64_fin(v)); + return ray_f64_fin(v); } -static ray_t* scov_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const pearson_state* st = s; - if (st->n <= 1) return ray_typed_null(-RAY_F64); +AGG_SCALAR_FINAL(cov_final, double, ray_f64, value != value) +static double scov_final_result(const void* s) { + const pearson_state* st = s; + if (st->n <= 1) return NULL_F64; double dn = (double)st->n; double v = (st->sxy - (st->sx * st->sy) / dn) / (dn - 1.0); - return ray_f64(ray_f64_fin(v)); + return ray_f64_fin(v); } -static ray_t* wsum_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const pearson_state* st = s; - return ray_f64(ray_f64_fin(st->sxy)); +AGG_SCALAR_FINAL(scov_final, double, ray_f64, value != value) +static double wsum_final_result(const void* s) { + const pearson_state* st = s; + return ray_f64_fin(st->sxy); } -static ray_t* wavg_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const pearson_state* st = s; - if (st->n <= 0 || st->sx == 0.0) return ray_typed_null(-RAY_F64); - return ray_f64(ray_f64_fin(st->sxy / st->sx)); +AGG_SCALAR_FINAL(wsum_final, double, ray_f64, value != value) +static double wavg_final_result(const void* s) { + const pearson_state* st = s; + if (st->n <= 0 || st->sx == 0.0) return NULL_F64; + return ray_f64_fin(st->sxy / st->sx); } +AGG_SCALAR_FINAL(wavg_final, double, ray_f64, value != value) static const agg_vtable_t PEARSON_F64 = { .state_size = sizeof(pearson_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = pearson_init, .update_batch2 = pearson_update2, - .merge = pearson_merge, .finalize = pearson_final, + .merge = pearson_merge, .finalize = pearson_final, .finalize_value = pearson_final_value, }; static const agg_vtable_t COV_F64 = { .state_size = sizeof(pearson_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = pearson_init, .update_batch2 = pearson_update2, - .merge = pearson_merge, .finalize = cov_final, + .merge = pearson_merge, .finalize = cov_final, .finalize_value = cov_final_value, }; static const agg_vtable_t SCOV_F64 = { .state_size = sizeof(pearson_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = pearson_init, .update_batch2 = pearson_update2, - .merge = pearson_merge, .finalize = scov_final, + .merge = pearson_merge, .finalize = scov_final, .finalize_value = scov_final_value, }; static const agg_vtable_t WSUM_F64 = { .state_size = sizeof(pearson_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = pearson_init, .update_batch2 = pearson_update2, - .merge = pearson_merge, .finalize = wsum_final, + .merge = pearson_merge, .finalize = wsum_final, .finalize_value = wsum_final_value, }; static const agg_vtable_t WAVG_F64 = { .state_size = sizeof(pearson_state), .kind = ACC_STREAMING, .out_type = RAY_F64, .init = pearson_init, .update_batch2 = pearson_update2, - .merge = pearson_merge, .finalize = wavg_final, + .merge = pearson_merge, .finalize = wavg_final, .finalize_value = wavg_final_value, }; /* ---- all / any truth reductions, BOOL output -------------------------- */ @@ -455,6 +601,7 @@ typedef struct { int64_t n; int64_t truthy; } truth_state; static void truth_init(void* s) { truth_state* st = s; st->n = 0; st->truthy = 0; } static inline int truth_read(const ray_valid_t* v, int64_t i) { switch (v->type) { + case RAY_F32: return ((const float*)v->base)[i] != 0.0f; case RAY_F64: return ((const double*)v->base)[i] != 0.0; case RAY_I64: case RAY_TIMESTAMP: return ((const int64_t*)v->base)[i] != 0; case RAY_I32: case RAY_DATE: case RAY_TIME: @@ -479,23 +626,25 @@ static void truth_merge(void* dd, const void* ss, acc_arena_t* a) { (void)a; truth_state* d = dd; const truth_state* s = ss; d->n += s->n; d->truthy += s->truthy; } -static ray_t* all_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const truth_state* st = s; - return ray_bool(st->truthy == st->n); +static uint8_t all_final_result(const void* s) { + const truth_state* st = s; + return st->truthy == st->n; } -static ray_t* any_final(const void* s, acc_arena_t* a, int64_t param) { - (void)a; (void)param; const truth_state* st = s; - return ray_bool(st->truthy > 0); +AGG_SCALAR_FINAL(all_final, uint8_t, ray_bool, false) +static uint8_t any_final_result(const void* s) { + const truth_state* st = s; + return st->truthy > 0; } +AGG_SCALAR_FINAL(any_final, uint8_t, ray_bool, false) static const agg_vtable_t ALL_BOOL = { .state_size = sizeof(truth_state), .kind = ACC_STREAMING, .out_type = RAY_BOOL, .init = truth_init, .update_batch = truth_update, - .merge = truth_merge, .finalize = all_final, + .merge = truth_merge, .finalize = all_final, .finalize_value = all_final_value, }; static const agg_vtable_t ANY_BOOL = { .state_size = sizeof(truth_state), .kind = ACC_STREAMING, .out_type = RAY_BOOL, .init = truth_init, .update_batch = truth_update, - .merge = truth_merge, .finalize = any_final, + .merge = truth_merge, .finalize = any_final, .finalize_value = any_final_value, }; /* ---- median, F64 output (first ACC_BUFFERED: growable per-group buffer) ---- */ @@ -595,14 +744,1119 @@ static const agg_vtable_t TOPK_F64 = { .state_size=sizeof(topk_f64_state), .kind static const agg_vtable_t BOTK_F64 = { .state_size=sizeof(topk_f64_state), .kind=ACC_BUFFERED, .out_type=RAY_LIST, .init=topk_f64_init, .update_batch=topk_f64_update, .merge=topk_f64_merge, .finalize=botN_f64_final, .destroy=topk_f64_destroy }; +/* Native-width streaming readers share the established accumulator states. */ + +static void min_bool_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const uint8_t* d = (const uint8_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] < st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static bool ext_bool_native_value(const void* s, void* dst) { + const ext_i32_state* st = s; + *(uint8_t*)dst = st->seen ? (uint8_t)st->v : 0; + return !st->seen; +} +static ray_t* ext_bool_native_final(const void* s, acc_arena_t* a, int64_t param) { + (void)a; (void)param; const ext_i32_state* st = s; + return st->seen ? ray_bool(st->v) : ray_typed_null(-RAY_BOOL); +} + +static const agg_vtable_t MIN_BOOL_NATIVE = { + SHARED_UPDATE(min_bool_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_BOOL, + .init = min_i32_init, .update_batch = min_bool_native_update, + .merge = min_i32_merge, .finalize = ext_bool_native_final, .finalize_value = ext_bool_native_value, +}; + +static void max_bool_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const uint8_t* d = (const uint8_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] > st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static const agg_vtable_t MAX_BOOL_NATIVE = { + SHARED_UPDATE(max_bool_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_BOOL, + .init = max_i32_init, .update_batch = max_bool_native_update, + .merge = max_i32_merge, .finalize = ext_bool_native_final, .finalize_value = ext_bool_native_value, +}; + +static void avg_bool_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const uint8_t* d = (const uint8_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + avg_f64_state* st = (avg_f64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum += d[i]; st->cnt++; + }); +} + +static const agg_vtable_t AVG_BOOL_NATIVE = { + .state_size = sizeof(avg_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = avg_f64_init, .update_batch = avg_bool_native_update, + .merge = avg_f64_merge, .finalize = avg_f64_final, .finalize_value = avg_f64_final_value, +}; + +static void var_bool_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const uint8_t* d = (const uint8_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + var_i64_state* st = (var_i64_state*)((char*)base + (size_t)gids[i]*stride); + VAR_ACC_STEP_INT(st, d[i]); + }); +} + +static const agg_vtable_t VAR_BOOL_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_bool_native_update, + .merge = var_i64_merge, .finalize = fin_var_i64, .finalize_value = fin_var_i64_value, +}; + +static const agg_vtable_t VAR_POP_BOOL_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_bool_native_update, + .merge = var_i64_merge, .finalize = fin_var_pop_i64, .finalize_value = fin_var_pop_i64_value, +}; + +static const agg_vtable_t STDDEV_BOOL_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_bool_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_i64, .finalize_value = fin_stddev_i64_value, +}; + +static const agg_vtable_t STDDEV_POP_BOOL_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_bool_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_pop_i64, .finalize_value = fin_stddev_pop_i64_value, +}; + +static void sum_bool_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* arena) { + (void)arena; + const uint8_t* d = (const uint8_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + sum_i64_state* st = (sum_i64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum = (int64_t)((uint64_t)st->sum + (uint64_t)d[i]); /* unsigned wrap: group.c:185 */ + }); +} + +static const agg_vtable_t SUM_BOOL_NATIVE = { + .state_size = sizeof(sum_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, + .init = sum_i64_init, .update_batch = sum_bool_native_update, + .merge = sum_i64_merge, .finalize = sum_i64_final, .finalize_value = sum_i64_final_value, +}; + +static void min_u8_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const uint8_t* d = (const uint8_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] < st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static bool ext_u8_native_value(const void* s, void* dst) { + const ext_i32_state* st = s; + *(uint8_t*)dst = st->seen ? (uint8_t)st->v : 0; + return !st->seen; +} +static ray_t* ext_u8_native_final(const void* s, acc_arena_t* a, int64_t param) { + (void)a; (void)param; const ext_i32_state* st = s; + return st->seen ? ray_u8(st->v) : ray_typed_null(-RAY_U8); +} + +static const agg_vtable_t MIN_U8_NATIVE = { + SHARED_UPDATE(min_u8_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_U8, + .init = min_i32_init, .update_batch = min_u8_native_update, + .merge = min_i32_merge, .finalize = ext_u8_native_final, .finalize_value = ext_u8_native_value, +}; + +static void max_u8_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const uint8_t* d = (const uint8_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] > st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static const agg_vtable_t MAX_U8_NATIVE = { + SHARED_UPDATE(max_u8_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_U8, + .init = max_i32_init, .update_batch = max_u8_native_update, + .merge = max_i32_merge, .finalize = ext_u8_native_final, .finalize_value = ext_u8_native_value, +}; + +static void avg_u8_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const uint8_t* d = (const uint8_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + avg_f64_state* st = (avg_f64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum += d[i]; st->cnt++; + }); +} + +static const agg_vtable_t AVG_U8_NATIVE = { + .state_size = sizeof(avg_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = avg_f64_init, .update_batch = avg_u8_native_update, + .merge = avg_f64_merge, .finalize = avg_f64_final, .finalize_value = avg_f64_final_value, +}; + +static void var_u8_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const uint8_t* d = (const uint8_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + var_i64_state* st = (var_i64_state*)((char*)base + (size_t)gids[i]*stride); + VAR_ACC_STEP_INT(st, d[i]); + }); +} + +static const agg_vtable_t VAR_U8_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_u8_native_update, + .merge = var_i64_merge, .finalize = fin_var_i64, .finalize_value = fin_var_i64_value, +}; + +static const agg_vtable_t VAR_POP_U8_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_u8_native_update, + .merge = var_i64_merge, .finalize = fin_var_pop_i64, .finalize_value = fin_var_pop_i64_value, +}; + +static const agg_vtable_t STDDEV_U8_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_u8_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_i64, .finalize_value = fin_stddev_i64_value, +}; + +static const agg_vtable_t STDDEV_POP_U8_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_u8_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_pop_i64, .finalize_value = fin_stddev_pop_i64_value, +}; + +static void sum_u8_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* arena) { + (void)arena; + const uint8_t* d = (const uint8_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + sum_i64_state* st = (sum_i64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum = (int64_t)((uint64_t)st->sum + (uint64_t)d[i]); /* unsigned wrap: group.c:185 */ + }); +} + +static const agg_vtable_t SUM_U8_NATIVE = { + .state_size = sizeof(sum_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, + .init = sum_i64_init, .update_batch = sum_u8_native_update, + .merge = sum_i64_merge, .finalize = sum_i64_final, .finalize_value = sum_i64_final_value, +}; + +static void min_i16_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int16_t* d = (const int16_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] < st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static bool ext_i16_native_value(const void* s, void* dst) { + const ext_i32_state* st = s; + *(int16_t*)dst = st->seen ? (int16_t)st->v : NULL_I16; + return !st->seen; +} +static ray_t* ext_i16_native_final(const void* s, acc_arena_t* a, int64_t param) { + (void)a; (void)param; const ext_i32_state* st = s; + return st->seen ? ray_i16(st->v) : ray_typed_null(-RAY_I16); +} + +static const agg_vtable_t MIN_I16_NATIVE = { + SHARED_UPDATE(min_i16_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_I16, + .init = min_i32_init, .update_batch = min_i16_native_update, + .merge = min_i32_merge, .finalize = ext_i16_native_final, .finalize_value = ext_i16_native_value, +}; + +static void max_i16_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int16_t* d = (const int16_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] > st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static const agg_vtable_t MAX_I16_NATIVE = { + SHARED_UPDATE(max_i16_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_I16, + .init = max_i32_init, .update_batch = max_i16_native_update, + .merge = max_i32_merge, .finalize = ext_i16_native_final, .finalize_value = ext_i16_native_value, +}; + +static void avg_i16_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int16_t* d = (const int16_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + avg_f64_state* st = (avg_f64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum += d[i]; st->cnt++; + }); +} + +static const agg_vtable_t AVG_I16_NATIVE = { + .state_size = sizeof(avg_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = avg_f64_init, .update_batch = avg_i16_native_update, + .merge = avg_f64_merge, .finalize = avg_f64_final, .finalize_value = avg_f64_final_value, +}; + +static void var_i16_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int16_t* d = (const int16_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + var_i64_state* st = (var_i64_state*)((char*)base + (size_t)gids[i]*stride); + VAR_ACC_STEP_INT(st, d[i]); + }); +} + +static const agg_vtable_t VAR_I16_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_i16_native_update, + .merge = var_i64_merge, .finalize = fin_var_i64, .finalize_value = fin_var_i64_value, +}; + +static const agg_vtable_t VAR_POP_I16_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_i16_native_update, + .merge = var_i64_merge, .finalize = fin_var_pop_i64, .finalize_value = fin_var_pop_i64_value, +}; + +static const agg_vtable_t STDDEV_I16_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_i16_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_i64, .finalize_value = fin_stddev_i64_value, +}; + +static const agg_vtable_t STDDEV_POP_I16_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_i16_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_pop_i64, .finalize_value = fin_stddev_pop_i64_value, +}; + +static void sum_i16_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* arena) { + (void)arena; + const int16_t* d = (const int16_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + sum_i64_state* st = (sum_i64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum = (int64_t)((uint64_t)st->sum + (uint64_t)d[i]); /* unsigned wrap: group.c:185 */ + }); +} + +static const agg_vtable_t SUM_I16_NATIVE = { + .state_size = sizeof(sum_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, + .init = sum_i64_init, .update_batch = sum_i16_native_update, + .merge = sum_i64_merge, .finalize = sum_i64_final, .finalize_value = sum_i64_final_value, +}; + +static void min_i32_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] < st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static bool ext_i32_native_value(const void* s, void* dst) { + const ext_i32_state* st = s; + *(int32_t*)dst = st->seen ? (int32_t)st->v : NULL_I32; + return !st->seen; +} +static ray_t* ext_i32_native_final(const void* s, acc_arena_t* a, int64_t param) { + (void)a; (void)param; const ext_i32_state* st = s; + return st->seen ? ray_i32(st->v) : ray_typed_null(-RAY_I32); +} + +static const agg_vtable_t MIN_I32_NATIVE = { + SHARED_UPDATE(min_i32_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_I32, + .init = min_i32_init, .update_batch = min_i32_native_update, + .merge = min_i32_merge, .finalize = ext_i32_native_final, .finalize_value = ext_i32_native_value, +}; + +static void max_i32_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] > st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static const agg_vtable_t MAX_I32_NATIVE = { + SHARED_UPDATE(max_i32_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_I32, + .init = max_i32_init, .update_batch = max_i32_native_update, + .merge = max_i32_merge, .finalize = ext_i32_native_final, .finalize_value = ext_i32_native_value, +}; + +static void avg_i32_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + avg_f64_state* st = (avg_f64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum += d[i]; st->cnt++; + }); +} + +static const agg_vtable_t AVG_I32_NATIVE = { + .state_size = sizeof(avg_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = avg_f64_init, .update_batch = avg_i32_native_update, + .merge = avg_f64_merge, .finalize = avg_f64_final, .finalize_value = avg_f64_final_value, +}; + +static void var_i32_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + var_i64_state* st = (var_i64_state*)((char*)base + (size_t)gids[i]*stride); + VAR_ACC_STEP_INT(st, d[i]); + }); +} + +static const agg_vtable_t VAR_I32_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_i32_native_update, + .merge = var_i64_merge, .finalize = fin_var_i64, .finalize_value = fin_var_i64_value, +}; + +static const agg_vtable_t VAR_POP_I32_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_i32_native_update, + .merge = var_i64_merge, .finalize = fin_var_pop_i64, .finalize_value = fin_var_pop_i64_value, +}; + +static const agg_vtable_t STDDEV_I32_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_i32_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_i64, .finalize_value = fin_stddev_i64_value, +}; + +static const agg_vtable_t STDDEV_POP_I32_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_i32_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_pop_i64, .finalize_value = fin_stddev_pop_i64_value, +}; + +static void sum_i32_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* arena) { + (void)arena; + const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + sum_i64_state* st = (sum_i64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum = (int64_t)((uint64_t)st->sum + (uint64_t)d[i]); /* unsigned wrap: group.c:185 */ + }); +} + +static const agg_vtable_t SUM_I32_NATIVE = { + .state_size = sizeof(sum_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, + .init = sum_i64_init, .update_batch = sum_i32_native_update, + .merge = sum_i64_merge, .finalize = sum_i64_final, .finalize_value = sum_i64_final_value, +}; + +static void avg_i64_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int64_t* d = (const int64_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + avg_f64_state* st = (avg_f64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum += d[i]; st->cnt++; + }); +} + +static const agg_vtable_t AVG_I64_NATIVE = { + .state_size = sizeof(avg_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = avg_f64_init, .update_batch = avg_i64_native_update, + .merge = avg_f64_merge, .finalize = avg_f64_final, .finalize_value = avg_f64_final_value, +}; + +static void min_f32_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const float* d = (const float*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_f64_state* st = (ext_f64_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] < st->v) st->v = d[i]; + st->cnt++; + }); +} + +static const agg_vtable_t MIN_F32_NATIVE = { + .state_size = sizeof(ext_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = min_f64_init, .update_batch = min_f32_native_update, + .merge = min_f64_merge, .finalize = ext_f64_final, .finalize_value = ext_f64_final_value, +}; + +static void max_f32_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const float* d = (const float*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_f64_state* st = (ext_f64_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] > st->v) st->v = d[i]; + st->cnt++; + }); +} + +static const agg_vtable_t MAX_F32_NATIVE = { + .state_size = sizeof(ext_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = max_f64_init, .update_batch = max_f32_native_update, + .merge = max_f64_merge, .finalize = ext_f64_final, .finalize_value = ext_f64_final_value, +}; + +static void avg_f32_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const float* d = (const float*)vals; + AGG_UPDATE_LOOP(valid, n, { + avg_f64_state* st = (avg_f64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum += d[i]; st->cnt++; + }); +} + +static const agg_vtable_t AVG_F32_NATIVE = { + .state_size = sizeof(avg_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = avg_f64_init, .update_batch = avg_f32_native_update, + .merge = avg_f64_merge, .finalize = avg_f64_final, .finalize_value = avg_f64_final_value, +}; + +static void var_f32_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const float* d = (const float*)vals; + AGG_UPDATE_LOOP(valid, n, { + var_f64_state* st = (var_f64_state*)((char*)base + (size_t)gids[i]*stride); + VAR_ACC_STEP_FLT(st, d[i]); + }); +} + +static const agg_vtable_t VAR_F32_NATIVE = { + .state_size = sizeof(var_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_f64_init, .update_batch = var_f32_native_update, + .merge = var_f64_merge, .finalize = fin_var_f64, .finalize_value = fin_var_f64_value, +}; + +static const agg_vtable_t VAR_POP_F32_NATIVE = { + .state_size = sizeof(var_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_f64_init, .update_batch = var_f32_native_update, + .merge = var_f64_merge, .finalize = fin_var_pop_f64, .finalize_value = fin_var_pop_f64_value, +}; + +static const agg_vtable_t STDDEV_F32_NATIVE = { + .state_size = sizeof(var_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_f64_init, .update_batch = var_f32_native_update, + .merge = var_f64_merge, .finalize = fin_stddev_f64, .finalize_value = fin_stddev_f64_value, +}; + +static const agg_vtable_t STDDEV_POP_F32_NATIVE = { + .state_size = sizeof(var_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_f64_init, .update_batch = var_f32_native_update, + .merge = var_f64_merge, .finalize = fin_stddev_pop_f64, .finalize_value = fin_stddev_pop_f64_value, +}; + +static void sum_f32_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const float* d = (const float*)vals; + AGG_UPDATE_LOOP(valid, n, + ((sum_f64_state*)((char*)base + (size_t)gids[i]*stride))->sum += d[i]); +} + +static const agg_vtable_t SUM_F32_NATIVE = { + .state_size = sizeof(sum_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = sum_f64_init, .update_batch = sum_f32_native_update, + .merge = sum_f64_merge, .finalize = sum_f64_final, .finalize_value = sum_f64_final_value, +}; + +static void min_date_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] < st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static bool ext_date_native_value(const void* s, void* dst) { + const ext_i32_state* st = s; + *(int32_t*)dst = st->seen ? (int32_t)st->v : NULL_I32; + return !st->seen; +} +static ray_t* ext_date_native_final(const void* s, acc_arena_t* a, int64_t param) { + (void)a; (void)param; const ext_i32_state* st = s; + return st->seen ? ray_date(st->v) : ray_typed_null(-RAY_DATE); +} + +static const agg_vtable_t MIN_DATE_NATIVE = { + SHARED_UPDATE(min_date_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_DATE, + .init = min_i32_init, .update_batch = min_date_native_update, + .merge = min_i32_merge, .finalize = ext_date_native_final, .finalize_value = ext_date_native_value, +}; + +static void max_date_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] > st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static const agg_vtable_t MAX_DATE_NATIVE = { + SHARED_UPDATE(max_date_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_DATE, + .init = max_i32_init, .update_batch = max_date_native_update, + .merge = max_i32_merge, .finalize = ext_date_native_final, .finalize_value = ext_date_native_value, +}; + +static void avg_date_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + avg_f64_state* st = (avg_f64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum += d[i]; st->cnt++; + }); +} + +static const agg_vtable_t AVG_DATE_NATIVE = { + .state_size = sizeof(avg_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = avg_f64_init, .update_batch = avg_date_native_update, + .merge = avg_f64_merge, .finalize = avg_f64_final, .finalize_value = avg_f64_final_value, +}; + +static void var_date_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + var_i64_state* st = (var_i64_state*)((char*)base + (size_t)gids[i]*stride); + VAR_ACC_STEP_INT(st, d[i]); + }); +} + +static const agg_vtable_t VAR_DATE_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_date_native_update, + .merge = var_i64_merge, .finalize = fin_var_i64, .finalize_value = fin_var_i64_value, +}; + +static const agg_vtable_t VAR_POP_DATE_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_date_native_update, + .merge = var_i64_merge, .finalize = fin_var_pop_i64, .finalize_value = fin_var_pop_i64_value, +}; + +static const agg_vtable_t STDDEV_DATE_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_date_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_i64, .finalize_value = fin_stddev_i64_value, +}; + +static const agg_vtable_t STDDEV_POP_DATE_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_date_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_pop_i64, .finalize_value = fin_stddev_pop_i64_value, +}; + +static void min_time_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] < st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static bool ext_time_native_value(const void* s, void* dst) { + const ext_i32_state* st = s; + *(int32_t*)dst = st->seen ? (int32_t)st->v : NULL_I32; + return !st->seen; +} +static ray_t* ext_time_native_final(const void* s, acc_arena_t* a, int64_t param) { + (void)a; (void)param; const ext_i32_state* st = s; + return st->seen ? ray_time(st->v) : ray_typed_null(-RAY_TIME); +} + +static const agg_vtable_t MIN_TIME_NATIVE = { + SHARED_UPDATE(min_time_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_TIME, + .init = min_i32_init, .update_batch = min_time_native_update, + .merge = min_i32_merge, .finalize = ext_time_native_final, .finalize_value = ext_time_native_value, +}; + +static void max_time_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i32_state* st = (ext_i32_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] > st->v) st->v = d[i]; + st->seen = 1; + }); +} + +static const agg_vtable_t MAX_TIME_NATIVE = { + SHARED_UPDATE(max_time_shared) + .state_size = sizeof(ext_i32_state), .kind = ACC_STREAMING, .out_type = RAY_TIME, + .init = max_i32_init, .update_batch = max_time_native_update, + .merge = max_i32_merge, .finalize = ext_time_native_final, .finalize_value = ext_time_native_value, +}; + +static void avg_time_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + avg_f64_state* st = (avg_f64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum += d[i]; st->cnt++; + }); +} + +static const agg_vtable_t AVG_TIME_NATIVE = { + .state_size = sizeof(avg_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = avg_f64_init, .update_batch = avg_time_native_update, + .merge = avg_f64_merge, .finalize = avg_f64_final, .finalize_value = avg_f64_final_value, +}; + +static void var_time_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + var_i64_state* st = (var_i64_state*)((char*)base + (size_t)gids[i]*stride); + VAR_ACC_STEP_INT(st, d[i]); + }); +} + +static const agg_vtable_t VAR_TIME_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_time_native_update, + .merge = var_i64_merge, .finalize = fin_var_i64, .finalize_value = fin_var_i64_value, +}; + +static const agg_vtable_t VAR_POP_TIME_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_time_native_update, + .merge = var_i64_merge, .finalize = fin_var_pop_i64, .finalize_value = fin_var_pop_i64_value, +}; + +static const agg_vtable_t STDDEV_TIME_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_time_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_i64, .finalize_value = fin_stddev_i64_value, +}; + +static const agg_vtable_t STDDEV_POP_TIME_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_time_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_pop_i64, .finalize_value = fin_stddev_pop_i64_value, +}; + +static void sum_time_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* arena) { + (void)arena; + const int32_t* d = (const int32_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + sum_i64_state* st = (sum_i64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum = (int64_t)((uint64_t)st->sum + (uint64_t)d[i]); /* unsigned wrap: group.c:185 */ + }); +} + +static int64_t sum_time_native_final_result(const void* s) { + return ((const sum_i64_state*)s)->sum; +} +AGG_SCALAR_FINAL(sum_time_native_final, int32_t, ray_time, value == NULL_I32) + +static const agg_vtable_t SUM_TIME_NATIVE = { + .state_size = sizeof(sum_i64_state), .kind = ACC_STREAMING, .out_type = RAY_TIME, + .init = sum_i64_init, .update_batch = sum_time_native_update, + .merge = sum_i64_merge, .finalize = sum_time_native_final, .finalize_value = sum_time_native_final_value, +}; + +static void min_timestamp_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int64_t* d = (const int64_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i64_state* st = (ext_i64_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] < st->v) st->v = d[i]; + st->cnt++; + }); +} + +static bool ext_timestamp_native_value(const void* s, void* dst) { + const ext_i64_state* st = s; + *(int64_t*)dst = st->cnt ? (int64_t)st->v : NULL_I64; + return !st->cnt; +} +static ray_t* ext_timestamp_native_final(const void* s, acc_arena_t* a, int64_t param) { + (void)a; (void)param; const ext_i64_state* st = s; + return st->cnt ? ray_timestamp(st->v) : ray_typed_null(-RAY_TIMESTAMP); +} + +static const agg_vtable_t MIN_TIMESTAMP_NATIVE = { + .state_size = sizeof(ext_i64_state), .kind = ACC_STREAMING, .out_type = RAY_TIMESTAMP, + .init = min_i64_init, .update_batch = min_timestamp_native_update, + .merge = min_i64_merge, .finalize = ext_timestamp_native_final, .finalize_value = ext_timestamp_native_value, +}; + +static void max_timestamp_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int64_t* d = (const int64_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + ext_i64_state* st = (ext_i64_state*)((char*)base + (size_t)gids[i]*stride); + if (d[i] > st->v) st->v = d[i]; + st->cnt++; + }); +} + +static const agg_vtable_t MAX_TIMESTAMP_NATIVE = { + .state_size = sizeof(ext_i64_state), .kind = ACC_STREAMING, .out_type = RAY_TIMESTAMP, + .init = max_i64_init, .update_batch = max_timestamp_native_update, + .merge = max_i64_merge, .finalize = ext_timestamp_native_final, .finalize_value = ext_timestamp_native_value, +}; + +static void avg_timestamp_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int64_t* d = (const int64_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + avg_f64_state* st = (avg_f64_state*)((char*)base + (size_t)gids[i]*stride); + st->sum += d[i]; st->cnt++; + }); +} + +static const agg_vtable_t AVG_TIMESTAMP_NATIVE = { + .state_size = sizeof(avg_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = avg_f64_init, .update_batch = avg_timestamp_native_update, + .merge = avg_f64_merge, .finalize = avg_f64_final, .finalize_value = avg_f64_final_value, +}; + +static void var_timestamp_native_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, + int64_t n, acc_arena_t* a) { + (void)a; const int64_t* d = (const int64_t*)vals; + AGG_UPDATE_LOOP(valid, n, { + var_i64_state* st = (var_i64_state*)((char*)base + (size_t)gids[i]*stride); + VAR_ACC_STEP_INT(st, d[i]); + }); +} + +static const agg_vtable_t VAR_TIMESTAMP_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_timestamp_native_update, + .merge = var_i64_merge, .finalize = fin_var_i64, .finalize_value = fin_var_i64_value, +}; + +static const agg_vtable_t VAR_POP_TIMESTAMP_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_timestamp_native_update, + .merge = var_i64_merge, .finalize = fin_var_pop_i64, .finalize_value = fin_var_pop_i64_value, +}; + +static const agg_vtable_t STDDEV_TIMESTAMP_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_timestamp_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_i64, .finalize_value = fin_stddev_i64_value, +}; + +static const agg_vtable_t STDDEV_POP_TIMESTAMP_NATIVE = { + .state_size = sizeof(var_i64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = var_i64_init, .update_batch = var_timestamp_native_update, + .merge = var_i64_merge, .finalize = fin_stddev_pop_i64, .finalize_value = fin_stddev_pop_i64_value, +}; + +typedef struct { int64_t sum, cnt; } prod_i64_state; +typedef struct { double sum; int64_t cnt; } prod_f64_state; +static int64_t prod_i64_final_result(const void* s) { + const prod_i64_state* st = s; + return st->cnt ? st->sum : NULL_I64; +} +AGG_SCALAR_FINAL(prod_i64_final, int64_t, ray_i64, value == NULL_I64) +static double prod_f64_final_result(const void* s) { + const prod_f64_state* st = s; + return st->cnt ? ray_f64_fin(st->sum) : NULL_F64; +} +AGG_SCALAR_FINAL(prod_f64_final, double, ray_f64, value != value) +static void prod_bool_init(void* s) { ((prod_i64_state*)s)->sum = 1; ((prod_i64_state*)s)->cnt = 0; } +static void prod_bool_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, int64_t n, acc_arena_t* a) { + (void)a; const uint8_t* d = vals; + AGG_UPDATE_LOOP(valid, n, { + prod_i64_state* st = (prod_i64_state*)((char*)base + (size_t)gids[i] * stride); + st->cnt++; + st->sum = (int64_t)((uint64_t)st->sum * (uint64_t)d[i]); + }); +} +static void prod_bool_merge(void* d, const void* s, acc_arena_t* a) { + (void)a; prod_i64_state* dst = d; const prod_i64_state* src = s; + dst->cnt += src->cnt; + dst->sum = (int64_t)((uint64_t)dst->sum * (uint64_t)src->sum); +} +static const agg_vtable_t PROD_BOOL = { + .state_size = sizeof(prod_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, + .init = prod_bool_init, .update_batch = prod_bool_update, + .merge = prod_bool_merge, .finalize = prod_i64_final, .finalize_value = prod_i64_final_value, +}; + +static void prod_u8_init(void* s) { ((prod_i64_state*)s)->sum = 1; ((prod_i64_state*)s)->cnt = 0; } +static void prod_u8_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, int64_t n, acc_arena_t* a) { + (void)a; const uint8_t* d = vals; + AGG_UPDATE_LOOP(valid, n, { + prod_i64_state* st = (prod_i64_state*)((char*)base + (size_t)gids[i] * stride); + st->cnt++; + st->sum = (int64_t)((uint64_t)st->sum * (uint64_t)d[i]); + }); +} +static void prod_u8_merge(void* d, const void* s, acc_arena_t* a) { + (void)a; prod_i64_state* dst = d; const prod_i64_state* src = s; + dst->cnt += src->cnt; + dst->sum = (int64_t)((uint64_t)dst->sum * (uint64_t)src->sum); +} +static const agg_vtable_t PROD_U8 = { + .state_size = sizeof(prod_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, + .init = prod_u8_init, .update_batch = prod_u8_update, + .merge = prod_u8_merge, .finalize = prod_i64_final, .finalize_value = prod_i64_final_value, +}; + +static void prod_i16_init(void* s) { ((prod_i64_state*)s)->sum = 1; ((prod_i64_state*)s)->cnt = 0; } +static void prod_i16_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, int64_t n, acc_arena_t* a) { + (void)a; const int16_t* d = vals; + AGG_UPDATE_LOOP(valid, n, { + prod_i64_state* st = (prod_i64_state*)((char*)base + (size_t)gids[i] * stride); + st->cnt++; + st->sum = (int64_t)((uint64_t)st->sum * (uint64_t)d[i]); + }); +} +static void prod_i16_merge(void* d, const void* s, acc_arena_t* a) { + (void)a; prod_i64_state* dst = d; const prod_i64_state* src = s; + dst->cnt += src->cnt; + dst->sum = (int64_t)((uint64_t)dst->sum * (uint64_t)src->sum); +} +static const agg_vtable_t PROD_I16 = { + .state_size = sizeof(prod_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, + .init = prod_i16_init, .update_batch = prod_i16_update, + .merge = prod_i16_merge, .finalize = prod_i64_final, .finalize_value = prod_i64_final_value, +}; + +static void prod_i32_init(void* s) { ((prod_i64_state*)s)->sum = 1; ((prod_i64_state*)s)->cnt = 0; } +static void prod_i32_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, int64_t n, acc_arena_t* a) { + (void)a; const int32_t* d = vals; + AGG_UPDATE_LOOP(valid, n, { + prod_i64_state* st = (prod_i64_state*)((char*)base + (size_t)gids[i] * stride); + st->cnt++; + st->sum = (int64_t)((uint64_t)st->sum * (uint64_t)d[i]); + }); +} +static void prod_i32_merge(void* d, const void* s, acc_arena_t* a) { + (void)a; prod_i64_state* dst = d; const prod_i64_state* src = s; + dst->cnt += src->cnt; + dst->sum = (int64_t)((uint64_t)dst->sum * (uint64_t)src->sum); +} +static const agg_vtable_t PROD_I32 = { + .state_size = sizeof(prod_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, + .init = prod_i32_init, .update_batch = prod_i32_update, + .merge = prod_i32_merge, .finalize = prod_i64_final, .finalize_value = prod_i64_final_value, +}; + +static void prod_i64_init(void* s) { ((prod_i64_state*)s)->sum = 1; ((prod_i64_state*)s)->cnt = 0; } +static void prod_i64_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, int64_t n, acc_arena_t* a) { + (void)a; const int64_t* d = vals; + AGG_UPDATE_LOOP(valid, n, { + prod_i64_state* st = (prod_i64_state*)((char*)base + (size_t)gids[i] * stride); + st->cnt++; + st->sum = (int64_t)((uint64_t)st->sum * (uint64_t)d[i]); + }); +} +static void prod_i64_merge(void* d, const void* s, acc_arena_t* a) { + (void)a; prod_i64_state* dst = d; const prod_i64_state* src = s; + dst->cnt += src->cnt; + dst->sum = (int64_t)((uint64_t)dst->sum * (uint64_t)src->sum); +} +static const agg_vtable_t PROD_I64 = { + .state_size = sizeof(prod_i64_state), .kind = ACC_STREAMING, .out_type = RAY_I64, + .init = prod_i64_init, .update_batch = prod_i64_update, + .merge = prod_i64_merge, .finalize = prod_i64_final, .finalize_value = prod_i64_final_value, +}; + +static void prod_f32_init(void* s) { ((prod_f64_state*)s)->sum = 1; ((prod_f64_state*)s)->cnt = 0; } +static void prod_f32_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, int64_t n, acc_arena_t* a) { + (void)a; const float* d = vals; + AGG_UPDATE_LOOP(valid, n, { + prod_f64_state* st = (prod_f64_state*)((char*)base + (size_t)gids[i] * stride); + st->cnt++; + st->sum = st->sum * d[i]; + }); +} +static void prod_f32_merge(void* d, const void* s, acc_arena_t* a) { + (void)a; prod_f64_state* dst = d; const prod_f64_state* src = s; + dst->cnt += src->cnt; + dst->sum = dst->sum * src->sum; +} +static const agg_vtable_t PROD_F32 = { + .state_size = sizeof(prod_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = prod_f32_init, .update_batch = prod_f32_update, + .merge = prod_f32_merge, .finalize = prod_f64_final, .finalize_value = prod_f64_final_value, +}; + +static void prod_f64_init(void* s) { ((prod_f64_state*)s)->sum = 1; ((prod_f64_state*)s)->cnt = 0; } +static void prod_f64_update(void* base, size_t stride, const uint32_t* gids, + const void* vals, const ray_valid_t* valid, int64_t n, acc_arena_t* a) { + (void)a; const double* d = vals; + AGG_UPDATE_LOOP(valid, n, { + prod_f64_state* st = (prod_f64_state*)((char*)base + (size_t)gids[i] * stride); + st->cnt++; + st->sum = st->sum * d[i]; + }); +} +static void prod_f64_merge(void* d, const void* s, acc_arena_t* a) { + (void)a; prod_f64_state* dst = d; const prod_f64_state* src = s; + dst->cnt += src->cnt; + dst->sum = dst->sum * src->sum; +} +static const agg_vtable_t PROD_F64 = { + .state_size = sizeof(prod_f64_state), .kind = ACC_STREAMING, .out_type = RAY_F64, + .init = prod_f64_init, .update_batch = prod_f64_update, + .merge = prod_f64_merge, .finalize = prod_f64_final, .finalize_value = prod_f64_final_value, +}; + const agg_vtable_t* agg_resolve(uint16_t agg_kind, int8_t in_type) { + if (agg_kind == OP_PROD && in_type == RAY_BOOL) return &PROD_BOOL; + if (agg_kind == OP_PROD && in_type == RAY_U8) return &PROD_U8; + if (agg_kind == OP_PROD && in_type == RAY_I16) return &PROD_I16; + if (agg_kind == OP_PROD && in_type == RAY_I32) return &PROD_I32; + if (agg_kind == OP_PROD && in_type == RAY_I64) return &PROD_I64; + if (agg_kind == OP_PROD && in_type == RAY_F32) return &PROD_F32; + if (agg_kind == OP_PROD && in_type == RAY_F64) return &PROD_F64; + if (agg_kind == OP_MIN && in_type == RAY_BOOL) return &MIN_BOOL_NATIVE; + if (agg_kind == OP_MAX && in_type == RAY_BOOL) return &MAX_BOOL_NATIVE; + if (agg_kind == OP_AVG && in_type == RAY_BOOL) return &AVG_BOOL_NATIVE; + if (agg_kind == OP_VAR && in_type == RAY_BOOL) return &VAR_BOOL_NATIVE; + if (agg_kind == OP_VAR_POP && in_type == RAY_BOOL) return &VAR_POP_BOOL_NATIVE; + if (agg_kind == OP_STDDEV && in_type == RAY_BOOL) return &STDDEV_BOOL_NATIVE; + if (agg_kind == OP_STDDEV_POP && in_type == RAY_BOOL) return &STDDEV_POP_BOOL_NATIVE; + if (agg_kind == OP_SUM && in_type == RAY_BOOL) return &SUM_BOOL_NATIVE; + if (agg_kind == OP_MIN && in_type == RAY_U8) return &MIN_U8_NATIVE; + if (agg_kind == OP_MAX && in_type == RAY_U8) return &MAX_U8_NATIVE; + if (agg_kind == OP_AVG && in_type == RAY_U8) return &AVG_U8_NATIVE; + if (agg_kind == OP_VAR && in_type == RAY_U8) return &VAR_U8_NATIVE; + if (agg_kind == OP_VAR_POP && in_type == RAY_U8) return &VAR_POP_U8_NATIVE; + if (agg_kind == OP_STDDEV && in_type == RAY_U8) return &STDDEV_U8_NATIVE; + if (agg_kind == OP_STDDEV_POP && in_type == RAY_U8) return &STDDEV_POP_U8_NATIVE; + if (agg_kind == OP_SUM && in_type == RAY_U8) return &SUM_U8_NATIVE; + if (agg_kind == OP_MIN && in_type == RAY_I16) return &MIN_I16_NATIVE; + if (agg_kind == OP_MAX && in_type == RAY_I16) return &MAX_I16_NATIVE; + if (agg_kind == OP_AVG && in_type == RAY_I16) return &AVG_I16_NATIVE; + if (agg_kind == OP_VAR && in_type == RAY_I16) return &VAR_I16_NATIVE; + if (agg_kind == OP_VAR_POP && in_type == RAY_I16) return &VAR_POP_I16_NATIVE; + if (agg_kind == OP_STDDEV && in_type == RAY_I16) return &STDDEV_I16_NATIVE; + if (agg_kind == OP_STDDEV_POP && in_type == RAY_I16) return &STDDEV_POP_I16_NATIVE; + if (agg_kind == OP_SUM && in_type == RAY_I16) return &SUM_I16_NATIVE; + if (agg_kind == OP_MIN && in_type == RAY_I32) return &MIN_I32_NATIVE; + if (agg_kind == OP_MAX && in_type == RAY_I32) return &MAX_I32_NATIVE; + if (agg_kind == OP_AVG && in_type == RAY_I32) return &AVG_I32_NATIVE; + if (agg_kind == OP_VAR && in_type == RAY_I32) return &VAR_I32_NATIVE; + if (agg_kind == OP_VAR_POP && in_type == RAY_I32) return &VAR_POP_I32_NATIVE; + if (agg_kind == OP_STDDEV && in_type == RAY_I32) return &STDDEV_I32_NATIVE; + if (agg_kind == OP_STDDEV_POP && in_type == RAY_I32) return &STDDEV_POP_I32_NATIVE; + if (agg_kind == OP_SUM && in_type == RAY_I32) return &SUM_I32_NATIVE; + if (agg_kind == OP_AVG && in_type == RAY_I64) return &AVG_I64_NATIVE; + if (agg_kind == OP_MIN && in_type == RAY_F32) return &MIN_F32_NATIVE; + if (agg_kind == OP_MAX && in_type == RAY_F32) return &MAX_F32_NATIVE; + if (agg_kind == OP_AVG && in_type == RAY_F32) return &AVG_F32_NATIVE; + if (agg_kind == OP_VAR && in_type == RAY_F32) return &VAR_F32_NATIVE; + if (agg_kind == OP_VAR_POP && in_type == RAY_F32) return &VAR_POP_F32_NATIVE; + if (agg_kind == OP_STDDEV && in_type == RAY_F32) return &STDDEV_F32_NATIVE; + if (agg_kind == OP_STDDEV_POP && in_type == RAY_F32) return &STDDEV_POP_F32_NATIVE; + if (agg_kind == OP_SUM && in_type == RAY_F32) return &SUM_F32_NATIVE; + if (agg_kind == OP_MIN && in_type == RAY_DATE) return &MIN_DATE_NATIVE; + if (agg_kind == OP_MAX && in_type == RAY_DATE) return &MAX_DATE_NATIVE; + if (agg_kind == OP_AVG && in_type == RAY_DATE) return &AVG_DATE_NATIVE; + if (agg_kind == OP_VAR && in_type == RAY_DATE) return &VAR_DATE_NATIVE; + if (agg_kind == OP_VAR_POP && in_type == RAY_DATE) return &VAR_POP_DATE_NATIVE; + if (agg_kind == OP_STDDEV && in_type == RAY_DATE) return &STDDEV_DATE_NATIVE; + if (agg_kind == OP_STDDEV_POP && in_type == RAY_DATE) return &STDDEV_POP_DATE_NATIVE; + if (agg_kind == OP_MIN && in_type == RAY_TIME) return &MIN_TIME_NATIVE; + if (agg_kind == OP_MAX && in_type == RAY_TIME) return &MAX_TIME_NATIVE; + if (agg_kind == OP_AVG && in_type == RAY_TIME) return &AVG_TIME_NATIVE; + if (agg_kind == OP_VAR && in_type == RAY_TIME) return &VAR_TIME_NATIVE; + if (agg_kind == OP_VAR_POP && in_type == RAY_TIME) return &VAR_POP_TIME_NATIVE; + if (agg_kind == OP_STDDEV && in_type == RAY_TIME) return &STDDEV_TIME_NATIVE; + if (agg_kind == OP_STDDEV_POP && in_type == RAY_TIME) return &STDDEV_POP_TIME_NATIVE; + if (agg_kind == OP_SUM && in_type == RAY_TIME) return &SUM_TIME_NATIVE; + if (agg_kind == OP_MIN && in_type == RAY_TIMESTAMP) return &MIN_TIMESTAMP_NATIVE; + if (agg_kind == OP_MAX && in_type == RAY_TIMESTAMP) return &MAX_TIMESTAMP_NATIVE; + if (agg_kind == OP_AVG && in_type == RAY_TIMESTAMP) return &AVG_TIMESTAMP_NATIVE; + if (agg_kind == OP_VAR && in_type == RAY_TIMESTAMP) return &VAR_TIMESTAMP_NATIVE; + if (agg_kind == OP_VAR_POP && in_type == RAY_TIMESTAMP) return &VAR_POP_TIMESTAMP_NATIVE; + if (agg_kind == OP_STDDEV && in_type == RAY_TIMESTAMP) return &STDDEV_TIMESTAMP_NATIVE; + if (agg_kind == OP_STDDEV_POP && in_type == RAY_TIMESTAMP) return &STDDEV_POP_TIMESTAMP_NATIVE; if (agg_kind == OP_TOP_N && in_type == RAY_I64) return &TOPK_I64; if (agg_kind == OP_BOT_N && in_type == RAY_I64) return &BOTK_I64; if (agg_kind == OP_TOP_N && in_type == RAY_F64) return &TOPK_F64; if (agg_kind == OP_BOT_N && in_type == RAY_F64) return &BOTK_F64; if (agg_kind == OP_MEDIAN && in_type == RAY_I64) return &MEDIAN_I64; if (agg_kind == OP_MEDIAN && in_type == RAY_F64) return &MEDIAN_F64; - if (in_type == RAY_F64 || in_type == RAY_I64 || in_type == RAY_I32 || + if (in_type == RAY_F32 || in_type == RAY_F64 || in_type == RAY_I64 || in_type == RAY_I32 || in_type == RAY_I16 || in_type == RAY_U8 || in_type == RAY_BOOL) { if (agg_kind == OP_ALL) return &ALL_BOOL; if (agg_kind == OP_ANY) return &ANY_BOOL; diff --git a/src/ops/builtins.c b/src/ops/builtins.c index 054ecb3f9..f4e85a57e 100644 --- a/src/ops/builtins.c +++ b/src/ops/builtins.c @@ -168,7 +168,7 @@ void ray_lang_print(FILE* fp, ray_t* val) { } /* Helper: format string with % placeholders, substituting args. - * Returns a heap-allocated char* (caller must ray_sys_free) and sets *out_len. + * Returns a heap-allocated char* (caller must ray_free_raw) and sets *out_len. * If fmt has no %, returns NULL (caller falls back to plain print). */ static char* fmt_interpolate(const char* fmt, size_t flen, ray_t** args, int64_t nargs, int64_t arg_start, size_t* out_len) { /* Quick scan: any % in fmt? */ @@ -178,7 +178,7 @@ static char* fmt_interpolate(const char* fmt, size_t flen, ray_t** args, int64_t /* Build result in a dynamic buffer */ size_t cap = flen + 256; - char* buf = ray_sys_alloc(cap); + char* buf = ray_calloc_raw(cap); if (!buf) return NULL; size_t pos = 0; int64_t ai = arg_start; @@ -213,7 +213,7 @@ static char* fmt_interpolate(const char* fmt, size_t flen, ray_t** args, int64_t } else if (a->type == -RAY_STR) { const char* sp = ray_str_ptr(a); size_t sl = ray_str_len(a); - while (pos + sl + 1 > cap) { cap *= 2; buf = ray_sys_realloc(buf, cap); } + while (pos + sl + 1 > cap) { cap *= 2; buf = ray_realloc_raw(buf, cap); } memcpy(buf + pos, sp, sl); pos += sl; if (a_owned) ray_release(a_owned); @@ -225,7 +225,7 @@ static char* fmt_interpolate(const char* fmt, size_t flen, ray_t** args, int64_t size_t sl = ray_str_len(ss); /* sym 0 resolves to "" — the empty symbol shows as ' */ if (sl == 0) { sp = "'"; sl = 1; } - while (pos + sl + 1 > cap) { cap *= 2; buf = ray_sys_realloc(buf, cap); } + while (pos + sl + 1 > cap) { cap *= 2; buf = ray_realloc_raw(buf, cap); } memcpy(buf + pos, sp, sl); pos += sl; ray_release(ss); @@ -239,7 +239,7 @@ static char* fmt_interpolate(const char* fmt, size_t flen, ray_t** args, int64_t if (formatted && !RAY_IS_ERR(formatted)) { const char* sp = ray_str_ptr(formatted); size_t sl = ray_str_len(formatted); - while (pos + sl + 1 > cap) { cap *= 2; buf = ray_sys_realloc(buf, cap); } + while (pos + sl + 1 > cap) { cap *= 2; buf = ray_realloc_raw(buf, cap); } memcpy(buf + pos, sp, sl); pos += sl; ray_release(formatted); @@ -250,11 +250,11 @@ static char* fmt_interpolate(const char* fmt, size_t flen, ray_t** args, int64_t tlen = snprintf(tmp, sizeof(tmp), "", a->type); } if (a_owned && !RAY_IS_ERR(a_owned)) ray_release(a_owned); - while (pos + (size_t)tlen + 1 > cap) { cap *= 2; buf = ray_sys_realloc(buf, cap); } + while (pos + (size_t)tlen + 1 > cap) { cap *= 2; buf = ray_realloc_raw(buf, cap); } memcpy(buf + pos, tmp, (size_t)tlen); pos += (size_t)tlen; } else { - if (pos + 2 > cap) { cap *= 2; buf = ray_sys_realloc(buf, cap); } + if (pos + 2 > cap) { cap *= 2; buf = ray_realloc_raw(buf, cap); } buf[pos++] = fmt[i]; } } @@ -281,7 +281,7 @@ ray_t* ray_println_fn(ray_t** args, int64_t n) { fwrite(result, 1, out_len, stdout); fputc('\n', stdout); fflush(stdout); - ray_sys_free(result); + ray_free_raw(result); return RAY_NULL_OBJ; } } @@ -308,7 +308,7 @@ ray_t* ray_print_fn(ray_t** args, int64_t n) { if (result) { fwrite(result, 1, out_len, stdout); fflush(stdout); - ray_sys_free(result); + ray_free_raw(result); return RAY_NULL_OBJ; } } @@ -353,7 +353,7 @@ ray_t* ray_format_fn(ray_t** args, int64_t n) { char* result = fmt_interpolate(fmt, flen, args, n, 1, &out_len); if (result) { ray_t* s = ray_str(result, out_len); - ray_sys_free(result); + ray_free_raw(result); return s; } /* No placeholders: return fmt as-is */ @@ -2771,6 +2771,8 @@ ray_t* ray_group_indices_fn(ray_t* x) { if (!ray_is_vec(x) && x->type != RAY_LIST) return ray_error("type", "group: argument must be a vector or list, got %s", ray_type_name(x->type)); int64_t n = x->len; + ray_t* parallel = agg_group_indices(x); + if (parallel) return parallel; if (n == 0) { ray_t* keys = ray_list_new(0); if (RAY_IS_ERR(keys)) return keys; @@ -3294,7 +3296,7 @@ static ray_t* str_vec_concat_atom(ray_t* vec, ray_t* atom, bool atom_first) { char sbuf[8192]; char* buf = sbuf; if (total > sizeof(sbuf)) { - buf = ray_sys_alloc(total); + buf = ray_calloc_raw(total); if (!buf) { ray_release(out); return ray_error("oom", NULL); @@ -3309,7 +3311,7 @@ static ray_t* str_vec_concat_atom(ray_t* vec, ray_t* atom, bool atom_first) { } ray_t* prev = out; out = ray_str_vec_append(out, buf, total); - if (buf != sbuf) ray_sys_free(buf); + if (buf != sbuf) ray_free_raw(buf); if (RAY_IS_ERR(out)) { ray_release(prev); return out; diff --git a/src/ops/cdfuse.c b/src/ops/cdfuse.c index 5f7763d54..db90762e0 100644 --- a/src/ops/cdfuse.c +++ b/src/ops/cdfuse.c @@ -21,9 +21,9 @@ * SOFTWARE. */ -/* Fused grouped count-distinct (spec Part B). Single pass over rows: - * phase 1 scatters compact [pairhash][k][v][row] records into per-(worker, - * partition) buffers; phase 2 walks each partition once with two +/* Fused grouped count-distinct (spec Part B). A histogram sizes one payload; + * phase 1 scatters compact [pairhash][k][v][row] records into disjoint source/ + * partition slices; phase 2 walks each partition once with two * partition-local open-addressing tables — a (k,v) dedupe table and a k * table — producing PARTIAL per-key (distinct count, first_row) laid out in * key-hash buckets; phase 3 dispatches one task per bucket to merge those @@ -49,6 +49,7 @@ #include #include "rayforce.h" #include "core/pool.h" +#include "core/profile.h" #include "mem/heap.h" /* ray_heap_anon_watermark */ #include "ops/internal.h" /* scratch_*, read_col_i64 */ #include "ops/hash.h" /* ray_hash_i64 */ @@ -79,7 +80,7 @@ static inline uint64_t cdf_fmix64(uint64_t h) { return h; } -/* Growable per-(worker, partition) record buffer. */ +/* A disjoint source/partition slice of one query-owned record payload. */ typedef struct { char* buf; uint32_t n, cap; @@ -94,19 +95,6 @@ typedef struct { * ray_pool_dispatch_n never needs to grow its ring). */ #define CDF_MAX_PARTS 1024u -/* Reserve room for one more record; returns dest ptr or NULL on OOM. */ -static char* cdf_reserve(cdf_buf_t* b, uint32_t prime) { - if (b->n == b->cap) { - if (b->cap > UINT32_MAX / 2) return NULL; - uint32_t nc = b->cap ? b->cap * 2 : (prime ? prime : 64); - char* nb = (char*)ray_realloc_raw(b->buf, (size_t)nc * CDF_REC); - if (!nb) return NULL; - b->buf = nb; - b->cap = nc; - } - return b->buf + (size_t)b->n++ * CDF_REC; -} - /* ══════════════════════════════════════════ * Phase 1 — scatter * ══════════════════════════════════════════ */ @@ -120,45 +108,99 @@ typedef struct { uint8_t vattrs; uint32_t n_parts, nw; cdf_buf_t* bufs; /* [nw * n_parts] */ - uint32_t prime; /* first-allocation capacity per buf */ + int64_t nrows; + bool dedup; _Atomic(int) oom; - /* Rows actually scattered. ray_pool_dispatch clamps its task count to - * the ring capacity but RECOMPUTES the grain, so no row is dropped by - * clamping — however a cancelled pool (pool->cancelled) skips claimed - * tasks outright. Comparing this against nrows turns any such silent - * row loss into a NULL decline instead of a short answer. */ + /* Rows completed in each pass. Logical source tasks are bounded by the + * pool ring; cancellation must decline rather than return a short answer. */ _Atomic(int64_t) rows_done; } cdf_p1_ctx_t; -static void cdf_p1_fn(void* vctx, uint32_t wid, int64_t start, int64_t end) { - cdf_p1_ctx_t* c = (cdf_p1_ctx_t*)vctx; - if (atomic_load_explicit(&c->oom, memory_order_relaxed)) return; - cdf_buf_t* my = &c->bufs[(size_t)(wid % c->nw) * c->n_parts]; - for (int64_t r = start; r < end; r++) { - int64_t k = read_col_i64(c->kdata, r, c->ktype, c->kattrs); - int64_t v = read_col_i64(c->vdata, r, c->vtype, c->vattrs); - /* PAIR hash: uniform even when one key owns most of the table. - * The odd-multiplier on the k side is LOAD-BEARING, not decoration: a - * bare `hash(k) ^ hash(v)` cancels to 0 for every row where k == v, so - * a `(count (distinct k)) by: k` — or any strongly correlated column - * pair — would pile every row into partition 0 at dedupe slot 0 and run - * one giant serial probe cluster (measured: 2m42s on 20M rows vs 0.09s - * uncorrelated). Multiplying one side by the golden-ratio constant - * makes the combine asymmetric, so k == v hashes like any other pair. */ - uint64_t h = cdf_fmix64(ray_hash_i64(k) * 0x9E3779B97F4A7C15ULL ^ - ray_hash_i64(v)); - uint32_t p = (uint32_t)(h & (c->n_parts - 1)); - char* rec = cdf_reserve(&my[p], c->prime); - if (!rec) { - atomic_store_explicit(&c->oom, 1, memory_order_relaxed); - return; +static int64_t cdf_read(const void* data, int64_t r, int8_t type, uint8_t attrs) { + if (type == RAY_F32) { + float v = ((const float*)data)[r]; + if (v != v) return INT64_C(0x7fc00000); + if (v == 0) return 0; + uint32_t bits; memcpy(&bits, &v, sizeof(bits)); return bits; + } + if (type == RAY_F64) { + double v = ((const double*)data)[r]; + if (v != v) return INT64_C(0x7ff8000000000000); + if (v == 0) return 0; + int64_t bits; memcpy(&bits, &v, sizeof(bits)); return bits; + } + return read_col_i64(data, r, type, attrs); +} +/* Keep the pair hash asymmetric: hash(k)^hash(v) collapses correlated + * k==v inputs into one partition. Both passes use the same exact transform. */ +static inline uint64_t cdf_pair_hash(int64_t key, int64_t value) { + return cdf_fmix64(ray_hash_i64(key) * 0x9E3779B97F4A7C15ULL ^ ray_hash_i64(value)); +} +/* Bounded, exact source-local preaggregation. A collision only forgets an + * earlier pair; the partition deduper still sees and reconciles extra copies. + * Nothing persists across source tasks or queries. */ +typedef struct { int64_t key, value; bool used; } cdf_recent_t; +static bool cdf_repeat(cdf_recent_t* recent, uint64_t hash, int64_t key, int64_t value) { + cdf_recent_t* entry = &recent[hash & 63]; + if (entry->used && entry->key == key && entry->value == value) return true; + entry->key = key; entry->value = value; entry->used = true; + return false; +} +static bool cdf_sample_repetition(const cdf_p1_ctx_t* c) { + if (c->nrows < 1024) return false; + cdf_recent_t recent[64] = {{0}}; + int repeats = 0; + int64_t step = c->nrows / 1024; + for (int i = 0; i < 1024; i++) { + int64_t row = i * step + ray_hash_i64(i) % step; + int64_t key = cdf_read(c->kdata, row, c->ktype, c->kattrs); + int64_t value = cdf_read(c->vdata, row, c->vtype, c->vattrs); + repeats += cdf_repeat(recent, cdf_pair_hash(key, value), key, value); + } + return repeats >= 128; +} +static void cdf_p1_hist(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; cdf_p1_ctx_t* c = raw; + for (int64_t task = start; task < end; task++) { + cdf_buf_t* my = c->bufs + (size_t)task * c->n_parts; + cdf_recent_t recent[64]; + if (c->dedup) memset(recent, 0, sizeof(recent)); + int64_t begin = c->nrows / c->nw * task; + int64_t limit = task + 1 == c->nw ? c->nrows : c->nrows / c->nw * (task + 1); + for (int64_t r = begin; r < limit; r++) { + int64_t k = cdf_read(c->kdata, r, c->ktype, c->kattrs); + int64_t v = cdf_read(c->vdata, r, c->vtype, c->vattrs); + uint64_t hash = cdf_pair_hash(k, v); + if (c->dedup && cdf_repeat(recent, hash, k, v)) continue; + uint32_t part = hash & (c->n_parts - 1); + if (my[part].cap == UINT32_MAX) { atomic_store(&c->oom, 1); return; } + my[part].cap++; } - ((uint64_t*)rec)[0] = h; - ((int64_t*)rec)[1] = k; - ((int64_t*)rec)[2] = v; - ((int64_t*)rec)[3] = r; + atomic_fetch_add_explicit(&c->rows_done, limit - begin, memory_order_relaxed); + } +} +static void cdf_p1_fn(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; cdf_p1_ctx_t* c = raw; + for (int64_t task = start; task < end; task++) { + cdf_buf_t* my = c->bufs + (size_t)task * c->n_parts; + cdf_recent_t recent[64]; + if (c->dedup) memset(recent, 0, sizeof(recent)); + int64_t begin = c->nrows / c->nw * task; + int64_t limit = task + 1 == c->nw ? c->nrows : c->nrows / c->nw * (task + 1); + for (int64_t r = begin; r < limit; r++) { + int64_t k = cdf_read(c->kdata, r, c->ktype, c->kattrs); + int64_t v = cdf_read(c->vdata, r, c->vtype, c->vattrs); + uint64_t hash = cdf_pair_hash(k, v); + if (c->dedup && cdf_repeat(recent, hash, k, v)) continue; + uint32_t part = hash & (c->n_parts - 1); + char* record = my[part].buf + (size_t)my[part].n++ * CDF_REC; + ((uint64_t*)record)[0] = hash; + ((int64_t*)record)[1] = k; + ((int64_t*)record)[2] = v; + ((int64_t*)record)[3] = r; + } + atomic_fetch_add_explicit(&c->rows_done, limit - begin, memory_order_relaxed); } - atomic_fetch_add_explicit(&c->rows_done, end - start, memory_order_relaxed); } /* ══════════════════════════════════════════ @@ -480,6 +522,22 @@ static int cdf_grp_cmp(const void* a, const void* b) { return x < y ? -1 : (x > y ? 1 : 0); } +typedef struct { + const cdf_grp_t* groups; + const uint64_t* order; + uint64_t mask; + int64_t* keys; + int64_t* counts; + int64_t* firsts; +} cdf_emit_t; +static void cdf_emit(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; cdf_emit_t* c = raw; + for (int64_t i = start; i < end; i++) { + const cdf_grp_t* group = &c->groups[c->order ? c->order[i] & c->mask : (uint64_t)i]; + c->keys[i] = group->key; c->counts[i] = group->cnt; c->firsts[i] = group->first; + } +} + /* Same sqrt-style sizing as agg_radix_part_count: at least one partition per * worker, and enough partitions that each holds ~sqrt(nrows) records — capped * at CDF_MAX_PARTS to bound phase 3's merge input. */ @@ -493,14 +551,15 @@ static uint32_t cdf_part_count(uint32_t nworkers, int64_t nrows) { } static int cdf_type_ok(int8_t t) { - return t == RAY_I64 || t == RAY_I32 || t == RAY_I16 || RAY_IS_SYM(t); + return t == RAY_BOOL || t == RAY_U8 || t == RAY_I16 || + t == RAY_I32 || t == RAY_I64 || t == RAY_DATE || + t == RAY_TIME || t == RAY_TIMESTAMP || t == RAY_F32 || t == RAY_F64 || RAY_IS_SYM(t); } -/* Free every phase-1/2 buffer; used by both the fallback and success paths. */ -static void cdf_free_all(cdf_buf_t* bufs, size_t nbuf, cdf_part_t* parts, +/* Free the shared payload and phase-2 outputs on fallback and success. */ +static void cdf_free_all(cdf_buf_t* bufs, char* records, cdf_part_t* parts, uint32_t n_parts) { - if (bufs) - for (size_t i = 0; i < nbuf; i++) ray_free_raw(bufs[i].buf); + ray_free_raw(records); ray_free_raw(bufs); if (parts) for (uint32_t p = 0; p < n_parts; p++) ray_free_raw(parts[p].g); @@ -511,9 +570,7 @@ ray_t* ray_cd_fused(ray_t* key_col, ray_t* val_col, int64_t nrows) { if (!key_col || !val_col || nrows <= 0) return NULL; if (!ray_is_vec(key_col) || !ray_is_vec(val_col)) return NULL; if (!cdf_type_ok(key_col->type) || !cdf_type_ok(val_col->type)) return NULL; - if (ray_vec_may_have_nulls(key_col) || - ray_vec_may_have_nulls(val_col)) - return NULL; + if (key_col->len < nrows || val_col->len < nrows) return NULL; if (nrows < CDF_MIN_ROWS) return NULL; /* small: existing path fine */ @@ -543,14 +600,17 @@ ray_t* ray_cd_fused(ray_t* key_col, ray_t* val_col, int64_t nrows) { if (wm > 0 && (double)nrows * CDF_BYTES_PER_ROW > (double)wm / 4.0) return NULL; - uint32_t nw = ray_pool_total_workers(pool); - - uint32_t n_parts = cdf_part_count(nw, nrows); + if ((uint64_t)nrows > SIZE_MAX / CDF_REC) return NULL; + uint32_t workers = ray_pool_total_workers(pool); + uint32_t nw = workers * 4; + if (nw > RAY_POOL_INIT_TASKS) nw = RAY_POOL_INIT_TASKS; + uint32_t n_parts = cdf_part_count(workers, nrows); + char* records = NULL; size_t nbuf = (size_t)nw * n_parts; cdf_buf_t* bufs = (cdf_buf_t*)ray_calloc_raw(nbuf * sizeof(cdf_buf_t)); cdf_part_t* parts = (cdf_part_t*)ray_calloc_raw((size_t)n_parts * sizeof(cdf_part_t)); if (!bufs || !parts) { - cdf_free_all(bufs, nbuf, parts, n_parts); + cdf_free_all(bufs, records, parts, n_parts); return NULL; } @@ -558,15 +618,32 @@ ray_t* ray_cd_fused(ray_t* key_col, ray_t* val_col, int64_t nrows) { .kdata = ray_data(key_col), .ktype = key_col->type, .kattrs = key_col->attrs, .vdata = ray_data(val_col), .vtype = val_col->type, .vattrs = val_col->attrs, .n_parts = n_parts, .nw = nw, .bufs = bufs, - /* Uniform-hash expectation with 25% slack; ≥8 so tiny buffers don't - * immediately re-double. */ - .prime = (uint32_t)((uint64_t)nrows / ((uint64_t)nw * n_parts) * 5 / 4 + 8), + .nrows = nrows, .oom = 0, .rows_done = 0, }; - ray_pool_dispatch(pool, cdf_p1_fn, &p1, nrows); + p1.dedup = cdf_sample_repetition(&p1); + if (p1.dedup) ray_profile_tick("count-distinct: source preaggregation"); + ray_profile_tick("count-distinct: prepared"); + ray_pool_dispatch_n(pool, cdf_p1_hist, &p1, nw); + if (atomic_load(&p1.oom) || atomic_load(&p1.rows_done) != nrows) { + cdf_free_all(bufs, records, parts, n_parts); return NULL; + } + int64_t record_count = 0; + for (size_t b = 0; b < nbuf; b++) record_count += bufs[b].cap; + records = ray_alloc_raw((size_t)record_count * CDF_REC); + if (!records) { cdf_free_all(bufs, records, parts, n_parts); return NULL; } + int64_t record_offset = 0; + for (size_t b = 0; b < nbuf; b++) { + bufs[b].buf = records + (size_t)record_offset * CDF_REC; + record_offset += bufs[b].cap; + } + atomic_store(&p1.rows_done, 0); + ray_profile_tick("count-distinct: histogram"); + ray_pool_dispatch_n(pool, cdf_p1_fn, &p1, nw); + ray_profile_tick("count-distinct: scattered pairs"); if (atomic_load_explicit(&p1.oom, memory_order_relaxed) || atomic_load_explicit(&p1.rows_done, memory_order_relaxed) != nrows) { - cdf_free_all(bufs, nbuf, parts, n_parts); + cdf_free_all(bufs, records, parts, n_parts); return NULL; /* fallback, not an error */ } @@ -575,9 +652,10 @@ ray_t* ray_cd_fused(ray_t* key_col, ray_t* val_col, int64_t nrows) { .part_bits = (uint32_t)__builtin_ctz(n_parts), .oom = 0, .done = 0, }; ray_pool_dispatch_n(pool, cdf_p2_fn, &p2, n_parts); + ray_profile_tick("count-distinct: deduplicated pairs"); if (atomic_load_explicit(&p2.oom, memory_order_relaxed) || atomic_load_explicit(&p2.done, memory_order_relaxed) != (int64_t)n_parts) { - cdf_free_all(bufs, nbuf, parts, n_parts); + cdf_free_all(bufs, records, parts, n_parts); return NULL; } @@ -597,13 +675,14 @@ ray_t* ray_cd_fused(ray_t* key_col, ray_t* val_col, int64_t nrows) { .parts = parts, .n_parts = n_parts, .mg = mg, .oom = 0, .done = 0, }; ray_pool_dispatch_n(pool, cdf_p3_fn, &p3, CDF_MERGE_PARTS); + ray_profile_tick("count-distinct: merged counts"); if (atomic_load_explicit(&p3.oom, memory_order_relaxed) || atomic_load_explicit(&p3.done, memory_order_relaxed) != CDF_MERGE_PARTS) { for (uint32_t m = 0; m < CDF_MERGE_PARTS; m++) ray_free_raw(mg[m].g); - cdf_free_all(bufs, nbuf, parts, n_parts); + cdf_free_all(bufs, records, parts, n_parts); return NULL; } - cdf_free_all(bufs, nbuf, parts, n_parts); + cdf_free_all(bufs, records, parts, n_parts); int64_t ng = 0; for (uint32_t m = 0; m < CDF_MERGE_PARTS; m++) ng += mg[m].n; @@ -620,8 +699,32 @@ ray_t* ray_cd_fused(ray_t* key_col, ray_t* val_col, int64_t nrows) { mo += mg[m].n; ray_free_raw(mg[m].g); } - /* Global stable first-seen order. */ - qsort(merged, (size_t)ng, sizeof(cdf_grp_t), cdf_grp_cmp); + /* Restore stable first-seen order with integer radix sorting. The packed + * row/order key replaces a serial comparison sort of 24-byte records. + * Free the unused sort buffer before allocating outputs: peak remains + * at most 56 bytes per group, within the existing row-based admission. */ + uint64_t* order = NULL; + uint64_t mask = 0; + if (ng >= RADIX_SORT_THRESHOLD && nrows > 1) { + unsigned bits = 64 - __builtin_clzll((uint64_t)ng - 1); + unsigned row_bits = 64 - __builtin_clzll((uint64_t)nrows - 1); + if (bits + row_bits <= 64) { + uint64_t* a = ray_alloc_raw((size_t)ng * sizeof(uint64_t)); + uint64_t* b = ray_alloc_raw((size_t)ng * sizeof(uint64_t)); + if (a && b) { + mask = (UINT64_C(1) << bits) - 1; + for (int64_t i = 0; i < ng; i++) a[i] = ((uint64_t)merged[i].first << bits) | (uint64_t)i; + order = packed_radix_sort_run(pool, a, b, ng, (uint8_t)((bits + row_bits + 7) / 8)); + } + if (a != order) ray_free_raw(a); + if (b != order) ray_free_raw(b); + } + } + if (atomic_load_explicit(&pool->cancelled, memory_order_relaxed)) { + ray_free_raw(order); ray_free_raw(merged); return NULL; + } + if (!order) qsort(merged, (size_t)ng, sizeof(cdf_grp_t), cdf_grp_cmp); + ray_profile_tick("count-distinct: ordered groups"); ray_t* keys = ray_vec_new(RAY_I64, ng); ray_t* cnts = ray_vec_new(RAY_I64, ng); @@ -630,19 +733,16 @@ ray_t* ray_cd_fused(ray_t* key_col, ray_t* val_col, int64_t nrows) { if (!keys || RAY_IS_ERR(keys) || !cnts || RAY_IS_ERR(cnts) || !firsts || RAY_IS_ERR(firsts) || !tbl || RAY_IS_ERR(tbl)) { ray_release(keys); ray_release(cnts); ray_release(firsts); ray_release(tbl); - ray_free_raw(merged); + ray_free_raw(order); ray_free_raw(merged); return NULL; } keys->len = cnts->len = firsts->len = ng; - int64_t* kd = (int64_t*)ray_data(keys); - int64_t* cd = (int64_t*)ray_data(cnts); - int64_t* fd = (int64_t*)ray_data(firsts); - for (int64_t i = 0; i < ng; i++) { - kd[i] = merged[i].key; - cd[i] = merged[i].cnt; - fd[i] = merged[i].first; + cdf_emit_t emit = {merged, order, mask, ray_data(keys), ray_data(cnts), ray_data(firsts)}; + ray_pool_dispatch(pool, cdf_emit, &emit, ng); + ray_free_raw(order); ray_free_raw(merged); + if (atomic_load_explicit(&pool->cancelled, memory_order_relaxed)) { + ray_release(keys); ray_release(cnts); ray_release(firsts); ray_release(tbl); return NULL; } - ray_free_raw(merged); tbl = ray_table_add_col(tbl, ray_sym_intern("k", 1), keys); tbl = ray_table_add_col(tbl, ray_sym_intern("u", 1), cnts); diff --git a/src/ops/cdfuse.h b/src/ops/cdfuse.h index a141b71dc..5cc5f6782 100644 --- a/src/ops/cdfuse.h +++ b/src/ops/cdfuse.h @@ -40,7 +40,8 @@ * (keys, distinct counts, first source row) in stable first-seen key * order, or NULL when the shape is unsupported (caller falls back to the * existing rewrite). key_col/val_col must be flat vectors of - * I64/I32/I16/SYM with no nulls. */ + * numeric/temporal/SYM types, including nulls. Float keys in `k` are + * canonical bit encodings; callers restore the source logical type. */ ray_t* ray_cd_fused(ray_t* key_col, ray_t* val_col, int64_t nrows); #endif /* RAY_OPS_CDFUSE_H */ diff --git a/src/ops/collection.c b/src/ops/collection.c index e91a7103f..b3e29db13 100644 --- a/src/ops/collection.c +++ b/src/ops/collection.c @@ -26,7 +26,6 @@ #include "lang/internal.h" #include "core/types.h" #include "core/pool.h" -#include "mem/sys.h" #include "ops/hash.h" #include "ops/internal.h" /* col_propagate_str_pool */ #include "ops/idxop.h" @@ -394,7 +393,7 @@ static bool find_hash_needles(ray_t* hay, ray_t* nd, int64_t* out, bool* any_nul int64_t m = nd->len, n = hay->len; hashset_t hs; if (!hashset_init(&hs, nd, m)) return false; - int64_t* first = (int64_t*)ray_sys_alloc((size_t)(m > 0 ? m : 1) * sizeof(int64_t)); + int64_t* first = (int64_t*)ray_alloc_raw((size_t)(m > 0 ? m : 1) * sizeof(int64_t)); if (!first) { hashset_destroy(&hs); return false; } int64_t distinct = 0; for (int64_t j = 0; j < m; j++) { @@ -414,7 +413,7 @@ static bool find_hash_needles(ray_t* hay, ray_t* nd, int64_t* out, bool* any_nul if (pos < 0) { out[i] = NULL_I64; *any_null = true; } else out[i] = pos; } - ray_sys_free(first); + ray_free_raw(first); hashset_destroy(&hs); return true; } @@ -1043,7 +1042,7 @@ ray_t* distinct_vec_eager(ray_t* x) { } int64_t idx_stack[256]; - int64_t* idx = (len <= 256) ? idx_stack : (int64_t*)ray_sys_alloc((size_t)len * sizeof(int64_t)); + int64_t* idx = (len <= 256) ? idx_stack : (int64_t*)ray_alloc_raw((size_t)len * sizeof(int64_t)); if (!idx) return ray_error("oom", NULL); /* SYM presence-bitmap dedup: a SYM code is a position in [0, domain_count), @@ -1054,9 +1053,8 @@ ray_t* distinct_vec_eager(ray_t* x) { if (x->type == RAY_SYM) { int64_t dc = ray_sym_domain_count(ray_sym_vec_domain(x)); if (dc > 0 && dc <= 65536 && dc <= len) { - uint8_t* seen = (uint8_t*)ray_sys_alloc((size_t)dc); - if (!seen) { if (idx != idx_stack) ray_sys_free(idx); return ray_error("oom", NULL); } - memset(seen, 0, (size_t)dc); + uint8_t* seen = (uint8_t*)ray_calloc_raw((size_t)dc); + if (!seen) { if (idx != idx_stack) ray_free_raw(idx); return ray_error("oom", NULL); } const void* data = ray_data(x); int8_t ty = x->type; uint16_t at = x->attrs; int64_t count = 0; @@ -1064,9 +1062,9 @@ ray_t* distinct_vec_eager(ray_t* x) { int64_t code = ray_read_sym(data, i, ty, at); if (!seen[code]) { seen[code] = 1; idx[count++] = i; } } - ray_sys_free(seen); + ray_free_raw(seen); ray_t* result = gather_by_idx(x, idx, count); - if (idx != idx_stack) ray_sys_free(idx); + if (idx != idx_stack) ray_free_raw(idx); return result; } } @@ -1082,17 +1080,16 @@ ray_t* distinct_vec_eager(ray_t* x) { if (nd > 0 && nd <= len && codes_v && !RAY_IS_ERR(codes_v) && codes_v->len == len) { const int32_t* codes = (const int32_t*)ray_data(codes_v); - uint8_t* seen = (uint8_t*)ray_sys_alloc((size_t)nd); - if (!seen) { if (idx != idx_stack) ray_sys_free(idx); return ray_error("oom", NULL); } - memset(seen, 0, (size_t)nd); + uint8_t* seen = (uint8_t*)ray_calloc_raw((size_t)nd); + if (!seen) { if (idx != idx_stack) ray_free_raw(idx); return ray_error("oom", NULL); } int64_t count = 0; for (int64_t i = 0; i < len; i++) { int32_t c = codes[i]; if (c >= 0 && c < nd && !seen[c]) { seen[c] = 1; idx[count++] = i; } } - ray_sys_free(seen); + ray_free_raw(seen); ray_t* result = gather_by_idx(x, idx, count); - if (idx != idx_stack) ray_sys_free(idx); + if (idx != idx_stack) ray_free_raw(idx); return result; } } @@ -1106,7 +1103,7 @@ ray_t* distinct_vec_eager(ray_t* x) { int64_t count = 0; ray_t* rids = distinct_radix_first_ids(x); if (rids && RAY_IS_ERR(rids)) { - if (idx != idx_stack) ray_sys_free(idx); + if (idx != idx_stack) ray_free_raw(idx); return rids; } if (rids) { @@ -1116,7 +1113,7 @@ ray_t* distinct_vec_eager(ray_t* x) { } else { hashset_t hs; if (!hashset_init(&hs, x, len)) { - if (idx != idx_stack) ray_sys_free(idx); + if (idx != idx_stack) ray_free_raw(idx); return ray_error("oom", NULL); } for (int64_t i = 0; i < len; i++) { @@ -1132,7 +1129,7 @@ ray_t* distinct_vec_eager(ray_t* x) { } ray_t* result = gather_by_idx(x, idx, count); - if (idx != idx_stack) ray_sys_free(idx); + if (idx != idx_stack) ray_free_raw(idx); return result; } @@ -1355,20 +1352,20 @@ ray_t* ray_in_fn(ray_t* val, ray_t* vec) { * null-free sets only), matching the hashset path below. */ if (ray_index_has(vec)) { ray_idx_consults[IDX_SITE_IN]++; - int64_t* pos = (int64_t*)ray_sys_alloc((size_t)vlen * sizeof(int64_t)); + int64_t* pos = (int64_t*)ray_calloc_raw((size_t)vlen * sizeof(int64_t)); if (!pos) return ray_error("oom", NULL); bool miss = false; if (ray_index_find_vec(vec, val, pos, &miss)) { ray_idx_hits[IDX_SITE_IN]++; ray_t* result = ray_vec_new(RAY_BOOL, vlen); - if (RAY_IS_ERR(result)) { ray_sys_free(pos); return result; } + if (RAY_IS_ERR(result)) { ray_free_raw(pos); return result; } result->len = vlen; bool* out = (bool*)ray_data(result); for (int64_t i = 0; i < vlen; i++) out[i] = pos[i] != NULL_I64; - ray_sys_free(pos); + ray_free_raw(pos); return result; } - ray_sys_free(pos); + ray_free_raw(pos); } /* Typed kernel first: verdict-LUT for SYM, SIMD small-set for * ints, pool-parallel — the same engine the fused WHERE path @@ -1389,14 +1386,14 @@ ray_t* ray_in_fn(ray_t* val, ray_t* vec) { /* Fewer needles than set rows: hash the needles and scan the * set once instead of hashing the whole set per call. */ if (find_needle_side_ok(vec, val)) { - int64_t* pos = (int64_t*)ray_sys_alloc((size_t)vlen * sizeof(int64_t)); + int64_t* pos = (int64_t*)ray_calloc_raw((size_t)vlen * sizeof(int64_t)); bool miss = false; if (pos && find_hash_needles(vec, val, pos, &miss)) { for (int64_t i = 0; i < vlen; i++) out[i] = pos[i] != NULL_I64; - ray_sys_free(pos); + ray_free_raw(pos); return result; } - if (pos) ray_sys_free(pos); + if (pos) ray_free_raw(pos); } hashset_t hs; if (!hashset_init(&hs, vec, vec->len)) { @@ -1536,7 +1533,7 @@ ray_t* ray_except_fn(ray_t* vec1, ray_t* vec2) { if (ray_is_vec(vec1) && (ray_is_vec(vec2) || ray_is_atom(vec2))) { int64_t len1 = vec1->len; int64_t idx_stack[256]; - int64_t* idx = (len1 <= 256) ? idx_stack : (int64_t*)ray_sys_alloc((size_t)len1 * sizeof(int64_t)); + int64_t* idx = (len1 <= 256) ? idx_stack : (int64_t*)ray_alloc_raw((size_t)len1 * sizeof(int64_t)); if (!idx) return ray_error("oom", NULL); int64_t count = 0; if (ray_is_atom(vec2)) { @@ -1551,7 +1548,7 @@ ray_t* ray_except_fn(ray_t* vec1, ray_t* vec2) { } else { hashset_t hs; if (!hashset_init(&hs, vec2, vec2->len)) { - if (idx != idx_stack) ray_sys_free(idx); + if (idx != idx_stack) ray_free_raw(idx); return ray_error("oom", NULL); } for (int64_t j = 0; j < vec2->len; j++) hashset_insert(&hs, j); @@ -1564,7 +1561,7 @@ ray_t* ray_except_fn(ray_t* vec1, ray_t* vec2) { hashset_destroy(&hs); } ray_t* result = gather_by_idx(vec1, idx, count); - if (idx != idx_stack) ray_sys_free(idx); + if (idx != idx_stack) ray_free_raw(idx); return result; } @@ -1616,11 +1613,11 @@ ray_t* ray_union_fn(ray_t* vec1, ray_t* vec2) { if (ray_is_vec(vec1) && ray_is_vec(vec2)) { int64_t len2 = vec2->len; int64_t idx_stack[256]; - int64_t* idx = (len2 <= 256) ? idx_stack : (int64_t*)ray_sys_alloc((size_t)len2 * sizeof(int64_t)); + int64_t* idx = (len2 <= 256) ? idx_stack : (int64_t*)ray_alloc_raw((size_t)len2 * sizeof(int64_t)); if (!idx) return ray_error("oom", NULL); hashset_t hs; if (!hashset_init(&hs, vec1, vec1->len)) { - if (idx != idx_stack) ray_sys_free(idx); + if (idx != idx_stack) ray_free_raw(idx); return ray_error("oom", NULL); } for (int64_t j = 0; j < vec1->len; j++) hashset_insert(&hs, j); @@ -1633,7 +1630,7 @@ ray_t* ray_union_fn(ray_t* vec1, ray_t* vec2) { } hashset_destroy(&hs); ray_t* part2 = gather_by_idx(vec2, idx, extra); - if (idx != idx_stack) ray_sys_free(idx); + if (idx != idx_stack) ray_free_raw(idx); if (RAY_IS_ERR(part2)) return part2; ray_t* result = ray_concat_fn(vec1, part2); ray_release(part2); @@ -1678,11 +1675,11 @@ ray_t* ray_sect_fn(ray_t* vec1, ray_t* vec2) { if (ray_is_vec(vec1) && ray_is_vec(vec2)) { int64_t len1 = vec1->len; int64_t idx_stack[256]; - int64_t* idx = (len1 <= 256) ? idx_stack : (int64_t*)ray_sys_alloc((size_t)len1 * sizeof(int64_t)); + int64_t* idx = (len1 <= 256) ? idx_stack : (int64_t*)ray_alloc_raw((size_t)len1 * sizeof(int64_t)); if (!idx) return ray_error("oom", NULL); hashset_t hs; if (!hashset_init(&hs, vec2, vec2->len)) { - if (idx != idx_stack) ray_sys_free(idx); + if (idx != idx_stack) ray_free_raw(idx); return ray_error("oom", NULL); } for (int64_t j = 0; j < vec2->len; j++) hashset_insert(&hs, j); @@ -1695,7 +1692,7 @@ ray_t* ray_sect_fn(ray_t* vec1, ray_t* vec2) { } hashset_destroy(&hs); ray_t* result = gather_by_idx(vec1, idx, count); - if (idx != idx_stack) ray_sys_free(idx); + if (idx != idx_stack) ray_free_raw(idx); return result; } @@ -4310,7 +4307,7 @@ static ray_t* map_iterate(ray_t* fn, ray_t* fixed, ray_t* vec, int fixed_is_left ray_t* stack_results[4096]; ray_t** results = stack_results; if (vn > 4096) { - results = (ray_t**)ray_sys_alloc((size_t)vn * sizeof(ray_t*)); + results = (ray_t**)ray_calloc_raw((size_t)vn * sizeof(ray_t*)); if (!results) return ray_error("oom", NULL); } @@ -4325,13 +4322,13 @@ static ray_t* map_iterate(ray_t* fn, ray_t* fixed, ray_t* vec, int fixed_is_left if (RAY_IS_ERR(results[i])) { ray_t* err = results[i]; for (int64_t j = 0; j < i; j++) ray_release(results[j]); - if (results != stack_results) ray_sys_free(results); + if (results != stack_results) ray_free_raw(results); return err; } } ray_t* out = ray_enlist_fn(results, vn); for (int64_t i = 0; i < vn; i++) ray_release(results[i]); - if (results != stack_results) ray_sys_free(results); + if (results != stack_results) ray_free_raw(results); return out; } diff --git a/src/ops/datalog.c b/src/ops/datalog.c index 3f3e5ddbc..7d885056b 100644 --- a/src/ops/datalog.c +++ b/src/ops/datalog.c @@ -33,7 +33,6 @@ #include "ops/ops.h" #include "ops/hash.h" /* ray_hash_i64, ray_hash_combine */ #include "ops/internal.h" /* col_propagate_str_pool */ -#include "mem/sys.h" /* ray_sys_alloc / ray_sys_free */ #include "lang/format.h" /* ray_type_name (error context) */ #include #include @@ -2722,7 +2721,7 @@ static bool dl_rowset_init(dl_rowset_t* rs, ray_t* ref) { int64_t ncols = ray_table_ncols(ref); int64_t nrows = ray_table_nrows(ref); rs->ncols = ncols; - rs->ref_cols = (int64_t**)ray_sys_alloc(sizeof(int64_t*) * (size_t)ncols); + rs->ref_cols = (int64_t**)ray_calloc_raw(sizeof(int64_t*) * (size_t)ncols); if (!rs->ref_cols) return false; for (int64_t c = 0; c < ncols; c++) { ray_t* col = ray_table_get_col_idx(ref, c); @@ -2732,7 +2731,7 @@ static bool dl_rowset_init(dl_rowset_t* rs, ray_t* ref) { while (cap < (nrows > 0 ? nrows * 2 : 16)) cap *= 2; rs->block = ray_alloc((size_t)cap * sizeof(int64_t)); if (!rs->block || RAY_IS_ERR(rs->block)) { - ray_sys_free(rs->ref_cols); + ray_free_raw(rs->ref_cols); rs->ref_cols = NULL; return false; } @@ -2752,7 +2751,7 @@ static bool dl_rowset_init(dl_rowset_t* rs, ray_t* ref) { static void dl_rowset_destroy(dl_rowset_t* rs) { if (rs->block) { ray_release(rs->block); rs->block = NULL; } - if (rs->ref_cols) { ray_sys_free(rs->ref_cols); rs->ref_cols = NULL; } + if (rs->ref_cols) { ray_free_raw(rs->ref_cols); rs->ref_cols = NULL; } } /* True if the row at `tbl_cols[..][row]` is present in the set. */ @@ -2938,7 +2937,7 @@ static void dl_build_provenance(dl_program_t* prog) { dl_rowset_t rs; if (dl_rowset_init(&rs, derived)) { int64_t ncols_t = ray_table_ncols(rel->table); - int64_t** tbl_cols = (int64_t**)ray_sys_alloc(sizeof(int64_t*) * (size_t)ncols_t); + int64_t** tbl_cols = (int64_t**)ray_calloc_raw(sizeof(int64_t*) * (size_t)ncols_t); if (tbl_cols) { for (int64_t c = 0; c < ncols_t; c++) { ray_t* col = ray_table_get_col_idx(rel->table, c); @@ -2949,7 +2948,7 @@ static void dl_build_provenance(dl_program_t* prog) { if (dl_rowset_contains(&rs, tbl_cols, row)) pd[row] = r; } - ray_sys_free(tbl_cols); + ray_free_raw(tbl_cols); } dl_rowset_destroy(&rs); } diff --git a/src/ops/embedding.c b/src/ops/embedding.c index 4eb0f4287..855cb67f7 100644 --- a/src/ops/embedding.c +++ b/src/ops/embedding.c @@ -79,12 +79,12 @@ static int list_vec_validate(ray_t* list, int32_t* out_dim) { } /* Flatten LIST of numeric vectors into a new float[] buffer. - * Caller frees with ray_sys_free. */ + * Caller frees with ray_free_raw. */ static float* list_flatten_floats(ray_t* list, int32_t dim, int64_t* out_n) { int64_t n = list->len; *out_n = n; if (n == 0) return NULL; - float* buf = (float*)ray_sys_alloc((size_t)n * (size_t)dim * sizeof(float)); + float* buf = (float*)ray_calloc_raw((size_t)n * (size_t)dim * sizeof(float)); if (!buf) return NULL; for (int64_t i = 0; i < n; i++) { ray_t* e = ray_list_get(list, i); @@ -124,7 +124,7 @@ static double row_score(metric_kind_t k, ray_t* row, /* Extract query vector to a double[] scratch buffer. */ static double* query_to_doubles(ray_t* q, int32_t dim, double* q_norm_out) { - double* buf = (double*)ray_sys_alloc((size_t)dim * sizeof(double)); + double* buf = (double*)ray_calloc_raw((size_t)dim * sizeof(double)); if (!buf) return NULL; double ns = 0.0; for (int32_t j = 0; j < dim; j++) { @@ -171,7 +171,7 @@ static ray_t* vec_binary_metric(metric_kind_t kind, ray_t* a, ray_t* b) { int64_t n = list->len; ray_t* result = ray_vec_new(RAY_F64, n); - if (!result || RAY_IS_ERR(result)) { ray_sys_free(q); return ray_error("oom", NULL); } + if (!result || RAY_IS_ERR(result)) { ray_free_raw(q); return ray_error("oom", NULL); } result->len = n; double* out = (double*)ray_data(result); for (int64_t i = 0; i < n; i++) { @@ -180,7 +180,7 @@ static ray_t* vec_binary_metric(metric_kind_t kind, ray_t* a, ray_t* b) { * non-finite (NaN/Inf) → canonicalize to NULL_F64. */ out[i] = ray_f64_fin(row_score(kind, row, q, q_norm, dim)); } - ray_sys_free(q); + ray_free_raw(q); mark_f64_nonfinite_as_null(result, 0, n); return result; } @@ -198,7 +198,7 @@ static ray_t* vec_binary_metric(metric_kind_t kind, ray_t* a, ray_t* b) { double* q = query_to_doubles(b, dim, &q_norm); if (!q) return ray_error("oom", NULL); double v = row_score(kind, a, q, q_norm, dim); - ray_sys_free(q); + ray_free_raw(q); return make_f64(v); } @@ -313,8 +313,8 @@ ray_t* ray_knn_fn(ray_t** args, int64_t n) { /* Max-heap on distance (root = farthest of top-K kept). */ typedef struct { double d; int64_t id; } ent_t; - ent_t* heap = (ent_t*)ray_sys_alloc((size_t)k * sizeof(ent_t)); - if (!heap) { ray_sys_free(q); return ray_error("oom", NULL); } + ent_t* heap = (ent_t*)ray_calloc_raw((size_t)k * sizeof(ent_t)); + if (!heap) { ray_free_raw(q); return ray_error("oom", NULL); } int64_t hsz = 0; for (int64_t i = 0; i < nrows; i++) { @@ -357,7 +357,7 @@ ray_t* ray_knn_fn(ray_t** args, int64_t n) { } } - ray_sys_free(q); + ray_free_raw(q); /* Sort ascending by distance. */ for (int64_t i = 1; i < hsz; i++) { @@ -373,7 +373,7 @@ ray_t* ray_knn_fn(ray_t** args, int64_t n) { ray_t* rv = ray_vec_new(RAY_I64, hsz); ray_t* dv = ray_vec_new(RAY_F64, hsz); if (!rv || RAY_IS_ERR(rv) || !dv || RAY_IS_ERR(dv)) { - ray_sys_free(heap); + ray_free_raw(heap); if (rv && !RAY_IS_ERR(rv)) ray_release(rv); if (dv && !RAY_IS_ERR(dv)) ray_release(dv); return ray_error("oom", NULL); @@ -383,7 +383,7 @@ ray_t* ray_knn_fn(ray_t** args, int64_t n) { for (int64_t i = 0; i < hsz; i++) { rd[i] = heap[i].id; dd[i] = heap[i].d; } rv->len = hsz; dv->len = hsz; - ray_sys_free(heap); + ray_free_raw(heap); ray_t* tbl = ray_table_new(2); if (!tbl || RAY_IS_ERR(tbl)) { ray_release(rv); ray_release(dv); return ray_error("oom", NULL); } @@ -449,7 +449,7 @@ ray_t* ray_hnsw_build_fn(ray_t** args, int64_t n) { ray_hnsw_t* idx = ray_hnsw_build(flat, n_rows, dim, metric, M, ef_c); /* ray_hnsw_build COPIES the vectors (idx->owns_data == true), so free our scratch. */ - if (flat) ray_sys_free(flat); + if (flat) ray_free_raw(flat); if (!idx) return ray_interrupted() ? ray_error("cancel", "interrupted") : ray_error("oom", NULL); @@ -485,29 +485,29 @@ ray_t* ray_ann_fn(ray_t** args, int64_t n) { } /* Copy query into float[] scratch. */ - float* qbuf = (float*)ray_sys_alloc((size_t)dim * sizeof(float)); + float* qbuf = (float*)ray_calloc_raw((size_t)dim * sizeof(float)); if (!qbuf) return ray_error("oom", NULL); rayvec_to_floats(args[1], qbuf, dim); - int64_t* out_ids = (int64_t*)ray_sys_alloc((size_t)k * sizeof(int64_t)); - double* out_ds = (double*)ray_sys_alloc((size_t)k * sizeof(double)); + int64_t* out_ids = (int64_t*)ray_calloc_raw((size_t)k * sizeof(int64_t)); + double* out_ds = (double*)ray_calloc_raw((size_t)k * sizeof(double)); if (!out_ids || !out_ds) { - ray_sys_free(qbuf); - if (out_ids) ray_sys_free(out_ids); - if (out_ds) ray_sys_free(out_ds); + ray_free_raw(qbuf); + if (out_ids) ray_free_raw(out_ids); + if (out_ds) ray_free_raw(out_ds); return ray_error("oom", NULL); } int64_t found = ray_hnsw_search(idx, qbuf, dim, k, ef, out_ids, out_ds); if (found < 0) { - ray_sys_free(qbuf); ray_sys_free(out_ids); ray_sys_free(out_ds); + ray_free_raw(qbuf); ray_free_raw(out_ids); ray_free_raw(out_ds); return ray_error("oom", NULL); } ray_t* rv = ray_vec_new(RAY_I64, found); ray_t* dv = ray_vec_new(RAY_F64, found); if (!rv || RAY_IS_ERR(rv) || !dv || RAY_IS_ERR(dv)) { - ray_sys_free(qbuf); ray_sys_free(out_ids); ray_sys_free(out_ds); + ray_free_raw(qbuf); ray_free_raw(out_ids); ray_free_raw(out_ds); if (rv && !RAY_IS_ERR(rv)) ray_release(rv); if (dv && !RAY_IS_ERR(dv)) ray_release(dv); return ray_error("oom", NULL); @@ -517,7 +517,7 @@ ray_t* ray_ann_fn(ray_t** args, int64_t n) { for (int64_t i = 0; i < found; i++) { rd[i] = out_ids[i]; dd[i] = out_ds[i]; } rv->len = found; dv->len = found; - ray_sys_free(qbuf); ray_sys_free(out_ids); ray_sys_free(out_ds); + ray_free_raw(qbuf); ray_free_raw(out_ids); ray_free_raw(out_ds); ray_t* tbl = ray_table_new(2); if (!tbl || RAY_IS_ERR(tbl)) { ray_release(rv); ray_release(dv); return ray_error("oom", NULL); } diff --git a/src/ops/expr.c b/src/ops/expr.c index 436697d4b..81a76cbcb 100644 --- a/src/ops/expr.c +++ b/src/ops/expr.c @@ -653,6 +653,48 @@ bool expr_compile(ray_graph_t* g, ray_t* tbl, ray_op_t* root, ray_expr_t* out) { uint8_t node_reg[nc]; memset(node_reg, 0xFF, nc * sizeof(uint8_t)); + /* Issue #533: which nodes may skip the SYM null proof. A null SYM is + * id 0, below every real intern id, and the fallback ranks a null SYM + * below everything in all six comparisons (null == null, null != x, + * null < x hold; null == x, x < null do not). Raw id compares in the + * fused lane produce the same table, so a SYM column whose every + * consumer in this subtree is EQ..GE against a non-null constant (a + * null literal bails at OP_CONST) or another SYM scan needs no O(n) + * ray_vec_has_nulls walk. Any other consumer — ISNULL, CAST, + * arithmetic — reads the lane as a value and keeps the proof. */ + uint8_t sym_cmp_safe[nc]; + memset(sym_cmp_safe, 1, nc * sizeof(uint8_t)); + { + uint8_t seen[nc]; + uint32_t stk[nc]; + uint32_t ssp = 0; + memset(seen, 0, nc * sizeof(uint8_t)); + if (root->id < nc) { seen[root->id] = 1; stk[ssp++] = root->id; } + while (ssp > 0) { + ray_op_t* n = &g->nodes[stk[--ssp]]; + bool cmp = n->arity == 2 && n->opcode >= OP_EQ && n->opcode <= OP_GE; + for (int i = 0; i < n->arity; i++) { + ray_op_t* ch = op_child(g, n, i); + if (!ch || ch->id >= nc) continue; + bool safe = false; + if (cmp) { + ray_op_t* other = op_child(g, n, 1 - i); + if (other && other->opcode == OP_CONST) safe = true; + else if (other && other->opcode == OP_SCAN) { + ray_op_ext_t* oe = find_ext(g, other->id); + ray_t* oc = oe ? ray_table_get_col(tbl, oe->sym) : NULL; + if (oc && (RAY_IS_PARTED(oc->type) + ? RAY_PARTED_BASETYPE(oc->type) == RAY_SYM + : oc->type == RAY_SYM)) + safe = true; + } + } + if (!safe) sym_cmp_safe[ch->id] = 0; + if (!seen[ch->id]) { seen[ch->id] = 1; stk[ssp++] = ch->id; } + } + } + } + /* Post-order DFS with explicit stack */ /* Depth limit 64 — expressions deeper than 64 levels fall back to non-fused path. */ typedef struct { ray_op_t* node; uint8_t phase; } dfs_t; @@ -730,16 +772,23 @@ bool expr_compile(ray_graph_t* g, ray_t* tbl, ray_op_t* root, ray_expr_t* out) { } /* Determine whether any lane in this column may be null. * For parted columns the wrapper attrs may not reflect - * individual segments — scan all segments. */ - bool col_nulls = ray_vec_has_nulls(col); - if (RAY_IS_PARTED(col->type)) { + * individual segments — scan all segments. SYM columns + * consumed only by null-safe comparisons skip the proof + * (see sym_cmp_safe above): their raw-id lanes already + * give the null-aware answers, so they compile as + * non-nullable whether or not id 0 is present. */ + bool need_proof = !(elem == RAY_SYM && node->id < nc && + sym_cmp_safe[node->id]); + bool col_nulls = need_proof && ray_vec_has_nulls(col); + if (need_proof && RAY_IS_PARTED(col->type)) { ray_t** segs = (ray_t**)ray_data(col); for (int64_t s = 0; s < col->len; s++) if (segs[s] && ray_vec_has_nulls(segs[s])) col_nulls = true; } - /* Nullable SYM is out of scope: sym ids are indistinguishable - * from the null sentinel (id 0) in raw integer lanes. */ + /* Nullable SYM read as a value is out of scope: sym ids are + * indistinguishable from the null sentinel (id 0) in raw + * integer lanes. */ if (col_nulls && (col->type == RAY_SYM || (RAY_IS_PARTED(col->type) && RAY_PARTED_BASETYPE(col->type) == RAY_SYM))) @@ -1682,9 +1731,11 @@ static inline uint16_t zone_swap_op(uint16_t op) { } /* Decide one comparison (col cmp_op cval) over chunk `ch` from its int64 - * extrema. cmp_op is normalized so the column is the left operand. The - * all-pass arm is gated on "no nulls in the chunk" (a NULL lane yields BOOL 0, - * never 1); the all-fail arm needs no guard (NULL op const is never TRUE). */ + * extrema. cmp_op is normalized so the column is the left operand. Extrema + * exclude nulls, and the null-aware kernels rank a null below every value + * (null != c, null < c, null <= c are TRUE; null == c, null > c, null >= c + * are FALSE), so a chunk that holds a null can never be decided all-pass for + * EQ/GT/GE nor all-fail for NE/LT/LE from its extrema alone. */ static int zone_cmp_decision(const ray_index_t* ix, int64_t ch, uint16_t cmp_op, int64_t cval) { const int64_t* mins = (const int64_t*)ray_data(ix->u.chunk_zone.mins); @@ -1700,14 +1751,14 @@ static int zone_cmp_decision(const ray_index_t* ix, int64_t ch, break; case OP_NE: if (!has_nulls && (cval < cmin || cval > cmax)) return 1; - if (cmin == cmax && cval == cmin) return 0; + if (!has_nulls && cmin == cmax && cval == cmin) return 0; break; case OP_LT: - if (cmin >= cval) return 0; + if (!has_nulls && cmin >= cval) return 0; if (!has_nulls && cmax < cval) return 1; break; case OP_LE: - if (cmin > cval) return 0; + if (!has_nulls && cmin > cval) return 0; if (!has_nulls && cmax <= cval) return 1; break; case OP_GT: diff --git a/src/ops/filter.c b/src/ops/filter.c index 226aa53f0..cc298a2ff 100644 --- a/src/ops/filter.c +++ b/src/ops/filter.c @@ -501,7 +501,7 @@ ray_t* exec_filter(ray_graph_t* g, ray_op_t* op, ray_t* input, ray_t* pred) { memcpy(dst + i * esz, src + match_idx[i] * esz, esz); } } - } else if (pool) { + } else if (ray_pool_par_dispatch_ok(pool, pass_count, RAY_PARALLEL_THRESHOLD)) { /* Batched fused multi-column gather (≤MGATHER_MAX_COLS per dispatch) */ multi_gather_ctx_t mgctx = { .idx = match_idx, .ncols = 0 }; for (int64_t c = 0; c < ncols; c++) { @@ -1019,7 +1019,7 @@ ray_t* sel_compact(ray_graph_t* g, ray_t* tbl, ray_t* sel, memcpy(dst + i * esz, src + match_idx[i] * esz, esz); } } - } else if (pool) { + } else if (ray_pool_par_dispatch_ok(pool, pass_count, RAY_PARALLEL_THRESHOLD)) { /* Batched multi-column gather: fill mgctx with up to MGATHER_MAX_COLS * kept columns, dispatch, then continue with the next batch. This is * column-count-agnostic — no per-column fallback. */ diff --git a/src/ops/fused_pred.c b/src/ops/fused_pred.c index 11b4eb4e4..ac5d5ee75 100644 --- a/src/ops/fused_pred.c +++ b/src/ops/fused_pred.c @@ -75,6 +75,8 @@ static int fp_op_from_1char(const char* op, size_t len) { * here are the negative-typed (-RAY_*) atom encoding from values. */ static int fp_atom_col_compatible(int8_t atom_type, int8_t col_type) { switch (col_type) { + case RAY_STR: return atom_type == -RAY_STR; + case RAY_GUID: return atom_type == -RAY_GUID; case RAY_SYM: /* SYM compares against a symbol-id atom or a string literal * (string is intern-resolved to a sym id at compile time). */ @@ -89,58 +91,33 @@ static int fp_atom_col_compatible(int8_t atom_type, int8_t col_type) { case RAY_U8: case RAY_I16: case RAY_I32: + case RAY_F32: case RAY_F64: case RAY_I64: /* Any signed/unsigned integer literal; we still range-check * cval against the column width to fold out-of-range. */ return atom_type == -RAY_BOOL || atom_type == -RAY_U8 || atom_type == -RAY_I16 || atom_type == -RAY_I32 - || atom_type == -RAY_I64; + || atom_type == -RAY_I64 || atom_type == -RAY_F32 || atom_type == -RAY_F64; default: return 0; } } -/* Reject columns the fused per-row compare can't read safely. - * - * The fused evaluator reads raw payload bytes. For a NUMERIC or TEMPORAL - * column the null is an OUT-OF-BAND SENTINEL (NULL_I64, NULL_I32, NaN, ...) - * that does not stand for any real value, so comparing it would answer a - * question about the sentinel rather than about the null — a different result - * from the unfused null-aware kernel. Those stay rejected. - * - * SYM and STR are different in kind: their null is an IN-BAND ORDINARY VALUE. - * A SYM null is id 0, which is a genuine dictionary entry — the empty string, - * held at position 0 of every symfile by construction (domain.c enforces the - * reservation on open). A STR null is a zero-length descriptor. Comparing - * those raw payloads for EQUALITY therefore gives exactly the answer the - * unfused kernel gives: `== ""` is true precisely on the null cells, `!= ""` - * false precisely on them. Nothing is being skipped or guessed. - * - * Deliberately limited to EQ/NE: - * - ORDERING (LT/LE/GT/GE) is NOT safe by this argument. The sort kernels - * treat nulls as a separate order class placed by the `nulls_first` flag - * (sort_cmp; the single-key path partitions them out and rotates), NOT by - * their payload — so "" sorting as the smallest string is not the ordering - * a null-aware comparison implies. (SYM ordering ops are rejected outright - * a few lines below in any case.) - * - LIKE is not safe either: bfb5b380 made the string kernels - * null-propagating (e.g. `strlen` of a null SYM/STR is NULL_I64), so a - * pattern match against the empty payload need not equal the null-aware - * answer. LIKE keeps the strict gate. - * - * This mattered the moment converted stores started carrying HAS_NULLS on SYM - * columns (#416): ClickBench q24/q25/q26 are `where: (!= SearchPhrase "")`, - * and losing the fused path cost them ~70x for an identical result. */ +/* Numeric, temporal, STR and GUID comparisons have an explicit typed leg + * with null-as-minimum ordering. SYM equality uses domain codes. LIKE/IN keep + * their stricter null-free admission because they use different evaluators. */ static int fp_col_supported_op(const ray_t* col, int eq_or_ne) { if (!col) return 0; - if (eq_or_ne && (col->type == RAY_SYM || col->type == RAY_STR)) return 1; + if (col->type >= RAY_BOOL && col->type <= RAY_TIMESTAMP) return 1; + if (col->type == RAY_GUID || col->type == RAY_STR) return 1; + if (eq_or_ne && col->type == RAY_SYM) return 1; return !ray_vec_has_nulls(col); } /* Strict form — no nullable column at all. Used by the shapes whose * evaluator arm is not an equality compare (LIKE, IN). */ static int fp_col_supported(const ray_t* col) { - return fp_col_supported_op(col, 0); + return col && !ray_vec_has_nulls(col); } static int fp_expr_const_str(ray_t* expr) { @@ -203,11 +180,12 @@ static int fp_check_simple_cmp(ray_t* expr, ray_t* tbl) { int is_dict_str = (ct == RAY_STR && !is_ord && rhs->type == -RAY_STR && ray_index_kind(col) == RAY_IDX_DICT); - /* F32/F64/non-dict-STR not supported by phase-3 evaluator. */ + /* Only types with a matching typed comparison implementation. */ if (!is_dict_str && ct != RAY_SYM && ct != RAY_BOOL && ct != RAY_U8 && ct != RAY_I16 && ct != RAY_I32 && ct != RAY_I64 - && ct != RAY_DATE && ct != RAY_TIME && ct != RAY_TIMESTAMP) + && ct != RAY_DATE && ct != RAY_TIME && ct != RAY_TIMESTAMP + && ct != RAY_F32 && ct != RAY_F64 && ct != RAY_STR && ct != RAY_GUID) return -1; if (!fp_col_supported_op(col, code == 0 || code == 1)) return -1; if (!is_dict_str && !fp_atom_col_compatible(rhs->type, ct)) return -1; @@ -336,6 +314,41 @@ void fp_eval_cmp(const fp_cmp_t* p, int64_t start, int64_t end, int8_t ct = p->col_type; uint8_t esz = p->col_esz; + if (p->typed_compare) { + for (int64_t r = start; r < end; r++) { + bool an = ray_vec_is_null(p->col_obj, r), bn = p->constant_null; + int cmp; + if (an || bn) cmp = an == bn ? 0 : an ? -1 : 1; + else if (ct == RAY_STR) { + size_t len = 0, clen = ray_str_len(p->literal); + const char* str = ray_str_vec_get(p->col_obj, r, &len); + size_t common = len < clen ? len : clen; + cmp = common ? memcmp(str, ray_str_ptr(p->literal), common) : 0; + if (!cmp) cmp = (len > clen) - (len < clen); + } else if (ct == RAY_GUID) { + cmp = memcmp((const char*)p->col_base + (size_t)r * 16, ray_data(p->literal->obj), 16); + } else if (p->float_compare) { + double v = ct == RAY_F32 ? ((const float*)p->col_base)[r] + : ct == RAY_F64 ? ((const double*)p->col_base)[r] + : (double)read_col_i64(p->col_base, r, ct, p->col_attrs); + cmp = (v > p->fval) - (v < p->fval); + } else { + int64_t v = read_col_i64(p->col_base, r, ct, p->col_attrs); + cmp = (v > cval) - (v < cval); + } + switch (op) { + case FP_EQ: bits[r - start] = cmp == 0; break; + case FP_NE: bits[r - start] = cmp != 0; break; + case FP_LT: bits[r - start] = cmp < 0; break; + case FP_LE: bits[r - start] = cmp <= 0; break; + case FP_GT: bits[r - start] = cmp > 0; break; + case FP_GE: bits[r - start] = cmp >= 0; break; + default: bits[r - start] = 0; break; + } + } + return; + } + /* Compile-time fold: out-of-range constant ⇒ all-true or all-false. */ if (p->fold) { memset(bits, (p->fold == FP_FOLD_TRUE) ? 1 : 0, (size_t)n); @@ -347,9 +360,10 @@ void fp_eval_cmp(const fp_cmp_t* p, int64_t start, int64_t end, * whole morsel from chunk extrema without reading a single value. * Only integer/temporal comparisons (EQ/NE/LT/LE/GT/GE) — LIKE/IN * have their own evaluators below and SYM ordering is rejected at - * compile time anyway. The all-pass shortcut is gated on "no - * nulls in this chunk" because SQL `(x op c)` is FALSE/NULL when x - * is NULL; the all-fail shortcut needs no such guard. */ + * compile time anyway. Extrema exclude nulls and the null-aware + * kernels rank a null below every value (null != c, null < c, + * null <= c are TRUE), so a chunk holding a null is never decided + * all-pass for EQ/GT/GE nor all-fail for NE/LT/LE from extrema. */ if (p->col_obj && (p->col_obj->attrs & RAY_ATTR_HAS_INDEX) && p->col_obj->index) { @@ -379,14 +393,14 @@ void fp_eval_cmp(const fp_cmp_t* p, int64_t start, int64_t end, break; case FP_NE: if (!has_nulls && (cval < cmin || cval > cmax)) decision = 1; - else if (cmin == cmax && cval == cmin) decision = 0; + else if (!has_nulls && cmin == cmax && cval == cmin) decision = 0; break; case FP_LT: - if (cmin >= cval) decision = 0; + if (!has_nulls && cmin >= cval) decision = 0; else if (!has_nulls && cmax < cval) decision = 1; break; case FP_LE: - if (cmin > cval) decision = 0; + if (!has_nulls && cmin > cval) decision = 0; else if (!has_nulls && cmax <= cval) decision = 1; break; case FP_GT: @@ -847,8 +861,7 @@ static int fp_compile_cmp(ray_graph_t* g, ray_op_t* pred_op, ray_t* tbl, if (col->type == RAY_SYM && (out->op == FP_LT || out->op == FP_LE || out->op == FP_GT || out->op == FP_GE)) return -1; - /* Nullable columns: only SYM/STR equality is safe — see - * fp_col_supported_op for why sentinels and in-band nulls differ. */ + /* Admission and execution share the typed comparison/null contract. */ if (!fp_col_supported_op(col, out->op == FP_EQ || out->op == FP_NE)) return -1; @@ -865,6 +878,13 @@ static int fp_compile_cmp(ray_graph_t* g, ray_op_t* pred_op, ray_t* tbl, out->col_obj = col; out->col_len = col->len; + out->literal = cv; + out->constant_null = RAY_ATOM_IS_NULL(cv); + out->float_compare = col->type == RAY_F32 || col->type == RAY_F64 || cv->type == -RAY_F32 || cv->type == -RAY_F64; + out->typed_compare = col->type == RAY_STR || col->type == RAY_GUID || + (col->type != RAY_SYM && (out->float_compare || out->constant_null || ray_vec_may_have_nulls(col))); + if (col->type == RAY_STR || col->type == RAY_GUID) return 0; + if (out->col_type == RAY_SYM) { /* The constant must be expressed in the COLUMN's domain — cells * below compare as raw indices against cval (sym-domain Phase 2). @@ -896,6 +916,7 @@ static int fp_compile_cmp(ray_graph_t* g, ray_op_t* pred_op, ray_t* tbl, * fp_atom_col_compatible above, so each branch knows the * stored unit matches the column's. */ switch (cv->type) { + case -RAY_F32: case -RAY_F64: out->fval = cv->f64; break; case -RAY_I64: case -RAY_TIMESTAMP: out->cval = cv->i64; break; case -RAY_I32: case -RAY_DATE: case -RAY_TIME: out->cval = (int64_t)cv->i32; break; @@ -906,6 +927,11 @@ static int fp_compile_cmp(ray_graph_t* g, ray_op_t* pred_op, ray_t* tbl, out->cval_in_dict = 1; } + if (out->typed_compare) { + if (cv->type != -RAY_F32 && cv->type != -RAY_F64) out->fval = (double)out->cval; + return 0; + } + /* Range-check cval against the column's representable range and * pre-fold the comparison when the constant lies outside it. * Without this, the inner-loop cast `(T)cval` silently truncates and diff --git a/src/ops/fused_pred.h b/src/ops/fused_pred.h index 8658d7bcf..89c7c0be2 100644 --- a/src/ops/fused_pred.h +++ b/src/ops/fused_pred.h @@ -62,6 +62,11 @@ typedef struct { ray_t* col_obj; int64_t col_len; int64_t cval; + ray_t* literal; /* borrowed from the predicate graph */ + uint8_t typed_compare; + double fval; + bool constant_null; + bool float_compare; int cval_in_dict; int64_t cvals[16]; uint8_t n_cvals; diff --git a/src/ops/fused_topk.c b/src/ops/fused_topk.c index 7ec4b17b3..3985b754e 100644 --- a/src/ops/fused_topk.c +++ b/src/ops/fused_topk.c @@ -189,6 +189,13 @@ static inline int fpk_cmp(const fpk_par_ctx_t* c, int64_t row_a, int64_t row_b) else if (la > lb) cmp = 1; } if (cmp == 0) continue; + } else if (ks->type == RAY_GUID) { + cmp = memcmp((const char*)ks->base + (size_t)row_a * 16, + (const char*)ks->base + (size_t)row_b * 16, 16); + } else if (ks->type == RAY_F32 || ks->type == RAY_F64) { + double va = ks->type == RAY_F32 ? ((const float*)ks->base)[row_a] : ((const double*)ks->base)[row_a]; + double vb = ks->type == RAY_F32 ? ((const float*)ks->base)[row_b] : ((const double*)ks->base)[row_b]; + cmp = (va > vb) - (va < vb); } else if (ks->esz == 8) { int64_t va = ((const int64_t*)ks->base)[row_a]; int64_t vb = ((const int64_t*)ks->base)[row_b]; @@ -330,7 +337,8 @@ ray_t* ray_fused_topk_select(ray_t* tbl, if (RAY_IS_PARTED(kt) || kt == RAY_MAPCOMMON) return NULL; if (kt != RAY_SYM && kt != RAY_STR && kt != RAY_BOOL && kt != RAY_U8 && kt != RAY_I16 && kt != RAY_I32 && kt != RAY_I64 - && kt != RAY_DATE && kt != RAY_TIME && kt != RAY_TIMESTAMP) + && kt != RAY_DATE && kt != RAY_TIME && kt != RAY_TIMESTAMP + && kt != RAY_F32 && kt != RAY_F64 && kt != RAY_GUID) return NULL; ctx.keys[i].type = kt; ctx.keys[i].attrs = col->attrs; diff --git a/src/ops/graph.c b/src/ops/graph.c index b65536346..5608332dd 100644 --- a/src/ops/graph.c +++ b/src/ops/graph.c @@ -25,7 +25,6 @@ #include "internal.h" /* EXT_TRAIL, graph_alloc_ext_node_ex */ #include "store/csr.h" #include "store/hnsw.h" -#include "mem/sys.h" #include /* -------------------------------------------------------------------------- @@ -46,7 +45,7 @@ ray_op_t* graph_alloc_node(ray_graph_t* g) { doubling would wrap around to a smaller value. */ if (g->node_cap > UINT32_MAX / 2) return NULL; uint32_t new_cap = g->node_cap * 2; - ray_op_t* new_nodes = (ray_op_t*)ray_sys_realloc(g->nodes, + ray_op_t* new_nodes = (ray_op_t*)ray_realloc_raw(g->nodes, new_cap * sizeof(ray_op_t)); if (!new_nodes) return NULL; g->nodes = new_nodes; @@ -62,19 +61,18 @@ ray_op_t* graph_alloc_node(ray_graph_t* g) { ray_op_ext_t* graph_alloc_ext_node_ex(ray_graph_t* g, size_t extra) { /* Extended nodes are 64 bytes; extra bytes appended for inline arrays */ - ray_op_ext_t* ext = (ray_op_ext_t*)ray_sys_alloc(sizeof(ray_op_ext_t) + extra); + ray_op_ext_t* ext = (ray_op_ext_t*)ray_calloc_raw(sizeof(ray_op_ext_t) + extra); if (!ext) return NULL; - memset(ext, 0, sizeof(ray_op_ext_t) + extra); ext->base.in_id[0] = ext->base.in_id[1] = RAY_OP_NONE; ext->third_in = RAY_OP_NONE; /* Also add a placeholder in the nodes array for ID tracking */ if (g->node_count >= g->node_cap) { - if (g->node_cap > UINT32_MAX / 2) { ray_sys_free(ext); return NULL; } + if (g->node_cap > UINT32_MAX / 2) { ray_free_raw(ext); return NULL; } uint32_t new_cap = g->node_cap * 2; - ray_op_t* new_nodes = (ray_op_t*)ray_sys_realloc(g->nodes, + ray_op_t* new_nodes = (ray_op_t*)ray_realloc_raw(g->nodes, new_cap * sizeof(ray_op_t)); - if (!new_nodes) { ray_sys_free(ext); return NULL; } + if (!new_nodes) { ray_free_raw(ext); return NULL; } g->nodes = new_nodes; g->node_cap = new_cap; } @@ -88,11 +86,11 @@ ray_op_ext_t* graph_alloc_ext_node_ex(ray_graph_t* g, size_t extra) { /* Track ext node for cleanup */ if (g->ext_count >= g->ext_cap) { - if (g->ext_cap > UINT32_MAX / 2) { g->node_count--; ray_sys_free(ext); return NULL; } + if (g->ext_cap > UINT32_MAX / 2) { g->node_count--; ray_free_raw(ext); return NULL; } uint32_t new_cap = g->ext_cap == 0 ? 16 : g->ext_cap * 2; - ray_op_ext_t** new_exts = (ray_op_ext_t**)ray_sys_realloc(g->ext_nodes, + ray_op_ext_t** new_exts = (ray_op_ext_t**)ray_realloc_raw(g->ext_nodes, new_cap * sizeof(ray_op_ext_t*)); - if (!new_exts) { g->node_count--; ray_sys_free(ext); return NULL; } + if (!new_exts) { g->node_count--; ray_free_raw(ext); return NULL; } g->ext_nodes = new_exts; g->ext_cap = new_cap; } @@ -113,11 +111,11 @@ static ray_op_ext_t* graph_alloc_ext_node(ray_graph_t* g) { * -------------------------------------------------------------------------- */ ray_graph_t* ray_graph_new(ray_t* tbl) { - ray_graph_t* g = (ray_graph_t*)ray_sys_alloc(sizeof(ray_graph_t)); + ray_graph_t* g = (ray_graph_t*)ray_calloc_raw(sizeof(ray_graph_t)); if (!g) return NULL; - g->nodes = (ray_op_t*)ray_sys_alloc(GRAPH_INIT_CAP * sizeof(ray_op_t)); - if (!g->nodes) { ray_sys_free(g); return NULL; } + g->nodes = (ray_op_t*)ray_alloc_raw(GRAPH_INIT_CAP * sizeof(ray_op_t)); + if (!g->nodes) { ray_free_raw(g); return NULL; } g->node_cap = GRAPH_INIT_CAP; g->node_count = 0; g->table = tbl; @@ -176,16 +174,16 @@ void ray_graph_free(ray_graph_t* g) { if (g->ext_nodes[j] && g->ext_nodes[j]->seg_mask == mask) g->ext_nodes[j]->seg_mask = NULL; } - ray_sys_free(mask); + ray_free_raw(mask); } } /* Free extended nodes */ for (uint32_t i = 0; i < g->ext_count; i++) { - ray_sys_free(g->ext_nodes[i]); + ray_free_raw(g->ext_nodes[i]); } - ray_sys_free(g->ext_nodes); + ray_free_raw(g->ext_nodes); - ray_sys_free(g->nodes); + ray_free_raw(g->nodes); if (g->table) ray_release(g->table); /* Release table registry */ @@ -193,11 +191,11 @@ void ray_graph_free(ray_graph_t* g) { for (uint16_t i = 0; i < g->n_tables; i++) { if (g->tables[i]) ray_release(g->tables[i]); } - ray_sys_free(g->tables); + ray_free_raw(g->tables); } if (g->selection) ray_release(g->selection); - ray_sys_free(g); + ray_free_raw(g); } /* -------------------------------------------------------------------------- @@ -1272,7 +1270,7 @@ uint16_t ray_graph_add_table(ray_graph_t* g, ray_t* table) { uint16_t id = g->n_tables; uint16_t new_cap = id + 1; - ray_t** new_tables = (ray_t**)ray_sys_realloc(g->tables, + ray_t** new_tables = (ray_t**)ray_realloc_raw(g->tables, (size_t)new_cap * sizeof(ray_t*)); if (!new_tables) return UINT16_MAX; /* error sentinel */ g->tables = new_tables; diff --git a/src/ops/group.c b/src/ops/group.c index 4a1671e00..f6ae68a3b 100644 --- a/src/ops/group.c +++ b/src/ops/group.c @@ -32,6 +32,7 @@ #include "vec/str.h" /* ray_str_t SSO hash/eq for wide STR group keys */ #include "ops/idxop.h" /* RAY_IDX_DICT: group on persisted string codes */ #include "core/runtime.h" /* __VM — per-thread group-key cardinality hint */ +#include "core/profile.h" #include @@ -231,6 +232,7 @@ static int64_t wide_winner_row(ray_t* input, uint16_t op, /* first/last: positional — return the first/last non-null row, no compare. */ if (op == OP_FIRST) { for (int64_t i = 0; i < scan_n; i++) { + if ((i & 65535) == 0 && ray_interrupted()) return -1; int64_t row = sel ? sel[i] : i; if (input->type == RAY_LIST) { ray_t* e = ((ray_t**)ray_data(input))[row]; @@ -243,6 +245,7 @@ static int64_t wide_winner_row(ray_t* input, uint16_t op, } if (op == OP_LAST) { for (int64_t i = scan_n - 1; i >= 0; i--) { + if ((i & 65535) == 0 && ray_interrupted()) return -1; int64_t row = sel ? sel[i] : i; if (input->type == RAY_LIST) { ray_t* e = ((ray_t**)ray_data(input))[row]; @@ -259,6 +262,7 @@ static int64_t wide_winner_row(ray_t* input, uint16_t op, if (input->type == RAY_GUID) { const uint8_t* d = (const uint8_t*)ray_data(input); for (int64_t i = 0; i < scan_n; i++) { + if ((i & 65535) == 0 && ray_interrupted()) return -1; int64_t row = sel ? sel[i] : i; if (has_nulls && ray_vec_is_null(input, row)) continue; if (best < 0) { best = row; continue; } @@ -267,6 +271,7 @@ static int64_t wide_winner_row(ray_t* input, uint16_t op, } } else { /* RAY_STR — lexicographic over the pooled bytes */ for (int64_t i = 0; i < scan_n; i++) { + if ((i & 65535) == 0 && ray_interrupted()) return -1; int64_t row = sel ? sel[i] : i; if (has_nulls && ray_vec_is_null(input, row)) continue; if (best < 0) { best = row; continue; } @@ -1027,10 +1032,28 @@ static int64_t cd_sym_dense_count(ray_t* input) { * global set. Distinct values land in the same * partition, so the global count is the sum of * per-partition counts. */ +/* Lossless F32 widening shares the exact F64 dedupe implementation. The + * fused narrow-pair route reads F32 directly and avoids this buffer. */ +static ray_t* cd_widen_f32(ray_t* src) { + ray_t* out = ray_vec_new(RAY_F64, src->len); + if (!out || RAY_IS_ERR(out)) return out; + out->len = src->len; + const float* in = ray_data(src); double* dst = ray_data(out); + for (int64_t i = 0; i < src->len; i++) dst[i] = in[i]; + if (ray_vec_may_have_nulls(src)) out->attrs |= RAY_ATTR_HAS_NULLS; + return out; +} + ray_t* exec_count_distinct(ray_graph_t* g, ray_op_t* op, ray_t* input) { (void)g; (void)op; if (!input || RAY_IS_ERR(input)) return input; + if (input->type == RAY_F32) { + ray_t* wide = cd_widen_f32(input); + if (!wide || RAY_IS_ERR(wide)) return wide ? wide : ray_error("oom", NULL); + ray_t* result = exec_count_distinct(g, op, wide); ray_release(wide); return result; + } + int8_t in_type = input->type; int64_t len = input->len; @@ -1896,6 +1919,12 @@ ray_t* ray_count_distinct_per_group(ray_t* src, const int64_t* row_gid, int64_t n_rows, int64_t n_groups) { if (!src || RAY_IS_ERR(src)) return ray_error("domain", "count distinct per group: invalid source column"); if (n_groups < 0) return ray_error("domain", "count distinct per group: group count must be non-negative, got %lld", (long long)n_groups); + if (src->type == RAY_F32) { + ray_t* wide = cd_widen_f32(src); + if (!wide || RAY_IS_ERR(wide)) return wide ? wide : ray_error("oom", NULL); + ray_t* result = ray_count_distinct_per_group(wide, row_gid, n_rows, n_groups); + ray_release(wide); return result; + } int8_t in_type = src->type; switch (in_type) { case RAY_BOOL: case RAY_U8: @@ -2076,7 +2105,7 @@ ray_t* ray_count_distinct_per_group(ray_t* src, const int64_t* row_gid, * cache-missing on the source column, but those misses overlap with * parallel tasks on other cores — the 27-core dispatch hides them. * - * Type support: F64 native; I64/I32/I16/U8 cast-to-double on read. + * Type support: floating, integer, boolean and temporal values widened to F64. * Null rows are skipped pairwise. * * Returns: F64 vec of length n_groups, or NULL on unsupported type @@ -2097,6 +2126,8 @@ typedef struct { double* scratch_pool; /* flat shared scratch, sized at sum(grp_cnt) */ double* out_data; /* ray_data(out) */ ray_t* out; /* for set_null */ + _Atomic(bool) any_null; + const uint8_t* handled; } med_par_ctx_t; typedef struct { @@ -2117,10 +2148,11 @@ typedef struct { typedef struct { ray_t* src; - ray_t* out; + int64_t* winners; const int64_t* idx_buf; const int64_t* offsets; const int64_t* grp_cnt; + int64_t threshold; _Atomic(int) oom; _Atomic(int) cancel; } mode_par_ctx_t; @@ -2128,42 +2160,153 @@ typedef struct { static inline double med_read_as_f64(const void* base, int8_t t, int64_t row) { switch (t) { case RAY_F64: { double v; memcpy(&v, (const char*)base + (size_t)row * 8, 8); return v; } + case RAY_F32: { float v; memcpy(&v, (const char*)base + (size_t)row * 4, 4); return (double)v; } case RAY_I64: { int64_t v; memcpy(&v, (const char*)base + (size_t)row * 8, 8); return (double)v; } case RAY_I32: { int32_t v; memcpy(&v, (const char*)base + (size_t)row * 4, 4); return (double)v; } case RAY_DATE: case RAY_TIME: { int32_t v; memcpy(&v, (const char*)base + (size_t)row * 4, 4); return (double)v; } case RAY_TIMESTAMP: { int64_t v; memcpy(&v, (const char*)base + (size_t)row * 8, 8); return (double)v; } case RAY_I16: { int16_t v; memcpy(&v, (const char*)base + (size_t)row * 2, 2); return (double)v; } - case RAY_U8: return (double)((const uint8_t*)base)[row]; + case RAY_BOOL: case RAY_U8: return (double)((const uint8_t*)base)[row]; default: return 0.0; } } -/* Type-correct sentinel null check for the med_par paths. U8 is - * non-nullable; med only accepts the listed types so SYM/STR/GUID/F32 - * never reach here. */ +/* Type-correct sentinel null check for the med_par paths. BOOL/U8 are + * non-nullable; text and GUID inputs are rejected by rank_per_group_buf. */ static inline bool med_is_null(const void* base, int8_t t, int64_t row) { switch (t) { case RAY_F64: { double v; memcpy(&v, (const char*)base + (size_t)row * 8, 8); return v != v; } + case RAY_F32: { float v; memcpy(&v, (const char*)base + (size_t)row * 4, 4); return v != v; } case RAY_I64: return ((const int64_t*)base)[row] == NULL_I64; case RAY_I32: return ((const int32_t*)base)[row] == NULL_I32; case RAY_DATE: case RAY_TIME: return ((const int32_t*)base)[row] == NULL_I32; case RAY_TIMESTAMP: return ((const int64_t*)base)[row] == NULL_I64; case RAY_I16: return ((const int16_t*)base)[row] == NULL_I16; - case RAY_U8: return false; /* non-nullable */ + case RAY_BOOL: case RAY_U8: return false; /* non-nullable */ default: return false; } } +/* Balance contiguous groups by input work, keeping large groups in separate + * tasks. The bounded wrapper also avoids dispatch_n's ring-growth failure path. */ +typedef struct { + ray_pool_fn fn; + void* context; + int64_t begin[RAY_POOL_INIT_TASKS]; + int64_t end[RAY_POOL_INIT_TASKS]; +} group_work_t; +static void group_work_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + group_work_t* c = raw; + for (int64_t t = start; t < end; t++) c->fn(c->context, wid, c->begin[t], c->end[t]); +} +void ray_group_dispatch(ray_pool_fn fn, void* context, const int64_t* counts, int64_t groups) { + ray_pool_t* pool = ray_pool_get(); + int64_t work = 0; + for (int64_t g = 0; g < groups; g++) work += counts[g] + 1; + if (groups < 2 || !ray_pool_par_dispatch_ok(pool, work, 4096)) { + fn(context, 0, 0, groups); return; + } + uint32_t budget = ray_pool_total_workers(pool) * 8; + if (budget > RAY_POOL_INIT_TASKS / 2) budget = RAY_POOL_INIT_TASKS / 2; + int64_t grain = (work + budget - 1) / budget; + group_work_t tasks = {.fn = fn, .context = context}; + uint32_t n = 0; + int64_t begin = 0, cost = 0; + for (int64_t g = 0; g < groups; g++) { + int64_t next = counts[g] + 1; + if (g > begin && cost + next > grain) { + assert(n < RAY_POOL_INIT_TASKS); + tasks.begin[n] = begin; tasks.end[n++] = g; begin = g; cost = 0; + } + cost += next; + if (cost >= grain) { + assert(n < RAY_POOL_INIT_TASKS); + tasks.begin[n] = begin; tasks.end[n++] = g + 1; begin = g + 1; cost = 0; + } + } + if (begin < groups) { + assert(n < RAY_POOL_INIT_TASKS); + tasks.begin[n] = begin; tasks.end[n++] = groups; + } + ray_pool_dispatch_n(pool, group_work_run, &tasks, n); +} + +typedef struct { + ray_group_winner_fn fn; + void* context; + const int64_t* rows; + const int64_t* offsets; + const int64_t* counts; + int64_t* winners; + int64_t threshold; + struct { int64_t group, begin, count, winner; } partial[RAY_POOL_INIT_TASKS]; +} group_winners_t; +static void group_winners_small(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; group_winners_t* c = raw; + for (int64_t g = start; g < end; g++) { + if (c->counts[g] >= c->threshold) continue; + c->winners[g] = c->fn(c->context, c->rows + c->offsets[g], c->counts[g]); + } +} +static void group_winners_partial(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; group_winners_t* c = raw; + for (int64_t t = start; t < end; t++) + c->partial[t].winner = c->fn(c->context, c->rows + c->partial[t].begin, c->partial[t].count); +} +void ray_group_winners(ray_group_winner_fn fn, void* context, const int64_t* rows, + const int64_t* offsets, const int64_t* counts, int64_t groups, int64_t* winners) { + ray_pool_t* pool = ray_pool_get(); + int64_t total = 0; + for (int64_t g = 0; g < groups; g++) total += counts[g]; + group_winners_t c = {.fn = fn, .context = context, .rows = rows, + .offsets = offsets, .counts = counts, .winners = winners, .threshold = INT64_MAX}; + uint32_t tasks = 0; + if (ray_pool_par_dispatch_ok(pool, total, 262144)) { + uint32_t budget = ray_pool_total_workers(pool) * 4; + if (budget > RAY_POOL_INIT_TASKS / 2) budget = RAY_POOL_INIT_TASKS / 2; + int64_t grain = (total + budget - 1) / budget; + c.threshold = total / ray_pool_total_workers(pool); + if (c.threshold < 262144) c.threshold = 262144; + if (c.threshold < grain) c.threshold = grain; + for (int64_t g = 0; g < groups; g++) if (counts[g] >= c.threshold) { + winners[g] = -1; + for (int64_t off = 0; off < counts[g]; off += grain) { + assert(tasks < RAY_POOL_INIT_TASKS); + c.partial[tasks].group = g; + c.partial[tasks].begin = offsets[g] + off; + c.partial[tasks].count = counts[g] - off < grain ? counts[g] - off : grain; + c.partial[tasks++].winner = -1; + } + } + } + ray_group_dispatch(group_winners_small, &c, counts, groups); + if (tasks && !ray_interrupted() && !atomic_load(&pool->cancelled)) { + ray_pool_dispatch_n(pool, group_winners_partial, &c, tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) return; + for (uint32_t t = 0; t < tasks; t++) { + int64_t g = c.partial[t].group, next = c.partial[t].winner; + if (next < 0) continue; + if (winners[g] < 0) winners[g] = next; + else { + int64_t pair[] = {winners[g], next}; + winners[g] = fn(context, pair, 2); + } + } + } +} + static void med_per_group_fn(void* ctx_v, uint32_t worker_id, int64_t start, int64_t end) { (void)worker_id; med_par_ctx_t* c = (med_par_ctx_t*)ctx_v; + bool any_null = false; for (int64_t g = start; g < end; g++) { + if (c->handled && c->handled[g]) continue; int64_t cnt = c->grp_cnt[g]; int64_t off = c->offsets[g]; - double* slice = c->scratch_pool + off; + double* slice = c->scratch_pool ? c->scratch_pool + off : NULL; int64_t actual = 0; if (c->has_nulls) { for (int64_t i = 0; i < cnt; i++) { @@ -2179,13 +2322,157 @@ static void med_per_group_fn(void* ctx_v, uint32_t worker_id, } if (actual == 0) { c->out_data[g] = NULL_F64; - ray_vec_set_null(c->out, g, true); + any_null = true; } else { c->out_data[g] = c->use_quantile ? ray_quantile_dbl_inplace(slice, actual, c->q) : ray_median_dbl_inplace(slice, actual); } } + if (any_null) atomic_store_explicit(&c->any_null, true, memory_order_relaxed); +} + +/* Exact selection within a dominant group. Count the pivot partitions in + * parallel and retain only the partition containing the requested adjacent + * ranks. The sample chooses work, never the answer. At most one extra double + * buffer is live; a depth bound falls back to the existing exact nth kernel. */ +typedef struct { + int64_t less, equal, count, offset; + double max_less, min_greater; +} rank_task_t; +typedef struct { + const med_par_ctx_t* source; + const int64_t* rows; + double* input; + double* output; + int64_t n; + uint32_t tasks; + double pivot; + bool keep_less; + rank_task_t task[RAY_POOL_INIT_TASKS]; +} rank_split_t; + +static void rank_gather_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; rank_split_t* c = raw; + const med_par_ctx_t* s = c->source; + for (int64_t t = start; t < end; t++) { + int64_t begin = c->n / c->tasks * t; + int64_t limit = t + 1 == c->tasks ? c->n : c->n / c->tasks * (t + 1); + int64_t count = 0; + for (int64_t i = begin; i < limit; i++) { + int64_t row = c->rows[i]; + if (s->has_nulls && med_is_null(s->base, s->src_type, row)) continue; + c->input[begin + count++] = med_read_as_f64(s->base, s->src_type, row); + } + c->task[t].count = count; + } +} +static void rank_compact_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; rank_split_t* c = raw; + for (int64_t t = start; t < end; t++) + memcpy(c->output + c->task[t].offset, c->input + c->n / c->tasks * t, + (size_t)c->task[t].count * sizeof(double)); +} +static void rank_count_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; rank_split_t* c = raw; + for (int64_t t = start; t < end; t++) { + int64_t begin = c->n / c->tasks * t; + int64_t limit = t + 1 == c->tasks ? c->n : c->n / c->tasks * (t + 1); + int64_t less = 0, equal = 0; + double max_less = -INFINITY, min_greater = INFINITY; + for (int64_t i = begin; i < limit; i++) { + double v = c->input[i]; + if (v < c->pivot) { less++; if (v > max_less) max_less = v; } + else if (v > c->pivot) { if (v < min_greater) min_greater = v; } + else equal++; + } + c->task[t].less = less; c->task[t].equal = equal; + c->task[t].max_less = max_less; c->task[t].min_greater = min_greater; + } +} +static void rank_scatter_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; rank_split_t* c = raw; + for (int64_t t = start; t < end; t++) { + int64_t begin = c->n / c->tasks * t; + int64_t limit = t + 1 == c->tasks ? c->n : c->n / c->tasks * (t + 1); + int64_t at = c->task[t].offset; + for (int64_t i = begin; i < limit; i++) { + double v = c->input[i]; + if (c->keep_less ? v < c->pivot : v > c->pivot) c->output[at++] = v; + } + } +} +static bool rank_large_group(med_par_ctx_t* source, int64_t group, + double* temporary, ray_pool_t* pool, double* answer) { + rank_split_t c = {.source = source, .rows = source->idx_buf + source->offsets[group], + .input = source->scratch_pool + source->offsets[group], .output = temporary, + .n = source->grp_cnt[group], .tasks = ray_pool_total_workers(pool) * 4}; + if (c.tasks > RAY_POOL_INIT_TASKS) c.tasks = RAY_POOL_INIT_TASKS; + ray_pool_dispatch_n(pool, rank_gather_run, &c, c.tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) return false; + int64_t valid = 0; + for (uint32_t t = 0; t < c.tasks; t++) { + c.task[t].offset = valid; valid += c.task[t].count; + } + if (!valid) { *answer = NULL_F64; atomic_store(&source->any_null, true); return true; } + if (valid != c.n) { + ray_pool_dispatch_n(pool, rank_compact_run, &c, c.tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) return false; + double* swap = c.input; c.input = c.output; c.output = swap; + } + c.n = valid; + double fraction = 0; + int64_t lo, hi; + if (source->use_quantile) { + double position = source->q * (double)(valid - 1); + lo = (int64_t)position; fraction = position - (double)lo; + hi = fraction > 0 ? lo + 1 : lo; + } else { lo = (valid - 1) / 2; hi = valid / 2; } + double lower = 0, upper = 0; + bool found = false; + for (unsigned depth = 0; depth < 32 && c.n >= RAY_PARALLEL_THRESHOLD; depth++) { + double sample[33]; + int64_t step = c.n / 33; + for (int i = 0; i < 33; i++) + sample[i] = c.input[i * step + ray_hash_i64(i + depth * 33) % step]; + c.pivot = ray_median_dbl_inplace(sample, 33); + ray_pool_dispatch_n(pool, rank_count_run, &c, c.tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) return false; + int64_t less = 0, equal = 0; + double max_less = -INFINITY, min_greater = INFINITY; + for (uint32_t t = 0; t < c.tasks; t++) { + less += c.task[t].less; equal += c.task[t].equal; + if (c.task[t].max_less > max_less) max_less = c.task[t].max_less; + if (c.task[t].min_greater < min_greater) min_greater = c.task[t].min_greater; + } + if (hi < less) c.keep_less = true; + else if (lo >= less + equal) c.keep_less = false; + else { + lower = lo < less ? max_less : c.pivot; + upper = hi >= less + equal ? min_greater : c.pivot; + found = true; break; + } + int64_t next = 0; + for (uint32_t t = 0; t < c.tasks; t++) { + c.task[t].offset = next; + int64_t begin = c.n / c.tasks * t; + int64_t limit = t + 1 == c.tasks ? c.n : c.n / c.tasks * (t + 1); + next += c.keep_less ? c.task[t].less : limit - begin - c.task[t].less - c.task[t].equal; + } + ray_pool_dispatch_n(pool, rank_scatter_run, &c, c.tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) return false; + if (!c.keep_less) { lo -= less + equal; hi -= less + equal; } + c.n = next; + double* swap = c.input; c.input = c.output; c.output = swap; + } + if (!found) { + lower = ray_nth_dbl_inplace(c.input, c.n, lo); + upper = hi == lo ? lower : ray_nth_dbl_inplace(c.input, c.n, hi); + } + *answer = source->use_quantile + ? (fraction > 0 ? lower + fraction * (upper - lower) : lower) + : (valid % 2 ? lower : (lower + upper) / 2.0); + return true; } static double decode_agg_f64_param(int64_t bits) { @@ -2203,8 +2490,8 @@ static ray_t* rank_per_group_buf(ray_t* src, bool use_quantile) { if (!src || RAY_IS_ERR(src) || n_groups < 0) return NULL; int8_t t = src->type; - if (t != RAY_F64 && t != RAY_I64 && t != RAY_I32 && - t != RAY_I16 && t != RAY_U8 && t != RAY_DATE && + if (t != RAY_F64 && t != RAY_F32 && t != RAY_I64 && t != RAY_I32 && + t != RAY_I16 && t != RAY_U8 && t != RAY_BOOL && t != RAY_DATE && t != RAY_TIME && t != RAY_TIMESTAMP) return NULL; if (use_quantile && (!__builtin_isfinite(q) || q < 0.0 || q > 1.0)) return ray_error("domain", "quantile: probability out of range"); @@ -2239,20 +2526,34 @@ static ray_t* rank_per_group_buf(ray_t* src, }; ray_pool_t* pool = ray_pool_get(); - bool par = pool && n_groups >= 8 && total >= 4096; - if (par) { - /* dispatch_n's task ring is capped at MAX_RING_CAP (65536); when - * n_groups exceeds that, fall back to elements-based dispatch - * (auto-grows grain so every group is covered). Under the cap, - * one task per group gives the best parallelism for small K - * per-group work like quickselect. */ - if (n_groups < (1 << 16)) - ray_pool_dispatch_n(pool, med_per_group_fn, &ctx, (uint32_t)n_groups); - else - ray_pool_dispatch(pool, med_per_group_fn, &ctx, n_groups); - } else { - med_per_group_fn(&ctx, 0, 0, n_groups); + /* Use the common parallel grain so a few medium groups can share the + * pool. The per-worker share still prevents splitting ordinary small groups. */ + int64_t threshold = RAY_PARALLEL_THRESHOLD, largest = 0; + if (ray_pool_par_dispatch_ok(pool, total, threshold)) { + int64_t share = total / ray_pool_total_workers(pool); + if (share > threshold) threshold = share; + for (int64_t g = 0; g < n_groups; g++) + if (grp_cnt[g] >= threshold && grp_cnt[g] > largest) largest = grp_cnt[g]; + } + double* temporary = largest ? ray_alloc_raw((size_t)largest * sizeof(double)) : NULL; + uint8_t* handled = temporary ? ray_calloc_raw((size_t)n_groups) : NULL; + if (handled) { + ctx.handled = handled; + for (int64_t g = 0; g < n_groups; g++) if (grp_cnt[g] >= threshold) { + if (!rank_large_group(&ctx, g, temporary, pool, &ctx.out_data[g])) break; + handled[g] = 1; + } + } + ray_free_raw(temporary); + ray_profile_tick("rank: selected dominant groups"); + ray_group_dispatch(med_per_group_fn, &ctx, grp_cnt, n_groups); + ray_profile_tick("rank: reduced remaining groups"); + ray_free_raw(handled); + if (ray_interrupted() || (pool && atomic_load(&pool->cancelled))) { + if (buf_hdr) scratch_free(buf_hdr); + ray_release(out); return ray_error("cancel", NULL); } + if (atomic_load_explicit(&ctx.any_null, memory_order_relaxed)) out->attrs |= RAY_ATTR_HAS_NULLS; if (buf_hdr) scratch_free(buf_hdr); return out; @@ -2329,46 +2630,6 @@ static inline uint64_t mode_scalar_key(ray_t* src, int64_t row) { } } -static inline void mode_copy_fixed_cell(ray_t* out, int64_t dst, - ray_t* src, int64_t row) { - uint8_t esz = col_esz(src); - memcpy((char*)ray_data(out) + (size_t)dst * esz, - (const char*)ray_data(src) + (size_t)row * esz, - esz); -} - -static inline void mode_set_null_cell(ray_t* out, int64_t dst) { - switch (out->type) { - case RAY_F64: - case RAY_F32: - case RAY_I64: - case RAY_TIMESTAMP: - case RAY_I32: - case RAY_DATE: - case RAY_TIME: - case RAY_I16: - par_set_null(out, dst); - return; - case RAY_GUID: - memset((uint8_t*)ray_data(out) + (size_t)dst * 16, 0, 16); - __atomic_fetch_or(&out->attrs, (uint8_t)RAY_ATTR_HAS_NULLS, - __ATOMIC_RELAXED); - return; - case RAY_STR: - memset((ray_str_t*)ray_data(out) + dst, 0, sizeof(ray_str_t)); - return; - case RAY_SYM: - ray_write_sym(ray_data(out), dst, 0, out->type, out->attrs); - return; - case RAY_BOOL: - case RAY_U8: - ((uint8_t*)ray_data(out))[dst] = 0; - return; - default: - return; - } -} - static int64_t mode_scalar_group(ray_t* src, const int64_t* rows, int64_t cnt, int* cancelled) { uint64_t cap = mode_hash_cap(cnt); @@ -2488,6 +2749,7 @@ static void mode_fixed_per_group_fn(void* ctx_v, uint32_t worker_id, return; int64_t off = c->offsets[g]; int64_t cnt = c->grp_cnt[g]; + if (cnt >= c->threshold) continue; int cancelled = 0; int64_t best = (c->src->type == RAY_GUID) ? mode_guid_group(c->src, &c->idx_buf[off], cnt, &cancelled) @@ -2500,42 +2762,45 @@ static void mode_fixed_per_group_fn(void* ctx_v, uint32_t worker_id, atomic_store_explicit(&c->oom, 1, memory_order_relaxed); return; } - if (best < 0) mode_set_null_cell(c->out, g); - else mode_copy_fixed_cell(c->out, g, c->src, best); + c->winners[g] = best; } } -static ray_t* mode_str_per_group_buf(ray_t* src, - const int64_t* idx_buf, - const int64_t* offsets, - const int64_t* grp_cnt, - int64_t n_groups) { - ray_t* out = col_vec_new(src, n_groups); - if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL); - out->len = n_groups; - - for (int64_t g = 0; g < n_groups; g++) { +typedef struct { + ray_t* src; + const int64_t* rows; + const int64_t* offsets; + const int64_t* counts; + int64_t* winners; + int64_t threshold; + _Atomic(bool) oom, cancel; +} mode_str_work_t; +static void mode_str_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; mode_str_work_t* c = raw; + ray_t* src = c->src; + const int64_t* idx_buf = c->rows; + const int64_t* offsets = c->offsets; + const int64_t* grp_cnt = c->counts; + for (int64_t g = start; g < end; g++) { int64_t cnt = grp_cnt[g]; + if (cnt >= c->threshold) continue; int64_t off = offsets[g]; uint64_t cap = mode_hash_cap(cnt); if (!cap || cap > SIZE_MAX / sizeof(mode_wide_entry_t)) { - ray_release(out); - return ray_error("oom", NULL); + atomic_store(&c->oom, true); return; } ray_t* hdr = NULL; mode_wide_entry_t* ht = (mode_wide_entry_t*)scratch_calloc( &hdr, (size_t)cap * sizeof(mode_wide_entry_t)); if (!ht) { - ray_release(out); - return ray_error("oom", NULL); + atomic_store(&c->oom, true); return; } uint64_t mask = cap - 1; int64_t best_count = 0, best_row = -1, best_pos = INT64_MAX; for (int64_t i = 0; i < cnt; i++) { if ((i & 65535) == 0 && ray_interrupted()) { scratch_free(hdr); - ray_release(out); - return ray_error("cancel", "interrupted"); + atomic_store(&c->cancel, true); return; } int64_t row = idx_buf[off + i]; size_t len = 0; @@ -2576,92 +2841,228 @@ static ray_t* mode_str_per_group_buf(ray_t* src, } } scratch_free(hdr); - if (best_row < 0) { - mode_set_null_cell(out, g); - } else { - size_t len = 0; - const char* s = ray_str_vec_get(src, best_row, &len); - ray_t* nv = ray_str_vec_set(out, g, s ? s : "", s ? len : 0); - if (!nv || RAY_IS_ERR(nv)) { - ray_release(out); - return nv ? nv : ray_error("oom", NULL); + c->winners[g] = best_row; + } +} +/* Values are partitioned by their full hash, then counted independently. + * Source-local combining compresses repeated values without changing counts + * or first-occurrence ties. Scratch is linear in rows, not rows × workers. */ +typedef struct { + uint64_t hash; + int64_t row, position, count; +} mode_record_t; +typedef struct { + ray_t* src; + const int64_t* rows; + int64_t count; + uint32_t tasks, partitions; + bool has_nulls; + mode_record_t *records, *scattered; + int64_t *histogram, *ends; + int64_t emitted[RAY_POOL_INIT_TASKS]; + mode_record_t best[RAY_POOL_INIT_TASKS]; + _Atomic(bool) oom; +} mode_split_t; +static uint64_t mode_row_hash(ray_t* src, int64_t row) { + if (src->type == RAY_GUID) + return ray_hash_bytes((const char*)ray_data(src) + (size_t)row * 16, 16); + if (src->type == RAY_STR) { + size_t len = 0; + const char* value = ray_str_vec_get(src, row, &len); + return ray_hash_bytes(value ? value : "", value ? len : 0); + } + return ray_hash_i64((int64_t)mode_scalar_key(src, row)); +} +static bool mode_rows_equal(ray_t* src, int64_t a, int64_t b) { + if (src->type == RAY_GUID) { + const char* base = ray_data(src); + return memcmp(base + (size_t)a * 16, base + (size_t)b * 16, 16) == 0; + } + if (src->type == RAY_STR) { + size_t na = 0, nb = 0; + const char* va = ray_str_vec_get(src, a, &na); + const char* vb = ray_str_vec_get(src, b, &nb); + if (!va) na = 0; + if (!vb) nb = 0; + return na == nb && (!na || memcmp(va, vb, na) == 0); + } + return mode_scalar_key(src, a) == mode_scalar_key(src, b); +} +static bool mode_better(mode_record_t a, mode_record_t b) { + return a.count > b.count || (a.count == b.count && a.position < b.position); +} +static void mode_emit_record(mode_split_t* c, int64_t task, int64_t begin, + int64_t* emitted, mode_record_t record) { + c->records[begin + (*emitted)++] = record; + c->histogram[task * c->partitions + (record.hash & (c->partitions - 1))]++; +} +static void mode_combine_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; mode_split_t* c = raw; + for (int64_t t = start; t < end; t++) { + int64_t begin = c->count / c->tasks * t; + int64_t limit = t + 1 == c->tasks ? c->count : c->count / c->tasks * (t + 1); + mode_record_t local[1024] = {0}; + int64_t emitted = 0; + unsigned used = 0; + for (int64_t i = begin; i < limit; i++) { + if ((i & 65535) == 0 && ray_interrupted()) return; + int64_t row = c->rows[i]; + if (c->has_nulls && ray_vec_is_null(c->src, row)) continue; + uint64_t hash = mode_row_hash(c->src, row); + unsigned at = hash & 1023; + while (local[at].count && (local[at].hash != hash || + !mode_rows_equal(c->src, local[at].row, row))) at = (at + 1) & 1023; + if (local[at].count) local[at].count++; + else { + local[at] = (mode_record_t){hash, row, i, 1}; + if (++used == 768) { + for (unsigned j = 0; j < 1024; j++) + if (local[j].count) mode_emit_record(c, t, begin, &emitted, local[j]); + memset(local, 0, sizeof(local)); used = 0; + } } - out = nv; } + for (unsigned i = 0; i < 1024; i++) + if (local[i].count) mode_emit_record(c, t, begin, &emitted, local[i]); + c->emitted[t] = emitted; } - return out; } - -ray_t* ray_mode_per_group_buf(ray_t* src, - const int64_t* idx_buf, - const int64_t* offsets, - const int64_t* grp_cnt, - int64_t n_groups) { - if (!src || RAY_IS_ERR(src) || n_groups < 0) return NULL; - switch (src->type) { - case RAY_BOOL: - case RAY_U8: - case RAY_I16: - case RAY_I32: - case RAY_I64: - case RAY_F32: - case RAY_F64: - case RAY_DATE: - case RAY_TIME: - case RAY_TIMESTAMP: - case RAY_GUID: - case RAY_SYM: - case RAY_STR: - break; - default: - return NULL; +static void mode_scatter_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; mode_split_t* c = raw; + for (int64_t t = start; t < end; t++) { + int64_t begin = c->count / c->tasks * t; + int64_t* cursor = c->histogram + t * c->partitions; + for (int64_t i = 0; i < c->emitted[t]; i++) { + if ((i & 65535) == 0 && ray_interrupted()) return; + mode_record_t record = c->records[begin + i]; + c->scattered[cursor[record.hash & (c->partitions - 1)]++] = record; + } } - if (src->type == RAY_STR) - return mode_str_per_group_buf(src, idx_buf, offsets, grp_cnt, n_groups); - - int64_t total = 0; - for (int64_t g = 0; g < n_groups; g++) total += grp_cnt[g]; - - ray_t* out = col_vec_new(src, n_groups); - if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL); - if (out->type == RAY_SYM) - ray_sym_vec_adopt_domain(out, sym_domain_rep(src)); - out->len = n_groups; - - mode_par_ctx_t ctx = { - .src = src, - .out = out, - .idx_buf = idx_buf, - .offsets = offsets, - .grp_cnt = grp_cnt, - .oom = 0, - .cancel = 0, - }; - - ray_pool_t* pool = ray_pool_get(); - bool par = pool && n_groups >= 8 && total >= 4096; - if (par) { - if (n_groups < (1 << 16)) - ray_pool_dispatch_n(pool, mode_fixed_per_group_fn, &ctx, - (uint32_t)n_groups); - else - ray_pool_dispatch(pool, mode_fixed_per_group_fn, &ctx, n_groups); - } else { - mode_fixed_per_group_fn(&ctx, 0, 0, n_groups); +} +static void mode_count_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; mode_split_t* c = raw; + for (int64_t p = start; p < end; p++) { + int64_t begin = p ? c->ends[p - 1] : 0, limit = c->ends[p]; + if (begin == limit) continue; + uint64_t cap = mode_hash_cap(limit - begin); + if (!cap || cap > SIZE_MAX / sizeof(mode_record_t)) { atomic_store(&c->oom, true); return; } + ray_t* hdr = NULL; + mode_record_t* table = scratch_calloc(&hdr, (size_t)cap * sizeof(mode_record_t)); + if (!table) { atomic_store(&c->oom, true); return; } + mode_record_t best = {.position = INT64_MAX}; + for (int64_t i = begin; i < limit; i++) { + if ((i & 65535) == 0 && ray_interrupted()) { scratch_free(hdr); return; } + mode_record_t record = c->scattered[i]; + /* Partition bits are constant here; mix again for local slots. */ + uint64_t at = ray_hash_i64((int64_t)record.hash) & (cap - 1); + while (table[at].count && (table[at].hash != record.hash || + !mode_rows_equal(c->src, table[at].row, record.row))) + at = (at + 1) & (cap - 1); + if (!table[at].count) table[at] = record; + else { + table[at].count += record.count; + if (record.position < table[at].position) { + table[at].position = record.position; + table[at].row = record.row; + } + } + if (mode_better(table[at], best)) best = table[at]; + } + c->best[p] = best; + scratch_free(hdr); } +} +static int64_t mode_large_group(ray_t* src, const int64_t* rows, int64_t count) { + ray_pool_t* pool = ray_pool_get(); + mode_split_t c = {.src = src, .rows = rows, .count = count, + .tasks = ray_pool_total_workers(pool) * 4, + /* Empty strings participate in mode; native nulls do not. */ + .has_nulls = src->type != RAY_STR && ray_vec_may_have_nulls(src)}; + if (c.tasks > RAY_POOL_INIT_TASKS / 2) c.tasks = RAY_POOL_INIT_TASKS / 2; + c.partitions = 1; + while (c.partitions < c.tasks) c.partitions *= 2; + /* Includes the scattered records and the worst hash-table capacity. */ + if ((uint64_t)count > SIZE_MAX / 160 || + (uint64_t)count * 160 > (uint64_t)ray_heap_anon_watermark() / 4) return -4; + ray_t *records_hdr = NULL, *scatter_hdr = NULL, *hist_hdr = NULL; + c.records = scratch_alloc(&records_hdr, (size_t)count * sizeof(mode_record_t)); + c.histogram = scratch_calloc(&hist_hdr, + ((size_t)c.tasks * c.partitions + c.partitions) * sizeof(int64_t)); + int64_t answer = -2; + if (!c.records || !c.histogram) goto done; + c.ends = c.histogram + (size_t)c.tasks * c.partitions; + ray_pool_dispatch_n(pool, mode_combine_run, &c, c.tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) { answer = -3; goto done; } + int64_t total = 0; + for (uint32_t p = 0; p < c.partitions; p++) { + for (uint32_t t = 0; t < c.tasks; t++) { + int64_t* slot = &c.histogram[(size_t)t * c.partitions + p]; + int64_t n = *slot; *slot = total; total += n; + } + c.ends[p] = total; + } + if (!total) { answer = -1; goto done; } + c.scattered = scratch_alloc(&scatter_hdr, (size_t)total * sizeof(mode_record_t)); + if (!c.scattered) goto done; + ray_pool_dispatch_n(pool, mode_scatter_run, &c, c.tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) { answer = -3; goto done; } + scratch_free(records_hdr); records_hdr = NULL; + ray_pool_dispatch_n(pool, mode_count_run, &c, c.partitions); + if (ray_interrupted() || atomic_load(&pool->cancelled)) { answer = -3; goto done; } + if (atomic_load(&c.oom)) goto done; + mode_record_t best = {.position = INT64_MAX}; + for (uint32_t p = 0; p < c.partitions; p++) + if (c.best[p].count && mode_better(c.best[p], best)) best = c.best[p]; + answer = best.count ? best.row : -1; +done: + scratch_free(records_hdr); scratch_free(scatter_hdr); scratch_free(hist_hdr); + return answer; +} - if (atomic_load_explicit(&ctx.cancel, memory_order_relaxed) || - ray_interrupted()) { - ray_release(out); - return ray_error("cancel", "interrupted"); - } - if (atomic_load_explicit(&ctx.oom, memory_order_relaxed)) { - ray_release(out); - return ray_error("oom", NULL); +ray_t* ray_mode_per_group_buf(ray_t* src, const int64_t* rows, + const int64_t* offsets, const int64_t* counts, int64_t ng) { + if (!src || RAY_IS_ERR(src) || ng < 0) return NULL; + switch (src->type) { + case RAY_BOOL: case RAY_U8: case RAY_I16: case RAY_I32: case RAY_I64: + case RAY_F32: case RAY_F64: case RAY_DATE: case RAY_TIME: case RAY_TIMESTAMP: + case RAY_GUID: case RAY_SYM: case RAY_STR: break; + default: return NULL; } - if (out->type != RAY_STR && out->type != RAY_BOOL && - out->type != RAY_U8 && out->type != RAY_SYM) - par_finalize_nulls(out); + int64_t* winners = ray_alloc_raw((size_t)(ng ? ng : 1) * sizeof(int64_t)); + if (!winners) return ray_error("oom", NULL); + ray_pool_t* pool = ray_pool_get(); + int64_t total = 0, threshold = INT64_MAX; + for (int64_t g = 0; g < ng; g++) total += counts[g]; + if (ray_pool_par_dispatch_ok(pool, total, 262144)) { + threshold = total / ray_pool_total_workers(pool); + if (threshold < 262144) threshold = 262144; + } + mode_str_work_t strings = {.src = src, .rows = rows, .offsets = offsets, + .counts = counts, .winners = winners, .threshold = threshold}; + mode_par_ctx_t fixed = {.src = src, .idx_buf = rows, .offsets = offsets, + .grp_cnt = counts, .winners = winners, .threshold = threshold}; + if (src->type == RAY_STR) ray_group_dispatch(mode_str_run, &strings, counts, ng); + else ray_group_dispatch(mode_fixed_per_group_fn, &fixed, counts, ng); + for (int64_t g = 0; g < ng; g++) { + if (ray_interrupted() || (pool && atomic_load(&pool->cancelled)) || + atomic_load(&strings.cancel) || atomic_load(&fixed.cancel) || + atomic_load(&strings.oom) || atomic_load(&fixed.oom)) break; + if (counts[g] < threshold) continue; + int64_t winner = mode_large_group(src, rows + offsets[g], counts[g]); + if (winner == -4) { + strings.threshold = fixed.threshold = INT64_MAX; + if (src->type == RAY_STR) mode_str_run(&strings, 0, g, g + 1); + else mode_fixed_per_group_fn(&fixed, 0, g, g + 1); + } else if (winner == -2) atomic_store(&fixed.oom, 1); + else if (winner == -3) atomic_store(&fixed.cancel, 1); + else winners[g] = winner; + } + bool cancel = ray_interrupted() || (pool && atomic_load(&pool->cancelled)) || + atomic_load(&strings.cancel) || atomic_load(&fixed.cancel); + ray_t* out = cancel ? ray_error("cancel", NULL) : + atomic_load(&strings.oom) || atomic_load(&fixed.oom) ? ray_error("oom", NULL) : + ray_group_gather(src, winners, ng); + ray_free_raw(winners); return out; } @@ -2830,6 +3231,8 @@ static void topk_per_group_fn(void* ctx_v, uint32_t worker_id, * heap[0] with heap[n-1] — that puts the root at the end * each iteration, which already gives the desired final * order. */ + if (kept < K) + for (int64_t h = kept / 2; h > 0; h--) topk_sift_down_dbl(dst, kept, h - 1, max_heap); int64_t n = kept; while (n > 1) { double tmp = dst[0]; dst[0] = dst[n-1]; dst[n-1] = tmp; @@ -2879,6 +3282,8 @@ static void topk_per_group_fn(void* ctx_v, uint32_t worker_id, } } } + if (kept < K) + for (int64_t h = kept / 2; h > 0; h--) topk_sift_down_i64(heap, kept, h - 1, max_heap); int64_t n = kept; while (n > 1) { int64_t tmp = heap[0]; heap[0] = heap[n-1]; heap[n-1] = tmp; @@ -2893,7 +3298,150 @@ static void topk_per_group_fn(void* ctx_v, uint32_t worker_id, } } -ray_t* ray_topk_per_group_buf(ray_t* src, +/* Wide and F32 top-K retain source row ids, so domains/string owners never + * enter worker state. Scratch is bounded by K, independent of group size. */ +static int topk_row_cmp(ray_t* src, int64_t a, int64_t b, const ray_group_sym_view_t* view) { + const void* data = ray_data(src); + if (src->type == RAY_F32) { + float x = ((const float*)data)[a], y = ((const float*)data)[b]; + return (x > y) - (x < y); + } + if (src->type == RAY_F64) { + double x = ((const double*)data)[a], y = ((const double*)data)[b]; + return (x > y) - (x < y); + } + if (src->type != RAY_GUID && src->type != RAY_SYM && src->type != RAY_STR) { + int64_t x = topk_read_i64(data, src->type, a), y = topk_read_i64(data, src->type, b); + return (x > y) - (x < y); + } + if (src->type == RAY_GUID) + return memcmp((const char*)data + (size_t)a * 16, (const char*)data + (size_t)b * 16, 16); + if (src->type == RAY_SYM) { + ray_t* x = ray_group_sym_read(view, ray_sym_vec_domain(src), ray_read_sym(data, a, src->type, src->attrs)); + ray_t* y = ray_group_sym_read(view, ray_sym_vec_domain(src), ray_read_sym(data, b, src->type, src->attrs)); + return ray_str_cmp(x, y); + } + size_t na = 0, nb = 0; + const char* x = ray_str_vec_get(src, a, &na); + const char* y = ray_str_vec_get(src, b, &nb); + size_t common = na < nb ? na : nb; + int cmp = common ? memcmp(x, y, common) : 0; + return cmp ? cmp : (na > nb) - (na < nb); +} +static void topk_rows_sift(ray_t* src, int64_t* heap, int64_t n, int64_t root, bool desc, const ray_group_sym_view_t* view) { +#define TOPK_SIFT_NATIVE(TYPE) do { \ + const TYPE* data = ray_data(src); \ + for (;;) { \ + int64_t child = root * 2 + 1; \ + if (child >= n) return; \ + if (child + 1 < n && (desc ? data[heap[child + 1]] < data[heap[child]] : \ + data[heap[child + 1]] > data[heap[child]])) child++; \ + if (!(desc ? data[heap[child]] < data[heap[root]] : data[heap[child]] > data[heap[root]])) return; \ + int64_t swap = heap[root]; heap[root] = heap[child]; heap[child] = swap; \ + root = child; \ + } \ + } while (0) + switch (src->type) { + case RAY_BOOL: case RAY_U8: TOPK_SIFT_NATIVE(uint8_t); + case RAY_I16: TOPK_SIFT_NATIVE(int16_t); + case RAY_I32: case RAY_DATE: case RAY_TIME: TOPK_SIFT_NATIVE(int32_t); + case RAY_I64: case RAY_TIMESTAMP: TOPK_SIFT_NATIVE(int64_t); + case RAY_F32: TOPK_SIFT_NATIVE(float); + case RAY_F64: TOPK_SIFT_NATIVE(double); + default: break; + } +#undef TOPK_SIFT_NATIVE + for (;;) { + int64_t child = root * 2 + 1; + if (child >= n) return; + if (child + 1 < n) { + int cmp = topk_row_cmp(src, heap[child + 1], heap[child], view); + if (desc ? cmp < 0 : cmp > 0) child++; + } + int cmp = topk_row_cmp(src, heap[child], heap[root], view); + if (!(desc ? cmp < 0 : cmp > 0)) return; + int64_t tmp = heap[root]; heap[root] = heap[child]; heap[child] = tmp; + root = child; + } +} +typedef struct { + ray_t* src; + int64_t k; + bool desc; + const int64_t* rows; + const int64_t* offsets; + const int64_t* counts; + ray_t** cells; + _Atomic(bool) oom; + _Atomic(ray_t*) error; + ray_group_sym_view_t symbols; +} topk_wide_work_t; +static void topk_wide_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; topk_wide_work_t* c = raw; + ray_t* src = c->src; + const int64_t* rows = c->rows; + const int64_t* offsets = c->offsets; + const int64_t* counts = c->counts; + bool desc = c->desc; + int64_t max_count = 0; + for (int64_t g = start; g < end; g++) if (counts[g] > max_count) max_count = counts[g]; + int64_t cap = c->k < max_count ? c->k : max_count; + int64_t* heap = ray_alloc_raw((size_t)(cap > 0 ? cap : 1) * sizeof(int64_t)); + if (!heap) { atomic_store(&c->oom, true); return; } + for (int64_t g = start; g < end; g++) { + int64_t kept = 0; + for (int64_t j = 0; j < counts[g]; j++) { + int64_t row = rows[offsets[g] + j]; + if (ray_vec_is_null(src, row)) continue; + if (kept < cap) { + heap[kept++] = row; + if (kept == cap) + for (int64_t h = kept / 2; h > 0; h--) topk_rows_sift(src, heap, kept, h - 1, desc, &c->symbols); + } else { + int cmp = topk_row_cmp(src, row, heap[0], &c->symbols); + if (desc ? cmp > 0 : cmp < 0) { heap[0] = row; topk_rows_sift(src, heap, kept, 0, desc, &c->symbols); } + } + } + if (kept < cap) + for (int64_t h = kept / 2; h > 0; h--) topk_rows_sift(src, heap, kept, h - 1, desc, &c->symbols); + for (int64_t n = kept; n > 1; n--) { + int64_t tmp = heap[0]; heap[0] = heap[n - 1]; heap[n - 1] = tmp; + topk_rows_sift(src, heap, n - 1, 0, desc, &c->symbols); + } + ray_t* cell = gather_by_idx(src, heap, kept); + if (!cell || RAY_IS_ERR(cell)) { + if (cell) { + ray_t* expected = NULL; + if (!atomic_compare_exchange_strong(&c->error, &expected, cell)) ray_release(cell); + } + atomic_store(&c->oom, true); break; + } + c->cells[g] = cell; /* transfer ownership to a disjoint list slot */ + } + ray_free_raw(heap); +} +static ray_t* topk_wide_per_group_buf(ray_t* src, int64_t k, bool desc, + const int64_t* rows, const int64_t* offsets, const int64_t* counts, int64_t ng) { + ray_t* out = ray_list_new(ng); + if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL); + out->len = ng; + memset(ray_data(out), 0, (size_t)ng * sizeof(ray_t*)); + topk_wide_work_t work = {.src = src, .k = k, .desc = desc, .rows = rows, + .offsets = offsets, .counts = counts, .cells = ray_data(out), .oom = false, .error = NULL}; + if (src->type == RAY_SYM) ray_sym_strings_borrow(&work.symbols.strings, &work.symbols.count); + ray_group_dispatch(topk_wide_run, &work, counts, ng); + ray_pool_t* pool = ray_pool_get(); + bool cancel = pool && atomic_load(&pool->cancelled); + ray_t* error = atomic_load(&work.error); + if (cancel || atomic_load(&work.oom)) { + ray_release(out); + if (error) return error; + return ray_error(cancel ? "cancel" : "oom", NULL); + } + return out; +} + +static ray_t* topk_regular_per_group_buf(ray_t* src, int64_t k, uint8_t desc, const int64_t* idx_buf, @@ -2903,13 +3451,13 @@ ray_t* ray_topk_per_group_buf(ray_t* src, if (!src || RAY_IS_ERR(src) || n_groups < 0) return NULL; if (k < 1) return NULL; int8_t t = src->type; + if (t == RAY_F32 || t == RAY_GUID || t == RAY_SYM || t == RAY_STR) + return topk_wide_per_group_buf(src, k, desc, idx_buf, offsets, grp_cnt, n_groups); if (t != RAY_F64 && t != RAY_I64 && t != RAY_I32 && t != RAY_I16 && t != RAY_U8 && t != RAY_BOOL && t != RAY_DATE && t != RAY_TIME && t != RAY_TIMESTAMP) return NULL; - int64_t total = 0; - for (int64_t g = 0; g < n_groups; g++) total += grp_cnt[g]; ray_t* out = ray_list_new(n_groups); if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL); @@ -2949,18 +3497,7 @@ ray_t* ray_topk_per_group_buf(ray_t* src, .oom = &oom, }; - ray_pool_t* pool = ray_pool_get(); - bool par = pool && n_groups >= 8 && total >= 4096; - if (par) { - /* See ray_median_per_group_buf for the rationale on the - * dispatch_n vs dispatch split. */ - if (n_groups < (1 << 16)) - ray_pool_dispatch_n(pool, topk_per_group_fn, &ctx, (uint32_t)n_groups); - else - ray_pool_dispatch(pool, topk_per_group_fn, &ctx, n_groups); - } else { - topk_per_group_fn(&ctx, 0, 0, n_groups); - } + ray_group_dispatch(topk_per_group_fn, &ctx, grp_cnt, n_groups); if (atomic_load_explicit(&oom, memory_order_relaxed)) { ray_release(out); @@ -2970,6 +3507,521 @@ ray_t* ray_topk_per_group_buf(ray_t* src, return out; } +/* A group's global K winners are contained in the union of its chunks' K + * winners. Keep only row ids and merge those bounded heaps after the barrier. */ +typedef struct { + ray_t* source; + const int64_t* rows; + int64_t count, k, heap_stride; + uint32_t tasks, source_tasks; + bool desc, sorted; + int64_t* heaps; + int64_t kept[RAY_POOL_INIT_TASKS]; + ray_group_sym_view_t symbols; +} topk_chunks_t; +static void topk_keep_row(topk_chunks_t* c, int64_t* heap, int64_t* kept, int64_t row) { + if (*kept < c->k) { + heap[(*kept)++] = row; + if (*kept == c->k) + for (int64_t h = *kept / 2; h > 0; h--) + topk_rows_sift(c->source, heap, *kept, h - 1, c->desc, &c->symbols); + } else { + int cmp = topk_row_cmp(c->source, row, heap[0], &c->symbols); + if (c->desc ? cmp > 0 : cmp < 0) { + heap[0] = row; + topk_rows_sift(c->source, heap, *kept, 0, c->desc, &c->symbols); + } + } +} +static void topk_sort_chunk(topk_chunks_t* c, int64_t t, int64_t kept) { + int64_t* heap = c->heaps + t * (c->heap_stride ? c->heap_stride : c->k); + if (kept < c->k) + for (int64_t h = kept / 2; h > 0; h--) + topk_rows_sift(c->source, heap, kept, h - 1, c->desc, &c->symbols); + for (int64_t n = kept; n > 1; n--) { + int64_t swap = heap[0]; heap[0] = heap[n - 1]; heap[n - 1] = swap; + topk_rows_sift(c->source, heap, n - 1, 0, c->desc, &c->symbols); + } +} +static void topk_sort_chunks_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; topk_chunks_t* c = raw; + for (int64_t t = start; t < end; t++) topk_sort_chunk(c, t, c->kept[t]); +} +static void topk_chunks_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + topk_chunks_t* c = raw; + uint32_t sources = c->source_tasks ? c->source_tasks : c->tasks; + for (int64_t t = start; t < end; t++) { + int64_t remainder = c->count % sources; + int64_t begin = c->count / sources * t + (t < remainder ? t : remainder); + int64_t limit = begin + c->count / sources + (t < remainder); + int64_t heap_id = c->source_tasks ? wid : t; + int64_t kept = c->source_tasks ? c->kept[heap_id] : 0; + int64_t* heap = c->heaps + heap_id * (c->heap_stride ? c->heap_stride : c->k); + bool has_nulls = ray_vec_may_have_nulls(c->source); + /* Most rows lose against the heap root. Dispatch native types once, + * and enter the general comparator only when the heap must change. */ +#define TOPK_SCAN_NATIVE(TYPE, VALID) do { \ + const TYPE* data = ray_data(c->source); \ + for (int64_t i = begin; i < limit; i++) { \ + if ((i & 65535) == 0 && ray_interrupted()) return; \ + int64_t row = c->rows[i]; \ + TYPE value = data[row]; \ + if (has_nulls && !(VALID)) continue; \ + if (kept < c->k) { \ + topk_keep_row(c, heap, &kept, row); \ + } else if (c->desc ? value > data[heap[0]] : value < data[heap[0]]) { \ + heap[0] = row; \ + int64_t root = 0; \ + for (;;) { \ + int64_t child = root * 2 + 1; \ + if (child >= kept) break; \ + if (child + 1 < kept && (c->desc ? data[heap[child + 1]] < data[heap[child]] : \ + data[heap[child + 1]] > data[heap[child]])) child++; \ + if (!(c->desc ? data[heap[child]] < data[heap[root]] : \ + data[heap[child]] > data[heap[root]])) break; \ + int64_t swap = heap[root]; heap[root] = heap[child]; heap[child] = swap; \ + root = child; \ + } \ + } \ + } \ + } while (0) + switch (c->source->type) { + case RAY_BOOL: case RAY_U8: TOPK_SCAN_NATIVE(uint8_t, true); break; + case RAY_I16: TOPK_SCAN_NATIVE(int16_t, value != NULL_I16); break; + case RAY_I32: case RAY_DATE: case RAY_TIME: + TOPK_SCAN_NATIVE(int32_t, value != NULL_I32); break; + case RAY_I64: case RAY_TIMESTAMP: + TOPK_SCAN_NATIVE(int64_t, value != NULL_I64); break; + case RAY_F32: TOPK_SCAN_NATIVE(float, value == value); break; + case RAY_F64: TOPK_SCAN_NATIVE(double, value == value); break; + default: + for (int64_t i = begin; i < limit; i++) { + if ((i & 65535) == 0 && ray_interrupted()) return; + if (!ray_vec_is_null(c->source, c->rows[i])) + topk_keep_row(c, heap, &kept, c->rows[i]); + } + break; + } +#undef TOPK_SCAN_NATIVE + if (c->sorted && !c->source_tasks) topk_sort_chunk(c, heap_id, kept); + c->kept[heap_id] = kept; + } +} +/* Large K can approach the group size. Sort bounded chunk candidates, then + * merge only the first K rows of each pair. Output-rank slices parallelize + * even the final pair, so no coordinator scans the whole group's winners. */ +typedef struct { + const topk_chunks_t* values; + const int64_t* input; + int64_t* output; + struct { + int64_t left, nl, right, nr, output, begin, end; + } job[RAY_POOL_INIT_TASKS]; +} topk_merge_t; +static bool topk_before(const topk_chunks_t* c, int64_t a, int64_t b, bool equal) { + int cmp = topk_row_cmp(c->source, a, b, &c->symbols); + return c->desc ? (equal ? cmp >= 0 : cmp > 0) : (equal ? cmp <= 0 : cmp < 0); +} +static void topk_merge_run(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; topk_merge_t* c = raw; + for (int64_t t = start; t < end; t++) { + const int64_t* left = c->input + c->job[t].left; + const int64_t* right = c->input + c->job[t].right; + int64_t nl = c->job[t].nl, nr = c->job[t].nr, diagonal = c->job[t].begin; + int64_t low = diagonal > nr ? diagonal - nr : 0; + int64_t high = diagonal < nl ? diagonal : nl, i = 0, j = 0; + /* Co-rank a merge position. Equal values from the left run precede + * the right run, making every output slice use the same boundary. */ + while (low <= high) { + i = low + (high - low) / 2; j = diagonal - i; + if (i > 0 && j < nr && !topk_before(c->values, left[i - 1], right[j], true)) high = i - 1; + else if (j > 0 && i < nl && topk_before(c->values, left[i], right[j - 1], true)) low = i + 1; + else break; + } + for (int64_t pos = diagonal; pos < c->job[t].end; pos++) { + if ((pos & 65535) == 0 && ray_interrupted()) return; + bool take_left = i < nl && (j == nr || topk_before(c->values, left[i], right[j], true)); + c->output[c->job[t].output + pos] = take_left ? left[i++] : right[j++]; + } + } +} +typedef struct { + const topk_chunks_t* values; + int64_t *input, *output; + int64_t base, n, pivot; + struct { int64_t begin, end, less, equal, lo, eq, hi; } task[RAY_POOL_INIT_TASKS]; +} topk_select_t; +static void topk_select_ranges(topk_select_t* c) { + int64_t tasks = c->values->tasks, remainder = c->n % tasks; + for (int64_t t = 0; t < tasks; t++) { + c->task[t].begin = c->base + c->n / tasks * t + (t < remainder ? t : remainder); + c->task[t].end = c->task[t].begin + c->n / tasks + (t < remainder); + } +} +static void topk_select_gather(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; topk_select_t* c = raw; + for (int64_t t = start; t < end; t++) { + int64_t at = c->task[t].begin; + for (int64_t i = at; i < c->task[t].end; i++) { + if ((i & 65535) == 0 && ray_interrupted()) return; + int64_t row = c->values->rows[i]; + if (!ray_vec_is_null(c->values->source, row)) c->input[at++] = row; + } + c->task[t].less = at - c->task[t].begin; + } +} +static void topk_select_compact(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; topk_select_t* c = raw; + for (int64_t t = start; t < end; t++) + memcpy(c->output + c->task[t].lo, c->input + c->task[t].begin, + (size_t)c->task[t].less * sizeof(int64_t)); +} +static void topk_select_count(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; topk_select_t* c = raw; + ray_t* src = c->values->source; +#define TOPK_SELECT_COUNT(TYPE) do { \ + const TYPE* data = ray_data(src); \ + TYPE pivot = data[c->pivot]; \ + for (int64_t t = start; t < end; t++) { \ + int64_t less = 0, equal = 0; \ + for (int64_t i = c->task[t].begin; i < c->task[t].end; i++) { \ + if ((i & 65535) == 0 && ray_interrupted()) return; \ + TYPE value = data[c->input[i]]; \ + int cmp = (value > pivot) - (value < pivot); \ + if (!cmp) equal++; \ + else if (c->values->desc ? cmp > 0 : cmp < 0) less++; \ + } \ + c->task[t].less = less; c->task[t].equal = equal; \ + } \ + return; \ + } while (0) + switch (src->type) { + case RAY_BOOL: case RAY_U8: TOPK_SELECT_COUNT(uint8_t); + case RAY_I16: TOPK_SELECT_COUNT(int16_t); + case RAY_I32: case RAY_DATE: case RAY_TIME: TOPK_SELECT_COUNT(int32_t); + case RAY_I64: case RAY_TIMESTAMP: TOPK_SELECT_COUNT(int64_t); + case RAY_F32: TOPK_SELECT_COUNT(float); + case RAY_F64: TOPK_SELECT_COUNT(double); + default: break; + } +#undef TOPK_SELECT_COUNT + for (int64_t t = start; t < end; t++) { + int64_t less = 0, equal = 0; + for (int64_t i = c->task[t].begin; i < c->task[t].end; i++) { + if ((i & 65535) == 0 && ray_interrupted()) return; + int cmp = topk_row_cmp(c->values->source, c->input[i], c->pivot, &c->values->symbols); + if (!cmp) equal++; + else if (c->values->desc ? cmp > 0 : cmp < 0) less++; + } + c->task[t].less = less; c->task[t].equal = equal; + } +} +static void topk_select_scatter(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; topk_select_t* c = raw; + ray_t* src = c->values->source; +#define TOPK_SELECT_SCATTER(TYPE) do { \ + const TYPE* data = ray_data(src); \ + TYPE pivot = data[c->pivot]; \ + for (int64_t t = start; t < end; t++) { \ + int64_t lo = c->task[t].lo, eq = c->task[t].eq, hi = c->task[t].hi; \ + /* Keep the already selected prefix when exchanging scratch buffers. */ \ + int64_t begin = c->base / c->values->tasks * t; \ + int64_t limit = t + 1 == c->values->tasks ? c->base : c->base / c->values->tasks * (t + 1); \ + memcpy(c->output + begin, c->input + begin, (size_t)(limit - begin) * sizeof(int64_t)); \ + for (int64_t i = c->task[t].begin; i < c->task[t].end; i++) { \ + if ((i & 65535) == 0 && ray_interrupted()) return; \ + int64_t row = c->input[i]; \ + TYPE value = data[row]; \ + int cmp = (value > pivot) - (value < pivot); \ + if (!cmp) c->output[eq++] = row; \ + else if (c->values->desc ? cmp > 0 : cmp < 0) c->output[lo++] = row; \ + else c->output[hi++] = row; \ + } \ + } \ + return; \ + } while (0) + switch (src->type) { + case RAY_BOOL: case RAY_U8: TOPK_SELECT_SCATTER(uint8_t); + case RAY_I16: TOPK_SELECT_SCATTER(int16_t); + case RAY_I32: case RAY_DATE: case RAY_TIME: TOPK_SELECT_SCATTER(int32_t); + case RAY_I64: case RAY_TIMESTAMP: TOPK_SELECT_SCATTER(int64_t); + case RAY_F32: TOPK_SELECT_SCATTER(float); + case RAY_F64: TOPK_SELECT_SCATTER(double); + default: break; + } +#undef TOPK_SELECT_SCATTER + for (int64_t t = start; t < end; t++) { + int64_t lo = c->task[t].lo, eq = c->task[t].eq, hi = c->task[t].hi; + /* Keep the already selected prefix when exchanging scratch buffers. */ + int64_t begin = c->base / c->values->tasks * t; + int64_t limit = t + 1 == c->values->tasks ? c->base : c->base / c->values->tasks * (t + 1); + memcpy(c->output + begin, c->input + begin, (size_t)(limit - begin) * sizeof(int64_t)); + for (int64_t i = c->task[t].begin; i < c->task[t].end; i++) { + if ((i & 65535) == 0 && ray_interrupted()) return; + int64_t row = c->input[i]; + int cmp = topk_row_cmp(c->values->source, row, c->pivot, &c->values->symbols); + if (!cmp) c->output[eq++] = row; + else if (c->values->desc ? cmp > 0 : cmp < 0) c->output[lo++] = row; + else c->output[hi++] = row; + } + } +} +/* Three-way selection leaves the exact K best rows in the prefix. Parallel + * partitioning handles large ranges; a bounded final range uses the same + * comparison in place. Sampling chooses the pivot, never the answer. */ +static int64_t topk_select_rows(const topk_chunks_t* values, int64_t** input, + int64_t** scratch, int64_t k) { + topk_select_t c = {.values = values, .input = *input, .output = *scratch, .n = values->count}; + ray_pool_t* pool = ray_pool_get(); + topk_select_ranges(&c); + ray_pool_dispatch_n(pool, topk_select_gather, &c, values->tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) return -1; + int64_t valid = 0; + for (uint32_t t = 0; t < values->tasks; t++) { c.task[t].lo = valid; valid += c.task[t].less; } + if (valid != c.n) { + ray_pool_dispatch_n(pool, topk_select_compact, &c, values->tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) return -1; + int64_t* swap = c.input; c.input = c.output; c.output = swap; + } + c.n = valid; + if (k > valid) k = valid; + unsigned depth = 0; + while (k > 0 && c.base + c.n > k) { + if (ray_interrupted() || atomic_load(&pool->cancelled)) return -1; + int64_t sample[33], step = c.n / 33; + unsigned ns = step ? 33 : (unsigned)c.n; + for (unsigned i = 0; i < ns; i++) { + sample[i] = c.input[c.base + (step ? i * step + ray_hash_i64(i + depth * 33) % step : i)]; + for (unsigned j = i; j && topk_before(values, sample[j], sample[j - 1], false); j--) { + int64_t swap = sample[j]; sample[j] = sample[j - 1]; sample[j - 1] = swap; + } + } + c.pivot = sample[ns / 2]; + int64_t less = 0, equal = 0; + if (c.n < 65536 || depth >= 32) { + if (depth >= 32) { + /* Bounded worst-case fallback: sort only the active range. */ + int64_t* heap = c.input + c.base; + for (int64_t h = c.n / 2; h > 0; h--) topk_rows_sift(values->source, heap, c.n, h - 1, values->desc, &values->symbols); + for (int64_t n = c.n; n > 1; n--) { + int64_t swap = heap[0]; heap[0] = heap[n - 1]; heap[n - 1] = swap; + topk_rows_sift(values->source, heap, n - 1, 0, values->desc, &values->symbols); + } + break; + } + int64_t lo = c.base, at = lo, hi = c.base + c.n; + while (at < hi) { + int cmp = topk_row_cmp(values->source, c.input[at], c.pivot, &values->symbols); + if (!cmp) at++; + else if (values->desc ? cmp > 0 : cmp < 0) { + int64_t swap = c.input[lo]; c.input[lo++] = c.input[at]; c.input[at++] = swap; + } else { + int64_t swap = c.input[--hi]; c.input[hi] = c.input[at]; c.input[at] = swap; + } + } + less = lo - c.base; equal = hi - lo; + } else { + topk_select_ranges(&c); + ray_pool_dispatch_n(pool, topk_select_count, &c, values->tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) return -1; + for (uint32_t t = 0; t < values->tasks; t++) { less += c.task[t].less; equal += c.task[t].equal; } + int64_t lo = c.base, eq = lo + less, hi = eq + equal; + for (uint32_t t = 0; t < values->tasks; t++) { + c.task[t].lo = lo; lo += c.task[t].less; + c.task[t].eq = eq; eq += c.task[t].equal; + c.task[t].hi = hi; hi += c.task[t].end - c.task[t].begin - c.task[t].less - c.task[t].equal; + } + ray_pool_dispatch_n(pool, topk_select_scatter, &c, values->tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) return -1; + int64_t* swap = c.input; c.input = c.output; c.output = swap; + } + depth++; + if (k <= c.base + less) c.n = less; + else if (k <= c.base + less + equal) break; + else { c.base += less + equal; c.n -= less + equal; } + } + *input = c.input; *scratch = c.output; + return k; +} +static ray_t* topk_sorted_large_group(ray_t* src, const int64_t* rows, int64_t count, + int64_t k, bool desc, uint32_t tasks) { + /* Sorting each chunk's K candidates costs K*log2(K). Select first + * once that work approaches a full scan of the source rows. */ + unsigned levels = 64 - (unsigned)__builtin_clzll((uint64_t)k); + bool select = k > count / tasks / levels / 2; + /* A near-full result also needs sorting. Bound each sorting run so + * small pools do not sort multi-million-row heaps on one worker. */ + if (select) { + uint32_t limit = tasks * 4; + if (limit > RAY_POOL_INIT_TASKS / 2) limit = RAY_POOL_INIT_TASKS / 2; + while (tasks < limit && k / tasks > 262144) tasks *= 2; + if (tasks > limit) tasks = limit; + } + int64_t cap = (count + tasks - 1) / tasks; + if (cap > k) cap = k; + if ((uint64_t)cap > SIZE_MAX / tasks / sizeof(int64_t) || + (uint64_t)count > SIZE_MAX / sizeof(int64_t) - tasks) return ray_error("oom", NULL); + size_t bytes = (select ? (size_t)count + tasks : (size_t)tasks * (size_t)cap) * sizeof(int64_t); + topk_chunks_t c = {.source = src, .rows = rows, .count = count, .k = cap, + .tasks = tasks, .desc = desc, .sorted = true}; + int64_t* first = ray_alloc_raw(bytes); + int64_t* second = ray_alloc_raw(bytes); + if (!first || !second) { ray_free_raw(first); ray_free_raw(second); return ray_error("oom", NULL); } + c.heaps = first; + if (src->type == RAY_SYM) ray_sym_strings_borrow(&c.symbols.strings, &c.symbols.count); + ray_pool_t* pool = ray_pool_get(); + ray_t* out = NULL; + int64_t *input = first, *output = second; + if (select) { + int64_t kept = topk_select_rows(&c, &input, &output, k); + if (kept < 0) goto cancelled; + ray_profile_tick("topK: selected rows"); + c.rows = input; c.count = kept; + cap = (kept + tasks - 1) / tasks; + if (cap < 1) cap = 1; + c.k = cap; c.heaps = output; + ray_pool_dispatch_n(pool, topk_chunks_run, &c, tasks); + int64_t* swap = input; input = output; output = swap; + } else { + /* Work stealing balances source chunks without multiplying K-sized + * heaps. Each physical worker owns one heap across its claimed tasks. */ + if (tasks == ray_pool_total_workers(pool)) { + c.source_tasks = tasks * 4; + if (c.source_tasks > RAY_POOL_INIT_TASKS / 2) c.source_tasks = RAY_POOL_INIT_TASKS / 2; + } + ray_pool_dispatch_n(pool, topk_chunks_run, &c, c.source_tasks ? c.source_tasks : tasks); + if (ray_interrupted() || atomic_load(&pool->cancelled)) goto cancelled; + ray_profile_tick("topK: scanned chunks"); + if (c.source_tasks) ray_pool_dispatch_n(pool, topk_sort_chunks_run, &c, tasks); + } + ray_profile_tick("topK: sorted chunks"); + int64_t offsets[RAY_POOL_INIT_TASKS], next_offsets[RAY_POOL_INIT_TASKS], next_counts[RAY_POOL_INIT_TASKS]; + for (uint32_t t = 0; t < tasks; t++) offsets[t] = t * cap; + topk_merge_t merge = {.values = &c, .input = input, .output = output}; + uint32_t runs = tasks; + while (runs > 1) { + if (ray_interrupted() || atomic_load(&pool->cancelled)) goto cancelled; + int64_t total = 0; + uint32_t pairs = (runs + 1) / 2; + for (uint32_t p = 0; p < pairs; p++) { + int64_t length = c.kept[p * 2] + (p * 2 + 1 < runs ? c.kept[p * 2 + 1] : 0); + next_offsets[p] = total; next_counts[p] = length < k ? length : k; + total += next_counts[p]; + } + int64_t grain = (total + tasks - 1) / tasks; + if (grain < 1) grain = 1; + uint32_t jobs = 0; + for (uint32_t p = 0; p < pairs; p++) { + uint32_t l = p * 2, r = l + 1; + for (int64_t pos = 0; pos < next_counts[p]; pos += grain) { + merge.job[jobs].left = offsets[l]; merge.job[jobs].nl = c.kept[l]; + merge.job[jobs].right = r < runs ? offsets[r] : 0; + merge.job[jobs].nr = r < runs ? c.kept[r] : 0; + merge.job[jobs].output = next_offsets[p]; merge.job[jobs].begin = pos; + merge.job[jobs++].end = pos + grain < next_counts[p] ? pos + grain : next_counts[p]; + } + } + if (jobs) ray_pool_dispatch_n(pool, topk_merge_run, &merge, jobs); + ray_profile_tick("topK: merged level"); + const int64_t* swap = merge.input; merge.input = merge.output; merge.output = (int64_t*)swap; + memcpy(offsets, next_offsets, pairs * sizeof(int64_t)); + memcpy(c.kept, next_counts, pairs * sizeof(int64_t)); + runs = pairs; + } + if (ray_interrupted() || atomic_load(&pool->cancelled)) goto cancelled; + out = ray_group_gather(src, merge.input, c.kept[0]); + ray_profile_tick("topK: gathered output"); + goto done; +cancelled: + out = ray_error("cancel", NULL); +done: + ray_free_raw(first); ray_free_raw(second); + return out; +} + +static ray_t* topk_large_group(ray_t* src, const int64_t* rows, int64_t count, + int64_t k, bool desc, uint32_t tasks) { + if (k >= 64 || k > count / tasks / 2) + return topk_sorted_large_group(src, rows, count, k, desc, tasks); + topk_chunks_t c = {.source = src, .rows = rows, .count = count, .k = k, + .tasks = tasks, .desc = desc, .heap_stride = ((k + 7) & ~INT64_C(7)) + 8}; + /* Updating adjacent tiny heaps must not bounce the same cache line + * between workers when every incoming row improves the current winner. */ + c.heaps = ray_alloc_raw((size_t)(tasks + 1) * (size_t)c.heap_stride * sizeof(int64_t)); + if (!c.heaps) return ray_error("oom", NULL); + if (src->type == RAY_SYM) ray_sym_strings_borrow(&c.symbols.strings, &c.symbols.count); + ray_pool_t* pool = ray_pool_get(); + if (tasks == ray_pool_total_workers(pool)) { + c.source_tasks = tasks * 4; + if (c.source_tasks > RAY_POOL_INIT_TASKS / 2) c.source_tasks = RAY_POOL_INIT_TASKS / 2; + } + ray_pool_dispatch_n(pool, topk_chunks_run, &c, c.source_tasks ? c.source_tasks : tasks); + ray_profile_tick("topK: selected chunk candidates"); + if (ray_interrupted() || atomic_load(&pool->cancelled)) { + ray_free_raw(c.heaps); return ray_error("cancel", NULL); + } + int64_t* heap = c.heaps + tasks * c.heap_stride; + int64_t kept = 0; + for (uint32_t t = 0; t < tasks; t++) + for (int64_t i = 0; i < c.kept[t]; i++) topk_keep_row(&c, heap, &kept, c.heaps[t * c.heap_stride + i]); + if (kept < k) + for (int64_t h = kept / 2; h > 0; h--) topk_rows_sift(src, heap, kept, h - 1, desc, &c.symbols); + for (int64_t n = kept; n > 1; n--) { + int64_t swap = heap[0]; heap[0] = heap[n - 1]; heap[n - 1] = swap; + topk_rows_sift(src, heap, n - 1, 0, desc, &c.symbols); + } + ray_t* out = ray_group_gather(src, heap, kept); + ray_free_raw(c.heaps); + return out; +} +ray_t* ray_topk_per_group_buf(ray_t* src, int64_t k, uint8_t desc, + const int64_t* rows, const int64_t* offsets, const int64_t* counts, int64_t groups) { + if (!src || RAY_IS_ERR(src) || groups < 0 || k < 1) return NULL; + int64_t total = 0; + for (int64_t g = 0; g < groups; g++) total += counts[g]; + ray_pool_t* pool = ray_pool_get(); + if (ray_interrupted() || (pool && atomic_load(&pool->cancelled))) return ray_error("cancel", NULL); + if (!ray_pool_par_dispatch_ok(pool, total, 262144)) + return topk_regular_per_group_buf(src, k, desc, rows, offsets, counts, groups); + /* Keep one candidate heap per available worker; source tasks can be + * smaller without replicating heap capacity or sorting work. */ + uint32_t tasks = ray_pool_total_workers(pool); + if (tasks > RAY_POOL_INIT_TASKS / 2) tasks = RAY_POOL_INIT_TASKS / 2; + int64_t threshold = total / ray_pool_total_workers(pool); + if (threshold < 262144) threshold = 262144; + int64_t* regular = NULL; + for (int64_t g = 0; g < groups; g++) if (counts[g] >= threshold) { + if (!regular) { + regular = ray_alloc_raw((size_t)groups * sizeof(int64_t)); + if (!regular) return ray_error("oom", NULL); + memcpy(regular, counts, (size_t)groups * sizeof(int64_t)); + } + regular[g] = 0; + } + if (!regular) return topk_regular_per_group_buf(src, k, desc, rows, offsets, counts, groups); + ray_t* out = topk_regular_per_group_buf(src, k, desc, rows, offsets, regular, groups); + if (out && !RAY_IS_ERR(out)) { + ray_t** cells = ray_data(out); + for (int64_t g = 0; g < groups; g++) if (counts[g] && !regular[g]) { + ray_t* cell = topk_large_group(src, rows + offsets[g], counts[g], k, desc, tasks); + if (!cell || RAY_IS_ERR(cell)) { ray_release(out); out = cell; break; } + ray_release(cells[g]); cells[g] = cell; + } + } + ray_free_raw(regular); + return out; +} + +typedef struct { + ray_t* src; + uint16_t op; + bool has_nulls; +} wide_winners_t; +static int64_t wide_winner_rows(void* raw, const int64_t* rows, int64_t count) { + wide_winners_t* c = raw; + return wide_winner_row(c->src, c->op, rows, count, c->has_nulls); +} + /* ─── ray_wide_minmax_per_group_buf ─────────────────────────────────────── * * Per-group min/max/first/last for elements that cannot flow through the @@ -2980,9 +4032,8 @@ ray_t* ray_topk_per_group_buf(ray_t* src, * materialises that element into a typed result column. LIST elements retain * the selected boxed child into a new owning list. * - * Runs SERIAL: ray_str_vec_set COW-mutates the result vector and its shared - * string pool, so concurrent group writers would corrupt the pool. Wide - * min/max is a cold path, not a vectorised bench kernel, so this is fine. */ + * Winner selection runs in parallel over immutable source columns. Result + * ownership and pooled string construction are handled after the barrier. */ ray_t* ray_wide_minmax_per_group_buf(ray_t* src, uint16_t op, const int64_t* idx_buf, const int64_t* offsets, @@ -2992,38 +4043,16 @@ ray_t* ray_wide_minmax_per_group_buf(ray_t* src, uint16_t op, if (!agg_needs_row_gather(src->type, op)) return NULL; /* caller falls back */ bool has_nulls = ray_vec_may_have_nulls(src); - ray_t* out = src->type == RAY_LIST - ? ray_list_new(n_groups) - : col_vec_new(src, n_groups); - if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL); - /* LIST length advances with initialized child slots so every error path - * releases exactly the children whose ownership has been retained. */ - out->len = src->type == RAY_LIST ? 0 : n_groups; - - for (int64_t g = 0; g < n_groups; g++) { - int64_t cnt = grp_cnt[g]; - int64_t off = offsets[g]; - int64_t best = wide_winner_row(src, op, &idx_buf[off], cnt, has_nulls); - if (src->type == RAY_LIST) { - ray_t* e = best < 0 ? NULL : ((ray_t**)ray_data(src))[best]; - if (e) ray_retain(e); - ((ray_t**)ray_data(out))[g] = e; - out->len = g + 1; - continue; - } - if (best < 0) { ray_vec_set_null(out, g, true); continue; } - int alloc; - ray_t* e = collection_elem(src, best, &alloc); - if (src->type == RAY_STR) { - ray_t* nv = ray_str_vec_set(out, g, ray_str_ptr(e), ray_str_len(e)); - if (alloc) ray_release(e); - if (!nv || RAY_IS_ERR(nv)) { if (nv != out) ray_release(out); return nv ? nv : ray_error("oom", NULL); } - out = nv; - } else { /* RAY_GUID — fixed 16-byte in-place store */ - store_typed_elem(out, g, e); - if (alloc) ray_release(e); - } + int64_t* winners = ray_alloc_raw((size_t)(n_groups ? n_groups : 1) * sizeof(int64_t)); + if (!winners) return ray_error("oom", NULL); + wide_winners_t work = {src, op, has_nulls}; + ray_group_winners(wide_winner_rows, &work, idx_buf, offsets, grp_cnt, n_groups, winners); + ray_pool_t* pool = ray_pool_get(); + if (ray_interrupted() || (pool && atomic_load_explicit(&pool->cancelled, memory_order_relaxed))) { + ray_free_raw(winners); return ray_error("cancel", NULL); } + ray_t* out = ray_group_gather(src, winners, n_groups); + ray_free_raw(winners); return out; } @@ -10333,7 +11362,7 @@ static ray_t* exec_group_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, * produced and continue on the generic paths. */ if (g->sg_col) { ray_t* sgr = exec_group_slices(g, op, tbl, group_limit); - if (sgr) { sg_hint_release(g); return sgr; } + if (sgr) { agg_route_record(AGG_ROUTE_SLICES); sg_hint_release(g); return sgr; } ray_t* err = sg_hint_to_selection(g, tbl); if (err) return err; } @@ -10344,6 +11373,7 @@ static ray_t* exec_group_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, for (int64_t c = 0; c < nc; c++) { ray_t* col = ray_table_get_col_idx(tbl, c); if (col && (RAY_IS_PARTED(col->type) || col->type == RAY_MAPCOMMON)) { + agg_route_record(AGG_ROUTE_PARTED); return exec_group_parted(g, op, tbl, group_limit); } } @@ -10358,9 +11388,12 @@ static ray_t* exec_group_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, * it down to the radix strategy's bounded emit and the caller trims the * result either way, so a positive limit stays on v2 rather than falling * back to the (slower, full-materialization) legacy ladder. */ - if (ray_agg_engine_v2 && group_limit >= 0 - && !ray_group_emit_filter_get().enabled - && agg_v2_can_handle(g, op, tbl)) + agg_v2_reason_t admission = !ray_agg_engine_v2 ? AGG_V2_DISABLED + : group_limit < 0 ? AGG_V2_SHAPE + : ray_group_emit_filter_get().enabled ? AGG_V2_EMIT_FILTER + : agg_v2_admission(g, op, tbl); + agg_route_reason(admission); + if (admission == AGG_V2_ADMITTED) return exec_group_v2(g, op, tbl, group_limit); /* Emit-filter shape on a wide-domain SYM key: the sp dense/sparse @@ -10428,6 +11461,7 @@ static ray_t* exec_group_run(ray_graph_t* g, ray_op_t* op, ray_t* tbl, if (r) return r; } + agg_route_record(AGG_ROUTE_LEGACY); int64_t nrows = ray_table_nrows(tbl); uint32_t n_keys = ext->n_keys; uint32_t n_aggs = ext->n_aggs; diff --git a/src/ops/idiom.c b/src/ops/idiom.c index 3cd4d4c1f..508fc0053 100644 --- a/src/ops/idiom.c +++ b/src/ops/idiom.c @@ -24,7 +24,6 @@ #include "vec/vec.h" #include "idiom.h" #include "opt.h" -#include "mem/sys.h" #include "mem/heap.h" #include @@ -260,20 +259,20 @@ ray_op_t* ray_idiom_pass(ray_graph_t* g, ray_op_t* root) { uint32_t cap = nc * 2; uint32_t stk1_local[256], stk2_local[256]; - uint32_t* stk1 = cap <= 256 ? stk1_local : (uint32_t*)ray_sys_alloc(cap * sizeof(uint32_t)); - uint32_t* stk2 = cap <= 256 ? stk2_local : (uint32_t*)ray_sys_alloc(cap * sizeof(uint32_t)); + uint32_t* stk1 = cap <= 256 ? stk1_local : (uint32_t*)ray_alloc_raw(cap * sizeof(uint32_t)); + uint32_t* stk2 = cap <= 256 ? stk2_local : (uint32_t*)ray_alloc_raw(cap * sizeof(uint32_t)); if (!stk1 || !stk2) { - if (stk1 && stk1 != stk1_local) ray_sys_free(stk1); - if (stk2 && stk2 != stk2_local) ray_sys_free(stk2); + if (stk1 && stk1 != stk1_local) ray_free_raw(stk1); + if (stk2 && stk2 != stk2_local) ray_free_raw(stk2); return root; } /* Visited-bit guard against re-entry on shared subgraphs. */ uint8_t visited_local[256]; - uint8_t* visited = nc <= 256 ? visited_local : (uint8_t*)ray_sys_alloc(nc); + uint8_t* visited = nc <= 256 ? visited_local : (uint8_t*)ray_alloc_raw(nc); if (!visited) { - if (stk1 != stk1_local) ray_sys_free(stk1); - if (stk2 != stk2_local) ray_sys_free(stk2); + if (stk1 != stk1_local) ray_free_raw(stk1); + if (stk2 != stk2_local) ray_free_raw(stk2); return root; } memset(visited, 0, nc); @@ -308,8 +307,8 @@ ray_op_t* ray_idiom_pass(ray_graph_t* g, ray_op_t* root) { } } - if (visited != visited_local) ray_sys_free(visited); - if (stk1 != stk1_local) ray_sys_free(stk1); - if (stk2 != stk2_local) ray_sys_free(stk2); + if (visited != visited_local) ray_free_raw(visited); + if (stk1 != stk1_local) ray_free_raw(stk1); + if (stk2 != stk2_local) ray_free_raw(stk2); return root; } diff --git a/src/ops/idxop.h b/src/ops/idxop.h index 2f1cc8361..2c9378444 100644 --- a/src/ops/idxop.h +++ b/src/ops/idxop.h @@ -184,10 +184,16 @@ typedef struct { int64_t n_distinct; } dict; struct { /* RAY_IDX_UKEY */ - /* Open addressing over the whole key tuple: slot = row + 1, - * 0 = empty. Rows are entered in row order, so a probe from - * the hash slot meets duplicates lowest-row first — the row - * the linear scan would have picked. + /* Open addressing over the whole key tuple. An occupied slot + * packs the low 32 bits of the key's hash in its high half and + * row + 1 in its low half (0 = empty, all-ones = tombstone), so + * a probe rejects a colliding slot without reading its row, and + * a map that outgrows its capacity is re-placed from the slots + * themselves instead of being rebuilt from the key columns. + * Rows are entered in row order, and the re-placement walks each + * chain from its beginning, so a probe from the hash slot meets + * duplicates lowest-row first — the row the linear scan would + * have picked. * * `nrows` is the table row count this map describes. Any * mutation that changes the row count leaves it behind and the diff --git a/src/ops/internal.h b/src/ops/internal.h index 108371e0e..60da88417 100644 --- a/src/ops/internal.h +++ b/src/ops/internal.h @@ -46,6 +46,21 @@ #include "vec/vec.h" #include #include + +/* Borrow only while the symbol table stays read-only through the dispatch + * barrier. Workers compare or copy source-domain codes without interning. + * File-domain lookup keeps its own publication/lifetime contract. */ +typedef struct { + ray_t** strings; + uint32_t count; +} ray_group_sym_view_t; +static inline ray_t* ray_group_sym_read(const ray_group_sym_view_t* view, struct ray_sym_domain_s* domain, int64_t id) { + if (domain == ray_sym_runtime_domain()) + return id >= 0 && (uint64_t)id < view->count ? view->strings[id] : NULL; + return ray_sym_domain_str(domain, id); +} + + #include #include #include @@ -789,7 +804,7 @@ extern bool ray_join_force_dup_fallback; extern bool ray_join_no_dup_fallback; extern uint64_t ray_join_dup_fallbacks; extern uint64_t ray_join_null_fallbacks; -extern bool ray_agg_engine_v2; /* route OP_GROUP through v2 agg engine; default off */ +extern bool ray_agg_engine_v2; /* route OP_GROUP through v2 agg engine; default ON (agg_engine.c) */ void ray_expr_stats_init(void); #define EXPR_MAX_REGS 16 @@ -1737,4 +1752,16 @@ static inline int64_t canon_f64_key(double v) { return k; } +/* Dispatch contiguous group slices by their row counts. */ +void ray_group_dispatch(ray_pool_fn fn, void* context, const int64_t* counts, int64_t groups); + +/* Associative winner reductions over stable source-row slices. The callback + * also merges two non-null partial winners, in original slice order. */ +typedef int64_t (*ray_group_winner_fn)(void* context, const int64_t* rows, int64_t count); +void ray_group_winners(ray_group_winner_fn fn, void* context, const int64_t* rows, + const int64_t* offsets, const int64_t* counts, int64_t groups, int64_t* winners); + +/* Gather winning group rows, retaining source domains and native types. */ +ray_t* ray_group_gather(ray_t* column, const int64_t* rows, int64_t count); + #endif /* RAY_EXEC_INTERNAL_H */ diff --git a/src/ops/opt.c b/src/ops/opt.c index a7fec81b6..1a9432e55 100644 --- a/src/ops/opt.c +++ b/src/ops/opt.c @@ -29,7 +29,6 @@ #include "opt.h" #include "idiom.h" #include "core/profile.h" -#include "mem/sys.h" #include "mem/heap.h" #include #include @@ -122,16 +121,16 @@ static void pass_type_inference(ray_graph_t* g, ray_op_t* root) { uint32_t stack_cap = nc * 2 + 64; /* extra space for high fan-out nodes */ uint32_t stack_local[256], order_local[256]; bool visited_stack[256]; - uint32_t *stack = stack_cap <= 256 ? stack_local : (uint32_t*)ray_sys_alloc(stack_cap * sizeof(uint32_t)); - uint32_t *order = nc <= 256 ? order_local : (uint32_t*)ray_sys_alloc(nc * sizeof(uint32_t)); + uint32_t *stack = stack_cap <= 256 ? stack_local : (uint32_t*)ray_alloc_raw(stack_cap * sizeof(uint32_t)); + uint32_t *order = nc <= 256 ? order_local : (uint32_t*)ray_alloc_raw(nc * sizeof(uint32_t)); bool* visited; if (nc <= 256) { visited = visited_stack; } else { - visited = (bool*)ray_sys_alloc(nc * sizeof(bool)); + visited = (bool*)ray_alloc_raw(nc * sizeof(bool)); } if (!stack || !order || !visited) { - { if (stack_cap > 256) ray_sys_free(stack); if (nc > 256) { ray_sys_free(order); ray_sys_free(visited); } } + { if (stack_cap > 256) ray_free_raw(stack); if (nc > 256) { ray_free_raw(order); ray_free_raw(visited); } } return; } memset(visited, 0, nc * sizeof(bool)); @@ -231,7 +230,7 @@ static void pass_type_inference(ray_graph_t* g, ray_op_t* root) { for (int i = oc - 1; i >= 0; i--) infer_type_for_node(g, &g->nodes[order[i]]); - { if (stack_cap > 256) ray_sys_free(stack); if (nc > 256) { ray_sys_free(order); ray_sys_free(visited); } } + { if (stack_cap > 256) ray_free_raw(stack); if (nc > 256) { ray_free_raw(order); ray_free_raw(visited); } } } /* -------------------------------------------------------------------------- @@ -261,7 +260,7 @@ static bool track_ext_node(ray_graph_t* g, ray_op_ext_t* ext) { if (g->ext_cap > UINT32_MAX / 2) return false; uint32_t new_cap = g->ext_cap == 0 ? 16 : g->ext_cap * 2; ray_op_ext_t** new_exts = - (ray_op_ext_t**)ray_sys_realloc(g->ext_nodes, new_cap * sizeof(ray_op_ext_t*)); + (ray_op_ext_t**)ray_realloc_raw(g->ext_nodes, new_cap * sizeof(ray_op_ext_t*)); if (!new_exts) return false; g->ext_nodes = new_exts; g->ext_cap = new_cap; @@ -274,14 +273,13 @@ static ray_op_ext_t* ensure_ext_node(ray_graph_t* g, uint32_t node_id) { ray_op_ext_t* ext = find_ext(g, node_id); if (ext) return ext; - ext = (ray_op_ext_t*)ray_sys_alloc(sizeof(ray_op_ext_t)); + /* Zero-filled so no field (literal, keys, agg_ins, ...) is read + uninitialised before the caller populates it. */ + ext = (ray_op_ext_t*)ray_calloc_raw(sizeof(ray_op_ext_t)); if (!ext) return NULL; - /* M1: Zero-init to prevent use of uninitialized fields (literal, - keys, agg_ins, etc.) before the caller populates them. */ - memset(ext, 0, sizeof(*ext)); ext->base.id = node_id; if (!track_ext_node(g, ext)) { - ray_sys_free(ext); + ray_free_raw(ext); return NULL; } return ext; @@ -722,16 +720,16 @@ static void pass_constant_fold(ray_graph_t* g, ray_op_t* root) { uint32_t stack_cap = nc * 2 + 64; /* extra space for high fan-out nodes */ uint32_t stack_local[256], order_local[256]; bool visited_stack[256]; - uint32_t *stack = stack_cap <= 256 ? stack_local : (uint32_t*)ray_sys_alloc(stack_cap * sizeof(uint32_t)); - uint32_t *order = nc <= 256 ? order_local : (uint32_t*)ray_sys_alloc(nc * sizeof(uint32_t)); + uint32_t *stack = stack_cap <= 256 ? stack_local : (uint32_t*)ray_alloc_raw(stack_cap * sizeof(uint32_t)); + uint32_t *order = nc <= 256 ? order_local : (uint32_t*)ray_alloc_raw(nc * sizeof(uint32_t)); bool* visited; if (nc <= 256) { visited = visited_stack; } else { - visited = (bool*)ray_sys_alloc(nc * sizeof(bool)); + visited = (bool*)ray_alloc_raw(nc * sizeof(bool)); } if (!stack || !order || !visited) { - { if (stack_cap > 256) ray_sys_free(stack); if (nc > 256) { ray_sys_free(order); ray_sys_free(visited); } } + { if (stack_cap > 256) ray_free_raw(stack); if (nc > 256) { ray_free_raw(order); ray_free_raw(visited); } } return; } memset(visited, 0, nc * sizeof(bool)); @@ -831,7 +829,7 @@ static void pass_constant_fold(ray_graph_t* g, ray_op_t* root) { for (int i = oc - 1; i >= 0; i--) fold_node(g, &g->nodes[order[i]]); - { if (stack_cap > 256) ray_sys_free(stack); if (nc > 256) { ray_sys_free(order); ray_sys_free(visited); } } + { if (stack_cap > 256) ray_free_raw(stack); if (nc > 256) { ray_free_raw(order); ray_free_raw(visited); } } } /* -------------------------------------------------------------------------- @@ -849,7 +847,7 @@ static void mark_live(ray_graph_t* g, ray_op_t* root, bool* live) { but nc*2 is a safe upper bound for the stack. */ uint32_t stack_cap = nc * 2; uint32_t stack_local[256]; - uint32_t *stack = stack_cap <= 256 ? stack_local : (uint32_t*)ray_sys_alloc(stack_cap * sizeof(uint32_t)); + uint32_t *stack = stack_cap <= 256 ? stack_local : (uint32_t*)ray_alloc_raw(stack_cap * sizeof(uint32_t)); if (!stack) return; int sp = 0; stack[sp++] = root->id; @@ -968,7 +966,7 @@ static void mark_live(ray_graph_t* g, ray_op_t* root, bool* live) { } } } - if (stack_cap > 256) ray_sys_free(stack); + if (stack_cap > 256) ray_free_raw(stack); } static void pass_dce(ray_graph_t* g, ray_op_t* root) { @@ -978,7 +976,7 @@ static void pass_dce(ray_graph_t* g, ray_op_t* root) { if (nc <= 256) { live = live_stack; } else { - live = (bool*)ray_sys_alloc(nc * sizeof(bool)); + live = (bool*)ray_alloc_raw(nc * sizeof(bool)); if (!live) return; } memset(live, 0, nc * sizeof(bool)); @@ -990,7 +988,7 @@ static void pass_dce(ray_graph_t* g, ray_op_t* root) { g->nodes[i].flags |= OP_FLAG_DEAD; } } - if (nc > 256) ray_sys_free(live); + if (nc > 256) ray_free_raw(live); } /* -------------------------------------------------------------------------- @@ -1699,7 +1697,7 @@ static ray_op_t* pass_filter_reorder(ray_graph_t* g, ray_op_t* root) { if (nc <= 256) { visited = visited_stack; } else { - visited = (bool*)ray_sys_alloc(nc * sizeof(bool)); + visited = (bool*)ray_alloc_raw(nc * sizeof(bool)); if (!visited) return &g->nodes[root_id]; } memset(visited, 0, nc * sizeof(bool)); @@ -1763,7 +1761,7 @@ static ray_op_t* pass_filter_reorder(ray_graph_t* g, ray_op_t* root) { } } - if (nc > 256) ray_sys_free(visited); + if (nc > 256) ray_free_raw(visited); return &g->nodes[root_id]; } @@ -1780,10 +1778,10 @@ static bool pass_projection_pushdown(ray_graph_t* g, ray_op_t* root) { uint32_t nc = g->node_count; bool live_stack[256]; - bool* live = nc <= 256 ? live_stack : (bool*)ray_sys_alloc(nc * sizeof(bool)); + bool* live = nc <= 256 ? live_stack : (bool*)ray_alloc_raw(nc * sizeof(bool)); uint32_t q_stack[256]; - uint32_t* q = nc <= 256 ? q_stack : (uint32_t*)ray_sys_alloc(nc * sizeof(uint32_t)); - if (!live || !q) { if (nc > 256) { ray_sys_free(live); ray_sys_free(q); } return false; } + uint32_t* q = nc <= 256 ? q_stack : (uint32_t*)ray_alloc_raw(nc * sizeof(uint32_t)); + if (!live || !q) { if (nc > 256) { ray_free_raw(live); ray_free_raw(q); } return false; } memset(live, 0, nc * sizeof(bool)); /* BFS from root */ @@ -1908,7 +1906,7 @@ static bool pass_projection_pushdown(ray_graph_t* g, ray_op_t* root) { g->nodes[i].flags |= OP_FLAG_DEAD; } - if (nc > 256) { ray_sys_free(live); ray_sys_free(q); } + if (nc > 256) { ray_free_raw(live); ray_free_raw(q); } return true; } @@ -1999,9 +1997,8 @@ static void pass_partition_pruning(ray_graph_t* g, ray_op_t* root) { /* Allocate seg_mask bitmap */ uint32_t n_words = (uint32_t)((n_parts + 63) / 64); - uint64_t* mask = (uint64_t*)ray_sys_alloc(n_words * sizeof(uint64_t)); + uint64_t* mask = (uint64_t*)ray_calloc_raw(n_words * sizeof(uint64_t)); if (!mask) continue; - memset(mask, 0, n_words * sizeof(uint64_t)); /* OP_IN / OP_NOT_IN expects a literal vector const on the RHS. * For the scalar ops, the const is a single atom or 1-elem vec. */ @@ -2010,7 +2007,7 @@ static void pass_partition_pruning(ray_graph_t* g, ray_op_t* root) { /* For IN/NOT_IN the scan must be the LHS (col IN set), not * swapped — we never pruned on `const IN col_set` anyway. */ - if ((is_in || is_nin) && swapped) { ray_sys_free(mask); continue; } + if ((is_in || is_nin) && swapped) { ray_free_raw(mask); continue; } /* Extract constant(s) for comparison. Scalar ops take one * value; IN ops take an array of values read from the vec @@ -2028,14 +2025,14 @@ static void pass_partition_pruning(ray_graph_t* g, ray_op_t* root) { bool narrow32 = (lt == RAY_I32 || lt == RAY_DATE || lt == RAY_TIME); bool wide64 = (lt == RAY_I64 || lt == RAY_TIMESTAMP || lt == RAY_SYM); if (!narrow32 && !wide64) { - ray_sys_free(mask); + ray_free_raw(mask); continue; /* unsupported type for partition pruning */ } if (is_in || is_nin) { /* Literal must be a vector (ray_const_vec carries the vec * pointer unchanged in ext->literal). */ - if (lit->type <= 0) { ray_sys_free(mask); continue; } + if (lit->type <= 0) { ray_free_raw(mask); continue; } set_len = lit->len; if (set_len <= 0) { /* Empty set: for IN no partition can match → mask stays 0 @@ -2049,7 +2046,7 @@ static void pass_partition_pruning(ray_graph_t* g, ray_op_t* root) { } if (set_len > 32) { set_heap = ray_alloc((size_t)set_len * sizeof(int64_t)); - if (!set_heap) { ray_sys_free(mask); continue; } + if (!set_heap) { ray_free_raw(mask); continue; } set_vals = (int64_t*)ray_data(set_heap); } /* Read set elements — skip nulls in the literal so a null @@ -2174,7 +2171,7 @@ static void pass_partition_pruning(ray_graph_t* g, ray_op_t* root) { mask_owned = true; } } - if (!mask_owned) ray_sys_free(mask); + if (!mask_owned) ray_free_raw(mask); n->est_rows = 1; } diff --git a/src/ops/pivot.c b/src/ops/pivot.c index 699f33f90..c02254f2f 100644 --- a/src/ops/pivot.c +++ b/src/ops/pivot.c @@ -1205,11 +1205,22 @@ ray_t* exec_pivot(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { const uint32_t pvt_null_word = n_idx >> 6; const int64_t pvt_null_bit = (int64_t)((uint64_t)1 << (n_idx & 63)); + /* Radix partitioning is an execution detail: positions are handed out + * in the order a value's or index tuple's earliest source row appears, + * so the pivot columns and the index rows come out in first-seen order + * whatever the partition or task-claim order was (group-by orders its + * output the same way). Both walks below stay linear over the + * partitions; the earliest row per position is tracked as they go and + * the positions are renumbered once at the end. */ + ray_t* pv_first_hdr = NULL; + uint32_t pv_first_cap = 64; + int64_t* pv_first = (int64_t*)scratch_alloc(&pv_first_hdr, pv_first_cap * sizeof(int64_t)); + if (!pv_first) { pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); return ray_error("oom", NULL); } /* Collect distinct pivot values */ uint32_t pv_cap = 64, pv_count = 0; ray_t* pv_hdr = NULL; int64_t* pv_vals = (int64_t*)scratch_alloc(&pv_hdr, pv_cap * sizeof(int64_t)); - if (!pv_vals) { pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); return ray_error("oom", NULL); } + if (!pv_vals) { scratch_free(pv_first_hdr); pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); return ray_error("oom", NULL); } const char* pvt_base = pvt_wide ? (const char*)key_data[n_idx] : NULL; for (uint32_t _p = 0; _p < pg.n_parts; _p++) { @@ -1220,13 +1231,15 @@ ray_t* exec_pivot(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { const int64_t* rkeys = (const int64_t*)(row + 8); if (rkeys[n_keys + pvt_null_word] & pvt_null_bit) continue; int64_t pval = rkeys[n_idx]; + int64_t gfirst; + memcpy(&gfirst, row + ly.off_group_first, 8); bool found = false; for (uint32_t p = 0; p < pv_count; p++) { if (pvt_wide) { if (memcmp(pvt_base + (size_t)pv_vals[p] * 16, - pvt_base + (size_t)pval * 16, 16) == 0) { found = true; break; } + pvt_base + (size_t)pval * 16, 16) == 0) { found = true; if (gfirst < pv_first[p]) pv_first[p] = gfirst; break; } } else { - if (pv_vals[p] == pval) { found = true; break; } + if (pv_vals[p] == pval) { found = true; if (gfirst < pv_first[p]) pv_first[p] = gfirst; break; } } } if (!found) { @@ -1234,10 +1247,13 @@ ray_t* exec_pivot(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { uint32_t new_cap = pv_cap * 2; int64_t* new_pv = (int64_t*)scratch_realloc(&pv_hdr, pv_cap * sizeof(int64_t), new_cap * sizeof(int64_t)); - if (!new_pv) { pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); return ray_error("oom", NULL); } - pv_vals = new_pv; - pv_cap = new_cap; + int64_t* new_pf = (int64_t*)scratch_realloc(&pv_first_hdr, + pv_first_cap * sizeof(int64_t), new_cap * sizeof(int64_t)); + if (!new_pv || !new_pf) { scratch_free(pv_first_hdr); pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); return ray_error("oom", NULL); } + pv_vals = new_pv; pv_first = new_pf; + pv_cap = new_cap; pv_first_cap = new_cap; } + pv_first[pv_count] = gfirst; pv_vals[pv_count++] = pval; } } @@ -1258,7 +1274,10 @@ ray_t* exec_pivot(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { ray_t* ix_hdr = NULL; size_t ix_entry = 8 + (size_t)n_idx * 8 + (size_t)null_words * 8; char* ix_rows = (char*)scratch_alloc(&ix_hdr, ix_cap * ix_entry); - if (!ix_rows) { scratch_free(pv_hdr); pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); return ray_error("oom", NULL); } + if (!ix_rows) { scratch_free(pv_first_hdr); scratch_free(pv_hdr); pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); return ray_error("oom", NULL); } + ray_t* ix_first_hdr = NULL; + int64_t* ix_first = (int64_t*)scratch_alloc(&ix_first_hdr, ix_cap * sizeof(int64_t)); + if (!ix_first) { scratch_free(ix_hdr); scratch_free(pv_first_hdr); scratch_free(pv_hdr); pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); return ray_error("oom", NULL); } /* Secondary HT: hash slot -> ix_row index; empty = UINT32_MAX. */ uint32_t ix_ht_cap = 256; @@ -1268,6 +1287,7 @@ ray_t* exec_pivot(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { if (!ix_ht) { scratch_free(ix_hdr); scratch_free(pv_hdr); pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); + scratch_free(ix_first_hdr); scratch_free(pv_first_hdr); return ray_error("oom", NULL); } memset(ix_ht, 0xFF, ix_ht_cap * sizeof(uint32_t)); @@ -1280,6 +1300,7 @@ ray_t* exec_pivot(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { scratch_free(ix_ht_hdr); scratch_free(ix_hdr); scratch_free(pv_hdr); pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); + scratch_free(ix_first_hdr); scratch_free(pv_first_hdr); return ray_error("oom", NULL); } uint32_t* grp_pv = grp_ix + grp_count; @@ -1300,6 +1321,8 @@ ray_t* exec_pivot(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { grp_pv[gi] = UINT32_MAX; continue; } + int64_t gfirst; + memcpy(&gfirst, row + ly.off_group_first, 8); /* Index-key null words: keys[n_keys .. n_keys+null_words) as-is. * The pivot key's own null bit (position n_idx, word pvt_null_word) * is guaranteed 0 here — the check just above already `continue`d @@ -1349,7 +1372,7 @@ ray_t* exec_pivot(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { const int64_t* ent_nwords = (const int64_t*)(ix_entry_p + 8 + (size_t)n_idx * 8); eq = (memcmp(ent_nwords, idx_nwords, (size_t)null_words * 8) == 0); } - if (eq) { ix_row = ent; break; } + if (eq) { ix_row = ent; if (gfirst < ix_first[ent]) ix_first[ent] = gfirst; break; } } slot = (slot + 1) & ix_ht_mask; } @@ -1362,12 +1385,23 @@ ray_t* exec_pivot(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { scratch_free(map_hdr); scratch_free(ix_ht_hdr); scratch_free(pv_hdr); pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); + scratch_free(ix_first_hdr); scratch_free(pv_first_hdr); return ray_error("oom", NULL); } + int64_t* new_first = (int64_t*)scratch_realloc(&ix_first_hdr, + ix_cap * sizeof(int64_t), new_cap * sizeof(int64_t)); + if (!new_first) { + scratch_free(map_hdr); scratch_free(ix_ht_hdr); scratch_free(ix_first_hdr); + scratch_free(ix_hdr); scratch_free(pv_first_hdr); scratch_free(pv_hdr); + pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); + return ray_error("oom", NULL); + } + ix_first = new_first; ix_rows = new_rows; ix_cap = new_cap; } ix_row = ix_count++; + ix_first[ix_row] = gfirst; char* dst = ix_rows + (size_t)ix_row * ix_entry; *(uint64_t*)dst = ih; memcpy(dst + 8, keys, (size_t)n_idx * 8); @@ -1395,6 +1429,103 @@ ray_t* exec_pivot(ray_graph_t* g, ray_op_t* op, ray_t* tbl) { } /* Pass 3: Build output table */ + /* Renumber: rank pivot values and index rows by their earliest source + * row, permute pv_vals / ix_rows into rank order, and point every group + * at its new positions. A linear scan over the row domain ranks the + * index rows when there are many of them; a merge on ids does otherwise. */ + { + /* 8-byte array first, the 4-byte ranks after it, so every array + * starts aligned whatever the counts are. */ + ray_t* rank_hdr = NULL; + int64_t* pv_tmp = (int64_t*)scratch_alloc(&rank_hdr, + ((size_t)pv_count + 1) * sizeof(int64_t) + ((size_t)pv_count + (size_t)ix_count + 1) * sizeof(uint32_t)); + uint32_t* pv_rank = pv_tmp ? (uint32_t*)(pv_tmp + pv_count + 1) : NULL; + if (!pv_rank) { + scratch_free(map_hdr); scratch_free(ix_ht_hdr); scratch_free(ix_first_hdr); + scratch_free(ix_hdr); scratch_free(pv_first_hdr); scratch_free(pv_hdr); + pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); + return ray_error("oom", NULL); + } + uint32_t* ix_rank = pv_rank + pv_count; + /* pivot values: few — insertion sort of old positions by first row */ + for (uint32_t p = 0; p < pv_count; p++) pv_rank[p] = p; + for (uint32_t i = 1; i < pv_count; i++) { + uint32_t v = pv_rank[i]; uint32_t j = i; + while (j > 0 && pv_first[pv_rank[j - 1]] > pv_first[v]) { pv_rank[j] = pv_rank[j - 1]; j--; } + pv_rank[j] = v; + } + for (uint32_t r = 0; r < pv_count; r++) pv_tmp[r] = pv_vals[pv_rank[r]]; + memcpy(pv_vals, pv_tmp, (size_t)pv_count * sizeof(int64_t)); + for (uint32_t r = 0; r < pv_count; r++) pv_tmp[pv_rank[r]] = r; /* invert: old -> new */ + for (uint32_t p = 0; p < pv_count; p++) pv_rank[p] = (uint32_t)pv_tmp[p]; + /* index rows */ + bool ranked = false; + if ((uint64_t)ix_count * 16 >= (uint64_t)nrows && nrows > 0) { + ray_t* pos_hdr = NULL; + uint32_t* pos = (uint32_t*)scratch_alloc(&pos_hdr, (size_t)nrows * sizeof(uint32_t)); + if (pos) { + memset(pos, 0xFF, (size_t)nrows * sizeof(uint32_t)); + bool ok = true; + for (uint32_t i = 0; i < ix_count && ok; i++) { + int64_t f = ix_first[i]; + if (f < 0 || f >= nrows || pos[f] != UINT32_MAX) ok = false; else pos[f] = i; + } + if (ok) { + uint32_t next = 0; + for (int64_t r = 0; r < nrows; r++) + if (pos[r] != UINT32_MAX) ix_rank[pos[r]] = next++; + ranked = (next == ix_count); + } + scratch_free(pos_hdr); + } + } + if (!ranked && ix_count > 0) { + ray_t* tmp_hdr = NULL; + uint32_t* buf = (uint32_t*)scratch_alloc(&tmp_hdr, 2 * (size_t)ix_count * sizeof(uint32_t)); + if (!buf) { + scratch_free(rank_hdr); scratch_free(map_hdr); scratch_free(ix_ht_hdr); scratch_free(ix_first_hdr); + scratch_free(ix_hdr); scratch_free(pv_first_hdr); scratch_free(pv_hdr); + pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); + return ray_error("oom", NULL); + } + uint32_t* src = buf; uint32_t* dst = buf + ix_count; + for (uint32_t i = 0; i < ix_count; i++) src[i] = i; + for (uint32_t w = 1; w < ix_count; w *= 2) { + for (uint32_t lo = 0; lo < ix_count; lo += 2 * w) { + uint32_t mid = lo + w < ix_count ? lo + w : ix_count; + uint32_t hi = lo + 2 * w < ix_count ? lo + 2 * w : ix_count; + uint32_t a = lo, b = mid, o = lo; + while (a < mid && b < hi) dst[o++] = (ix_first[src[b]] < ix_first[src[a]]) ? src[b++] : src[a++]; + while (a < mid) dst[o++] = src[a++]; + while (b < hi) dst[o++] = src[b++]; + } + uint32_t* t = src; src = dst; dst = t; + } + for (uint32_t r = 0; r < ix_count; r++) ix_rank[src[r]] = r; /* old -> new */ + scratch_free(tmp_hdr); + } + if (ix_count > 0) { + ray_t* perm_hdr = NULL; + char* perm = (char*)scratch_alloc(&perm_hdr, (size_t)ix_count * ix_entry); + if (!perm) { + scratch_free(rank_hdr); scratch_free(map_hdr); scratch_free(ix_ht_hdr); scratch_free(ix_first_hdr); + scratch_free(ix_hdr); scratch_free(pv_first_hdr); scratch_free(pv_hdr); + pivot_ingest_free(&pg); ght_layout_free(&ly); scratch_free(key_hdr); + return ray_error("oom", NULL); + } + for (uint32_t i = 0; i < ix_count; i++) + memcpy(perm + (size_t)ix_rank[i] * ix_entry, ix_rows + (size_t)i * ix_entry, ix_entry); + memcpy(ix_rows, perm, (size_t)ix_count * ix_entry); + scratch_free(perm_hdr); + } + for (uint32_t gi = 0; gi < grp_count; gi++) { + if (grp_ix[gi] != UINT32_MAX) grp_ix[gi] = ix_rank[grp_ix[gi]]; + if (grp_pv[gi] != UINT32_MAX) grp_pv[gi] = pv_rank[grp_pv[gi]]; + } + scratch_free(rank_hdr); + } + scratch_free(ix_first_hdr); + scratch_free(pv_first_hdr); ray_progress_update("pivot", "scatter", 0, (uint64_t)pv_count); bool val_is_f64 = vcol->type == RAY_F64; int8_t out_agg_type; diff --git a/src/ops/query.c b/src/ops/query.c index 81b511640..0a07c70d6 100644 --- a/src/ops/query.c +++ b/src/ops/query.c @@ -45,7 +45,6 @@ #include "table/domain.h" #include "table/dict.h" #include "mem/heap.h" -#include "mem/sys.h" #include #include @@ -302,7 +301,7 @@ static bool dag_numeric_type_admitted(int8_t t) { if (t <= 0) return true; /* unknown/uninferred: let executor validate */ if (RAY_IS_PARTED(t)) t = (int8_t)RAY_PARTED_BASETYPE(t); return t == RAY_BOOL || t == RAY_U8 || t == RAY_I16 || - t == RAY_I32 || t == RAY_I64 || t == RAY_F64; + t == RAY_I32 || t == RAY_I64 || t == RAY_F32 || t == RAY_F64; } static bool dag_pow_type_admitted(int8_t t) { @@ -2794,6 +2793,204 @@ static int collect_col_refs(ray_t* expr, ray_t* tbl, return n; } +/* A group-key expression that reads exactly ONE flat SYM column is a + * function of the symbol alone. Evaluate it once per symbol actually + * present in the column (du distinct ids, first-seen order, as a SYM + * vector adopting the column's domain) and spread the du results over the + * rows — instead of running every string op over every row, where each + * substr/str-find over SYM interns its output per row. Returns a fresh + * nrows-long key vector, or NULL when the shape does not apply (caller + * falls back to the plain evaluation). Only when the distinct count is + * well below the row count: the expression costs O(du) here, the spread + * O(nrows) plain reads. */ +/* Is `e` a pure, row-local function of the column `col_sym` (plus + * let-bound names)? Heads must come from a fixed list of elementwise + * operators — anything positional (differ, fills, deltas, moving windows), + * a user function, or a free symbol that is not the column (a global + * vector of table length) disqualifies the per-symbol evaluation. */ +static bool derived_key_head_ok(int64_t head) { + static const char* const ok[] = { + "let", "if", "cond", "and", "or", "not", + "==", "!=", "<", "<=", ">", ">=", "+", "-", "*", "/", "%", + "abs", "neg", "floor", "ceil", "round", "xbar", "as", + "substr", "str-find", "strlen", "within", "nil?", "upper", "lower", "like", + }; + ray_t* name = ray_sym_str(head); + if (!name || RAY_IS_ERR(name)) return false; + size_t n = ray_str_len(name); + const char* q = ray_str_ptr(name); + for (size_t i = 0; i < sizeof(ok) / sizeof(ok[0]); i++) + if (strlen(ok[i]) == n && memcmp(ok[i], q, n) == 0) return true; + return false; +} +static bool derived_key_expr_ok(ray_t* e, ray_t* tbl, int64_t col_sym, int64_t* bound, int nbound) { + if (!e) return false; + if (e->type == -RAY_SYM) { + /* A symbol literal — unless it names a column of the table, which + * the DAG compiler resolves to that column. */ + if (e->attrs & ATTR_QUOTED) return ray_table_get_col(tbl, e->i64) == NULL; + if (e->i64 == col_sym) return true; + for (int i = 0; i < nbound; i++) if (bound[i] == e->i64) return true; + return false; /* free name */ + } + if (e->type != RAY_LIST) return true; /* atom / vector literal */ + int64_t n = ray_len(e); + if (n < 1) return false; + ray_t** el = (ray_t**)ray_data(e); + if (el[0]->type != -RAY_SYM || (el[0]->attrs & ATTR_QUOTED) || !derived_key_head_ok(el[0]->i64)) + return false; + /* The name must still mean the builtin: the DAG compiler inlines a + * global lambda of that name ahead of the builtin, and a let-bound + * name shadows it too. */ + for (int i = 0; i < nbound; i++) if (bound[i] == el[0]->i64) return false; + { + ray_t* gv = ray_env_get(el[0]->i64); /* borrowed */ + if (gv && gv->type == RAY_LAMBDA) return false; + } + ray_t* hs = ray_sym_str(el[0]->i64); + size_t hl = ray_str_len(hs); + const char* hp = ray_str_ptr(hs); + if (hl == 3 && memcmp(hp, "let", 3) == 0) { + if (n != 4 || el[1]->type != -RAY_SYM || nbound >= 32) return false; + if (!derived_key_expr_ok(el[2], tbl, col_sym, bound, nbound)) return false; + bound[nbound] = el[1]->i64; + return derived_key_expr_ok(el[3], tbl, col_sym, bound, nbound + 1); + } + if (hl == 4 && memcmp(hp, "cond", 4) == 0) { + for (int64_t i = 1; i < n; i++) { + if (el[i]->type != RAY_LIST || ray_len(el[i]) != 2) return false; + ray_t** cp = (ray_t**)ray_data(el[i]); + bool is_else = cp[0]->type == -RAY_SYM && !(cp[0]->attrs & ATTR_QUOTED) && + ray_str_len(ray_sym_str(cp[0]->i64)) == 4 && + memcmp(ray_str_ptr(ray_sym_str(cp[0]->i64)), "else", 4) == 0; + if (!is_else && !derived_key_expr_ok(cp[0], tbl, col_sym, bound, nbound)) return false; + if (!derived_key_expr_ok(cp[1], tbl, col_sym, bound, nbound)) return false; + } + return true; + } + for (int64_t i = 1; i < n; i++) + if (!derived_key_expr_ok(el[i], tbl, col_sym, bound, nbound)) return false; + return true; +} + +/* Name for a computed group key: the last bare (unquoted) symbol of the + * form, else `key` — the rule the eval-level group path applies. */ +static int64_t derived_key_name(ray_t* by_expr) { + if (by_expr && by_expr->type == RAY_LIST && by_expr->len >= 2) { + ray_t** be = (ray_t**)ray_data(by_expr); + for (int64_t i = by_expr->len - 1; i >= 1; i--) + if (be[i]->type == -RAY_SYM && !(be[i]->attrs & ATTR_QUOTED)) return be[i]->i64; + } + return ray_sym_intern("key", 3); +} +#define DERIVED_KEY_MAX_DOMAIN (64LL * 1024 * 1024) +static ray_t* derived_key_over_sym_domain(ray_t* by_expr, ray_t* tbl) { + if (!by_expr || by_expr->type != RAY_LIST || !tbl) return NULL; + int64_t ref_syms[2]; + int nref = collect_col_refs(by_expr, tbl, ref_syms, 2, 0); + if (nref != 1) return NULL; + int64_t bound[32]; + if (!derived_key_expr_ok(by_expr, tbl, ref_syms[0], bound, 0)) return NULL; + ray_t* C = ray_table_get_col(tbl, ref_syms[0]); + int64_t nrows = ray_table_nrows(tbl); + if (!C || C->type != RAY_SYM || !ray_is_vec(C) || C->len != nrows || nrows < 4096) + return NULL; + struct ray_sym_domain_s* dom = ray_sym_vec_domain(C); + int64_t dn = dom ? ray_sym_domain_count(dom) : 0; + /* The slot table is dn ints: refuse a domain far wider than the table + * (a process-wide symbol domain behind a small in-memory column). */ + if (dn <= 0 || dn > DERIVED_KEY_MAX_DOMAIN || dn > 16 * nrows) return NULL; + + /* Probe the first block before touching anything domain-sized: a + * distinct share above 3/4 over the first 65536 rows means a + * near-unique column, for which the per-symbol evaluation cannot win. + * A 128k-slot open-addressing set on scratch — no dn-sized memory. */ + const void* cd = ray_data(C); + { + const int64_t probe_rows = nrows < 65536 ? nrows : 65536; + int64_t PROBE_SLOTS = 1024; /* power of two, load <= 1/2 */ + while (PROBE_SLOTS < 2 * probe_rows) PROBE_SLOTS <<= 1; + ray_t* set_hdr = NULL; + int64_t* set = (int64_t*)scratch_alloc(&set_hdr, (size_t)PROBE_SLOTS * sizeof(int64_t)); + if (!set) return NULL; + memset(set, 0xff, (size_t)PROBE_SLOTS * sizeof(int64_t)); + int64_t seen = 0; + for (int64_t r = 0; r < probe_rows; r++) { + int64_t id = ray_read_sym(cd, r, C->type, C->attrs); + if (id < 0 || id >= dn) { seen = probe_rows; break; } + uint64_t h = (((uint64_t)id * 0x9E3779B97F4A7C15ull) >> 32) & (uint64_t)(PROBE_SLOTS - 1); + while (set[h] >= 0 && set[h] != id) h = (h + 1) & (uint64_t)(PROBE_SLOTS - 1); + if (set[h] < 0) { set[h] = id; seen++; } + } + scratch_free(set_hdr); + if (seen * 4 > probe_rows * 3) return NULL; + } + + /* Pass 1: first-seen slot per symbol id; bail on an id outside the + * domain (a foreign domain) or once du crosses the gate. The null + * symbol is id 0, inside the domain: it gets a slot and the expression + * sees it once, exactly as the row-wise evaluation sees it per row. */ + ray_t* pos_hdr = NULL; + int32_t* pos = (int32_t*)scratch_alloc(&pos_hdr, (size_t)dn * sizeof(int32_t)); + if (!pos) return NULL; + memset(pos, 0xff, (size_t)dn * sizeof(int32_t)); + ray_t* dom_vec = ray_sym_vec_new(ray_sym_dict_width(dn), dn); + if (!dom_vec || RAY_IS_ERR(dom_vec)) { if (dom_vec) ray_error_free(dom_vec); scratch_free(pos_hdr); return NULL; } + ray_sym_vec_adopt_domain(dom_vec, C); + int64_t du = 0, du_max = nrows / 2; + if (du_max > INT32_MAX) du_max = INT32_MAX; /* slots are int32 */ + bool ok = true; + for (int64_t r = 0; r < nrows; r++) { + int64_t id = ray_read_sym(cd, r, C->type, C->attrs); + if (id < 0 || id >= dn) { ok = false; break; } + if (pos[id] < 0) { + if (du >= du_max) { ok = false; break; } + pos[id] = (int32_t)du; + write_col_i64(ray_data(dom_vec), du, id, dom_vec->type, dom_vec->attrs); + du++; + } + } + if (!ok || du == 0) { ray_release(dom_vec); scratch_free(pos_hdr); return NULL; } + dom_vec->len = du; + + /* Evaluate the expression over the du distinct symbols through the + * same DAG compiler the row-wise key would take, against a one-column + * table holding the distinct vector under the referenced name. */ + ray_t* key_dom = NULL; + ray_t* mini = ray_table_new(0); + if (mini && !RAY_IS_ERR(mini)) mini = ray_table_add_col(mini, ref_syms[0], dom_vec); + ray_release(dom_vec); + if (!mini || RAY_IS_ERR(mini)) { if (mini) ray_error_free(mini); scratch_free(pos_hdr); return NULL; } + ray_graph_t* g2 = ray_graph_new(mini); + if (g2) { + ray_op_t* kop = compile_expr_dag(g2, by_expr); + if (kop) kop = ray_optimize(g2, kop); + if (kop) key_dom = ray_execute(g2, kop); + ray_graph_free(g2); + } + ray_release(mini); + if (key_dom && !RAY_IS_ERR(key_dom) && ray_is_lazy(key_dom)) key_dom = ray_lazy_materialize(key_dom); + if (!key_dom || RAY_IS_ERR(key_dom)) { if (key_dom) ray_error_free(key_dom); scratch_free(pos_hdr); return NULL; } + if (!ray_is_vec(key_dom) || key_dom->len != du) { ray_release(key_dom); scratch_free(pos_hdr); return NULL; } + + /* Pass 2: spread by slot. */ + ray_t* ids = ray_vec_new(RAY_I64, nrows); + if (!ids || RAY_IS_ERR(ids)) { if (ids) ray_error_free(ids); ray_release(key_dom); scratch_free(pos_hdr); return NULL; } + ids->len = nrows; + int64_t* idp = (int64_t*)ray_data(ids); + for (int64_t r = 0; r < nrows; r++) + idp[r] = pos[ray_read_sym(cd, r, C->type, C->attrs)]; + scratch_free(pos_hdr); + ray_t* spread = ray_at_fn(key_dom, ids); + ray_release(ids); + ray_release(key_dom); + if (spread && !RAY_IS_ERR(spread) && ray_is_lazy(spread)) spread = ray_lazy_materialize(spread); + if (!spread || RAY_IS_ERR(spread)) { if (spread) ray_error_free(spread); return NULL; } + if (!ray_is_vec(spread) || spread->len != nrows) { ray_release(spread); return NULL; } + agg_route_note_key_domain(); + return spread; +} + /* Bind a single column-id to a slice of its column under `idx_list`. * Helper used inside the per-group hot loop (slices the table's column * via ray_at_fn, hands the slice to env_bind_local which retains, then @@ -2884,6 +3081,39 @@ static ray_t* nonagg_eval_per_group_core(ray_t* expr, ray_t* tbl, ray_t* result = NULL; /* typed vec OR list col */ int direct_typed = 0; /* non-zero → result is a typed vec */ int8_t typed_t = 0; /* atom type sentinel for the typed path */ + if (n_groups == 0) { + /* No group to walk, but the column must still exist. Probe the + * expression once over an EMPTY slice of every referenced column so + * the result keeps the type a non-empty result would have had; an + * expression that cannot evaluate on zero rows yields a LIST column, + * the same shape the per-group path falls back to. */ + ray_t* empty_idx = ray_vec_new(RAY_I64, 0); + int8_t vt = 0; + if (empty_idx && !RAY_IS_ERR(empty_idx)) { + empty_idx->len = 0; + int bound = 1; + for (int i = 0; i < n_cols && bound; i++) { + ray_t* err = bind_col_slice(col_syms[i], cols[i], empty_idx); + if (err) { ray_error_free(err); bound = 0; } + } + if (bound) { + ray_t* cell = ray_eval(expr); + if (cell && !RAY_IS_ERR(cell) && ray_is_lazy(cell)) cell = ray_lazy_materialize(cell); + if (cell && !RAY_IS_ERR(cell)) { + int8_t t = cell->type; + if (t < 0 && t != -RAY_SYM && t != -RAY_STR && t != -RAY_GUID) vt = (int8_t)(-t); + ray_release(cell); + } else if (cell) ray_error_free(cell); + } + ray_release(empty_idx); + } else if (empty_idx) ray_error_free(empty_idx); + result = vt ? ray_vec_new(vt, 0) : ray_list_new(0); + if (result && !RAY_IS_ERR(result)) result->len = 0; + g_active_query_table = _aqt; + ray_env_pop_scope(); + scratch_free(refs_hdr); + return result ? result : ray_error("oom", NULL); + } for (int64_t gi = 0; gi < n_groups; gi++) { ray_t* idx_list = feeder(gi, fstate); @@ -3202,6 +3432,32 @@ static ray_t* eval_expr_whole_column(ray_t* expr, ray_t* tbl) { return result; } +/* Zero groups still need the aggregate's column. Run the aggregate once over + * an empty vector of the source's type so the empty column keeps the type a + * non-empty result would have had; if that probe cannot run, an empty LIST + * column stands in, the shape the per-group path itself falls back to. */ +static ray_t* empty_agg_column(ray_t* fn_name, ray_t* src) { + ray_t* out = NULL; + ray_t* fn_obj = fn_name ? ray_env_get(fn_name->i64) : NULL; + if (fn_obj && fn_obj->type == RAY_UNARY && src && ray_is_vec(src) && src->type != RAY_LIST) { + ray_t* empty = ray_vec_new(src->type, 0); + if (empty && !RAY_IS_ERR(empty)) { + empty->len = 0; + ray_t* v = ((ray_unary_fn)(uintptr_t)fn_obj->i64)(empty); + if (v && !RAY_IS_ERR(v) && ray_is_lazy(v)) v = ray_lazy_materialize(v); + if (v && !RAY_IS_ERR(v)) { + int8_t t = v->type; + if (t < 0 && t != -RAY_SYM && t != -RAY_STR && t != -RAY_GUID) out = ray_vec_new((int8_t)(-t), 0); + ray_release(v); + } else if (v) ray_error_free(v); + ray_release(empty); + } else if (empty) ray_error_free(empty); + } + if (!out || RAY_IS_ERR(out)) { if (out) ray_error_free(out); out = ray_list_new(0); } + if (out && !RAY_IS_ERR(out)) out->len = 0; + return out; +} + /* Streaming-style per-group AGG body, DAG flavor. For an expression * like `(med v)` (head is RAY_FN_AGGR + RAY_UNARY, second elem is a * column ref or full-table-eval-able sub-expression), slice src per @@ -3209,6 +3465,34 @@ static ray_t* eval_expr_whole_column(ray_t* expr, ray_t* tbl) { * result into a pre-sized typed vec. Mirrors the eval-fallback's AGG * branch (`query.c:~1955`) but with the idx_buf+offsets+grp_cnt * layout the DAG path produces. */ +/* Infer an empty grouped column from the aggregate's empty-input result. + * Preserve source domains/widths when the result has the source element type. */ +static ray_t* empty_unary_group_result(ray_unary_fn fn, ray_t* source) { + if (!source) return ray_error("domain", "aggregation source missing"); + ray_t* input = ray_group_gather(source, NULL, 0); + if (!input || RAY_IS_ERR(input)) return input ? input : ray_error("oom", NULL); + ray_t* value = fn(input); + if (value && !RAY_IS_ERR(value) && ray_is_lazy(value)) value = ray_lazy_materialize(value); + if (!value || RAY_IS_ERR(value)) { + ray_release(input); + if (!value) return ray_error("oom", NULL); + /* No group invokes this aggregate. An empty-input domain/type error + * only means its result type cannot be inferred; preserve the former + * empty LIST result. Resource failures still abort the query. */ + const char* code = ray_err_code(value); + if (strcmp(code, "domain") && strcmp(code, "type")) return value; + ray_release(value); + ray_t* out = ray_list_new(0); + return out ? out : ray_error("oom", NULL); + } + ray_t* out; + if (ray_is_atom(value) && -value->type == input->type) { + out = input; input = NULL; + } else out = ray_is_atom(value) ? ray_vec_new(-value->type, 0) : ray_list_new(0); + ray_release(input); ray_release(value); + return out ? out : ray_error("oom", NULL); +} + static ray_t* aggr_unary_per_group_buf(ray_t* expr, ray_t* tbl, const int64_t* idx_buf, const int64_t* offsets, @@ -3249,6 +3533,11 @@ static ray_t* aggr_unary_per_group_buf(ray_t* expr, ray_t* tbl, } } + if (n_groups == 0) { + ray_t* out = empty_unary_group_result(uf, src); + ray_release(src); return out; + } + /* Reusable I64 idx wrapper. */ int64_t max_cnt = 0; for (int64_t gi = 0; gi < n_groups; gi++) @@ -3299,13 +3588,18 @@ static ray_t* aggr_unary_per_group_buf(ray_t* expr, ray_t* tbl, ray_release(agg_val); } - ray_release(idx_vec); ray_release(src); + ray_release(idx_vec); if (!agg_vec) { - /* No groups produced a value (all empty?) — return an empty typed - * vec sized n_groups; default to I64 for lack of a better guess. */ - agg_vec = ray_vec_new(RAY_I64, n_groups); - if (agg_vec && !RAY_IS_ERR(agg_vec)) agg_vec->len = n_groups; + /* No group produced a value. With zero groups the column must still + * carry the type a value would have had; with groups that were all + * empty keep the I64 default sized to n_groups. */ + if (n_groups == 0) agg_vec = empty_agg_column(fn_name, src); + else { + agg_vec = ray_vec_new(RAY_I64, n_groups); + if (agg_vec && !RAY_IS_ERR(agg_vec)) agg_vec->len = n_groups; + } } + ray_release(src); return agg_vec; } @@ -3600,10 +3894,8 @@ static ray_t* query_materialize_parted_col(ray_t* col) { * * Returns NULL on shape miss (caller falls through to the existing * count-distinct path); returns a result table on success. Gates: - * - single scalar K column (not SYM, no nulls) - * - cd_inner is a column ref X (not SYM, no nulls) — composite key - * fits in 16 bytes (v2's wide-key cap) - * - K + X ≤ 16 bytes packed + * - plain K and X columns with supported grouping key types + * - at most 15 K columns, leaving one component for X * - WHERE optional; if present, must be supported by the fused predicate * - desc/take optional, must be on the cd output column when present */ static ray_t* try_count_distinct_v2_rewrite( @@ -3616,16 +3908,25 @@ static ray_t* try_count_distinct_v2_rewrite( int64_t nearest_id) { if (!tbl || tbl->type != RAY_TABLE) return NULL; - /* by: accepts either a single bare column name ((by: K), single-key) - * or a {Name: Col Name: Col ...} dict (multi-key composite). In - * either case we collect the source column syms into K_syms[]. - * The output aliases for multi-key (dict keys) are looked up from - * by_expr again when the inner pass renames its output columns. */ + /* by: accepts a single bare column name ((by: K), single-key), a + * [K1 K2 ...] symbol vector (multi-key, output columns keep the source + * names), or a {Name: Col Name: Col ...} dict (multi-key composite with + * aliases). In every case we collect the source column syms into + * K_syms[]. The vector form is how a composite key is normally written + * (`by: [MobilePhone MobilePhoneModel]`); without this branch it fell + * to the general per-group path, seven times slower than the dict form + * of the same query. */ int64_t K_syms[15]; /* leave room for X in the composite */ int n_K = 0; if (by_expr && by_expr->type == -RAY_SYM && !(by_expr->attrs & ATTR_QUOTED)) { K_syms[n_K++] = by_expr->i64; + } else if (by_expr && by_expr->type == RAY_SYM && ray_is_vec(by_expr) && + !(by_expr->attrs & ATTR_QUOTED)) { + int64_t n = ray_len(by_expr); + if (n == 0 || n > 15) return NULL; + for (int64_t i = 0; i < n; i++) + K_syms[n_K++] = ray_read_sym(ray_data(by_expr), i, by_expr->type, by_expr->attrs); } else if (by_expr && by_expr->type == RAY_DICT) { DICT_VIEW_DECL(byv); DICT_VIEW_OPEN(by_expr, byv); @@ -3710,35 +4011,31 @@ static ray_t* try_count_distinct_v2_rewrite( if (asc_col_sym >= 0 && asc_col_sym != cd_c_sym) return NULL; if (desc_col_sym >= 0 && asc_col_sym >= 0) return NULL; - /* Type checks on every K column and on X. Composite must fit in - * the mk_compile 16-byte budget (sum of K storage widths + X). */ + /* General grouping retains full keys when a tuple does not fit the + * packed kernel. Its byte-width limit must not restrict this rewrite. */ ray_t* K_cols[15]; - int K_esz_total = 0; for (int j = 0; j < n_K; j++) { K_cols[j] = ray_table_get_col(tbl, K_syms[j]); if (!K_cols[j]) return NULL; int8_t kct_j = K_cols[j]->type; if (RAY_IS_PARTED(kct_j) || kct_j == RAY_MAPCOMMON) return NULL; - if (ray_vec_has_nulls(K_cols[j])) return NULL; int kct_ok_j = (kct_j == RAY_SYM || kct_j == RAY_BOOL || kct_j == RAY_U8 || kct_j == RAY_I16 || kct_j == RAY_I32 || kct_j == RAY_I64 || - kct_j == RAY_DATE || kct_j == RAY_TIME || kct_j == RAY_TIMESTAMP); + kct_j == RAY_DATE || kct_j == RAY_TIME || kct_j == RAY_TIMESTAMP || kct_j == RAY_F32 || kct_j == RAY_F64 || + kct_j == RAY_STR || kct_j == RAY_GUID || kct_j == RAY_LIST); if (!kct_ok_j) return NULL; - K_esz_total += ray_sym_elem_size(kct_j, K_cols[j]->attrs); } ray_t* X_col = ray_table_get_col(tbl, cd_X_sym); if (!X_col) return NULL; int8_t xct = X_col->type; if (RAY_IS_PARTED(xct) || xct == RAY_MAPCOMMON) return NULL; - if (ray_vec_has_nulls(X_col)) return NULL; - int X_esz = ray_sym_elem_size(xct, X_col->attrs); - if (K_esz_total + X_esz > 16) return NULL; /* X gets the same per-type acceptability check as the K columns * (validated in the loop above). SYM is allowed — mk_compile packs * it by storage width into the composite key. */ int xct_ok = (xct == RAY_SYM || xct == RAY_BOOL || xct == RAY_U8 || xct == RAY_I16 || xct == RAY_I32 || xct == RAY_I64 || - xct == RAY_DATE || xct == RAY_TIME || xct == RAY_TIMESTAMP); + xct == RAY_DATE || xct == RAY_TIME || xct == RAY_TIMESTAMP || xct == RAY_F32 || xct == RAY_F64 || + xct == RAY_STR || xct == RAY_GUID || xct == RAY_LIST); if (!xct_ok) return NULL; if (where_expr && !ray_fused_group_supported(where_expr, tbl)) @@ -3783,8 +4080,12 @@ static ray_t* try_count_distinct_v2_rewrite( { const int64_t* src = (const int64_t*)ray_data(fk); void* dst = ray_data(kv); - for (int64_t i = 0; i < ng; i++) - write_col_i64(dst, i, src[i], kv->type, kv->attrs); + for (int64_t i = 0; i < ng; i++) { + if (kv->type == RAY_F32) { uint32_t bits = (uint32_t)src[i]; memcpy((float*)dst + i, &bits, 4); } + else if (kv->type == RAY_F64) memcpy((double*)dst + i, &src[i], 8); + else write_col_i64(dst, i, src[i], kv->type, kv->attrs); + } + if (ray_vec_may_have_nulls(K_cols[0])) kv->attrs |= RAY_ATTR_HAS_NULLS; } ray_t* out = ray_table_new(2); if (out && !RAY_IS_ERR(out)) @@ -3965,6 +4266,12 @@ static ray_t* count_distinct_per_group_buf(ray_t* inner_expr, ray_t* tbl, if (!src || RAY_IS_ERR(src)) return src ? src : ray_error("oom", NULL); } + if (src->type == RAY_STR || src->type == RAY_GUID || src->type == RAY_LIST) { + ray_t* wide = agg_count_distinct_indexed(src, idx_buf, offsets, grp_cnt, n_groups); + ray_release(src); + return wide; + } + ray_t* out = ray_vec_new(RAY_I64, n_groups); if (!out || RAY_IS_ERR(out)) { ray_release(src); @@ -4020,7 +4327,7 @@ static ray_t* count_distinct_per_group_buf(ray_t* inner_expr, ray_t* tbl, .sym_cap_bound = sym_cap_bound, .oom = 0, }; - ray_pool_dispatch_n(pool, cdpg_buf_par_fn, &pctx, (uint32_t)n_groups); + ray_group_dispatch(cdpg_buf_par_fn, &pctx, grp_cnt, n_groups); if (!atomic_load_explicit(&pctx.oom, memory_order_relaxed)) { ray_release(src); return out; @@ -4425,7 +4732,7 @@ static int can_atom_broadcast(ray_t* a) { switch (vt) { case RAY_BOOL: case RAY_U8: case RAY_I16: case RAY_I32: - case RAY_I64: case RAY_F64: + case RAY_I64: case RAY_F32: case RAY_F64: case RAY_GUID: case RAY_STR: case RAY_DATE: case RAY_TIME: case RAY_TIMESTAMP: case RAY_SYM: return 1; @@ -4442,10 +4749,15 @@ static int can_atom_broadcast(ray_t* a) { * per group, scaling with output cardinality, not row count). Allocate * once and fill — Q35 falls back into parity with Q34. * - * Returns NULL for atom types not yet handled (RAY_STR, RAY_GUID, F32); + * Returns NULL for unsupported atom kinds; * caller falls back to the per-cell LIST path. */ static ray_t* atom_broadcast_vec(ray_t* a, int64_t n) { - if (!a || !ray_is_atom(a) || n <= 0) return NULL; + /* n == 0 is a legitimate request, not a failure: a grouped select that + * matched no row still owes its caller a typed, empty column for every + * literal projection. Returning NULL here made the sole call site + * (which treats NULL as allocation failure) report `oom` for an + * ordinary `select {by: k where: lit: 1 ...}`. */ + if (!a || !ray_is_atom(a) || n < 0) return NULL; int8_t vec_type = (int8_t)(-a->type); if (vec_type <= 0) return NULL; @@ -4468,6 +4780,7 @@ static ray_t* atom_broadcast_vec(ray_t* a, int64_t n) { } if (!v || RAY_IS_ERR(v)) return NULL; v->len = n; + if (n == 0) return v; /* typed and empty; nothing to broadcast into */ void* dst = ray_data(v); switch (vec_type) { @@ -4497,6 +4810,22 @@ static ray_t* atom_broadcast_vec(ray_t* a, int64_t n) { for (int64_t i = 0; i < n; i++) d[i] = val; break; } + case RAY_F32: { + float* d = dst; + for (int64_t i = 0; i < n; i++) d[i] = (float)a->f64; + break; + } + case RAY_GUID: + for (int64_t i = 0; i < n; i++) + memcpy((char*)dst + (size_t)i * 16, ray_data(a->obj), 16); + break; + case RAY_STR: + for (int64_t i = 0; i < n; i++) { + ray_t* next = ray_str_vec_set(v, i, ray_str_ptr(a), ray_str_len(a)); + if (!next || RAY_IS_ERR(next)) { ray_release(v); return NULL; } + v = next; + } + break; case RAY_F64: { double val = a->f64; double* d = (double*)dst; @@ -4529,16 +4858,11 @@ static ray_t* atom_broadcast_vec(ray_t* a, int64_t n) { } /* Propagate atom-null: an entirely-null broadcast keeps the null bit - * of every cell so `is_null` and aggregations behave the same as - * the LIST path would have. The aux memset is a bitmap-era residue - * (no vec-level consumer reads aux null bits since the sentinel - * migration); it MUST skip RAY_SYM, whose aux bytes 8-15 now carry - * the resolution-domain pointer — clobbering it would corrupt the - * header and crash the owned-ref release on free. */ + * of every cell so `is_null` and aggregations retain their semantics. + * Payloads already contain typed sentinels; aux belongs to owners/domains. */ if (RAY_ATOM_IS_NULL(a)) { v->attrs |= RAY_ATTR_HAS_NULLS; - if (vec_type != RAY_SYM) - memset(v->aux, 0xFF, 16); + } return v; } @@ -5614,6 +5938,106 @@ static int filt_compact_keep(ray_t* dict, ray_t* by_expr, ray_t* tbl, return n; } +/* Materialize pure temporal arithmetic with the unit-aware evaluator once, + * then let the normal typed GROUP pipeline reduce that column. The expression + * DAG's raw integer arithmetic remains guarded for mixed temporal units. */ +static bool temporal_materialize_pure(ray_t* expr, ray_t* tbl) { + if (!expr) return false; + if (ray_is_atom(expr)) { + if (expr->type == -RAY_SYM && !(expr->attrs & ATTR_QUOTED)) { + ray_t* col = ray_table_get_col(tbl, expr->i64); + return col && !RAY_IS_PARTED(col->type) && col->type != RAY_MAPCOMMON; + } + return true; + } + if (expr->type != RAY_LIST || expr->len != 3) return false; + ray_t** es = ray_data(expr); + if (!es[0] || es[0]->type != -RAY_SYM) return false; + ray_t* name = ray_sym_str(es[0]->i64); + if (!name || ray_str_len(name) != 1) return false; + char c = ray_str_ptr(name)[0]; + return (c == '+' || c == '-' || c == '*') && + temporal_materialize_pure(es[1], tbl) && temporal_materialize_pure(es[2], tbl); +} +static ray_t* try_temporal_group_materialize(ray_t* dict, ray_t* tbl) { + if (!dict_get(dict, "by")) return NULL; + ray_t* vals = ray_dict_vals(dict); + ray_t* keys = ray_dict_keys(dict); + ray_t* rewritten = NULL; + ray_t* extended = NULL; + uint32_t seq = 0; + ray_t* replacement = NULL; + ray_t* failure = NULL; + #define MAT_CHECK(value) do { if (!(value) || RAY_IS_ERR(value)) { failure = (value); (value) = NULL; goto oom; } } while (0) + for (int64_t i = 0; i < ray_dict_len(dict); i++) { + ray_t* expr = ray_list_get(vals, i); + if (!expr || !is_group_dag_agg_expr(expr)) continue; + ray_t** es = ray_data(expr); + uint16_t kind = resolve_agg_opcode(es[0]->i64); + int argc = agg_is_binary_agg(kind) ? 2 : 1; + replacement = NULL; + for (int arg = 1; arg <= argc; arg++) { + if (arg >= expr->len || !expr_contains_temporal_arith(es[arg], tbl) || + !temporal_materialize_pure(es[arg], tbl)) continue; + if (ray_env_push_query_scope() != RAY_OK) goto oom; + ray_t* previous = bind_all_columns(tbl); + ray_t* value = ray_eval(es[arg]); + g_active_query_table = previous; + ray_env_pop_scope(); + if (value && !RAY_IS_ERR(value) && ray_is_lazy(value)) value = ray_lazy_materialize(value); + if (!value || RAY_IS_ERR(value)) { + if (replacement) ray_release(replacement); + if (rewritten) ray_release(rewritten); + if (extended) ray_release(extended); + return value ? value : ray_error("oom", NULL); + } + if (!ray_is_vec(value) || value->len != ray_table_nrows(tbl)) { ray_release(value); continue; } + if (!extended) { extended = tbl; ray_retain(extended); } + char name[48]; int64_t sym; + do { + int len = snprintf(name, sizeof(name), "_temporal_agg_%u", seq++); + sym = ray_sym_intern(name, (size_t)len); + } while (ray_table_get_col(extended, sym)); + extended = ray_table_add_col(extended, sym, value); ray_release(value); + MAT_CHECK(extended); + if (!replacement) { + replacement = ray_list_new(expr->len); + MAT_CHECK(replacement); + for (int64_t j = 0; j < expr->len; j++) { + replacement = ray_list_append(replacement, es[j]); MAT_CHECK(replacement); + } + } + ray_t* ref = ray_sym(sym); + MAT_CHECK(ref); + replacement = ray_list_set(replacement, arg, ref); ray_release(ref); + MAT_CHECK(replacement); + } + if (replacement) { + if (!rewritten) { rewritten = dict; ray_retain(rewritten); } + int alloc = 0; + ray_t* key = collection_elem(keys, i, &alloc); + rewritten = ray_dict_upsert(rewritten, key, replacement); + if (alloc) ray_release(key); + ray_release(replacement); replacement = NULL; + MAT_CHECK(rewritten); + } + } + if (!rewritten) { if (extended) ray_release(extended); return NULL; } + ray_t* from = ray_sym(ray_sym_intern("from", 4)); + rewritten = ray_dict_upsert(rewritten, from, extended); + ray_release(from); ray_release(extended); extended = NULL; + MAT_CHECK(rewritten); + ray_t* result = ray_select(&rewritten, 1); + ray_release(rewritten); + return result; +oom: + if (replacement) ray_release(replacement); + if (rewritten) ray_release(rewritten); + if (extended) ray_release(extended); + return failure ? failure : ray_error("oom", NULL); + #undef MAT_CHECK +} + ray_t* ray_select(ray_t** args, int64_t n) { if (n < 1) return ray_error("arity", "select: expects a query dict, got %lld args", (long long)n); ray_t* dict = args[0]; @@ -5665,6 +6089,9 @@ ray_t* ray_select(ray_t** args, int64_t n) { if (RAY_IS_ERR(tbl)) return tbl; if (tbl->type != RAY_TABLE) { int8_t tbl_t = tbl->type; ray_release(tbl); return ray_error("type", "select: `from:` must evaluate to a table, got %s", ray_type_name(tbl_t)); } + ray_t* temporal_result = try_temporal_group_materialize(dict, tbl); + if (temporal_result) { ray_release(tbl); return temporal_result; } + ray_t* by_expr = dict_get(dict, "by"); ray_t* take_expr = dict_get(dict, "take"); ray_t* nearest_expr = dict_get(dict, "nearest"); @@ -5927,6 +6354,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { * a SYM vec of the ALIAS names. parted_bydict_deferred stays false on the * FLAT path so nothing there changes. */ bool parted_bydict_deferred = false; + bool computed_single_key = false; /* by: is one expression, compiled or const */ ray_t* deferred_bydict = NULL; int64_t deferred_nk = 0; int64_t dep_key_base_sym = -1; @@ -6808,7 +7236,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { /* Copy query into a fresh float[] that the DAG op borrows; freed * after ray_execute completes. */ - nearest_query_owned = (float*)ray_sys_alloc((size_t)dim * sizeof(float)); + nearest_query_owned = (float*)ray_calloc_raw((size_t)dim * sizeof(float)); if (!nearest_query_owned) { ray_release(qvec); ray_graph_free(g); ray_release(tbl); @@ -6839,12 +7267,12 @@ ray_t* ray_select(ray_t** args, int64_t n) { if (head->i64 == ann_sym_id) { ray_t* hobj = ray_eval(nlist[1]); if (!hobj || RAY_IS_ERR(hobj)) { - ray_sys_free(nearest_query_owned); + ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return hobj ? hobj : ray_error("domain", "nearest (ann): failed to evaluate HNSW handle"); } if (hobj->type != -RAY_I64 || !(hobj->attrs & RAY_ATTR_HNSW)) { - ray_release(hobj); ray_sys_free(nearest_query_owned); + ray_release(hobj); ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "nearest (ann): first arg must be an HNSW handle (from hnsw-build)"); @@ -6852,13 +7280,13 @@ ray_t* ray_select(ray_t** args, int64_t n) { ray_hnsw_t* idx = (ray_hnsw_t*)(uintptr_t)hobj->i64; if (!idx) { /* Defensive: attr set but pointer cleared — treat as invalid. */ - ray_release(hobj); ray_sys_free(nearest_query_owned); + ray_release(hobj); ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "nearest (ann): HNSW handle has been freed"); } if (idx->dim != dim) { - ray_release(hobj); ray_sys_free(nearest_query_owned); + ray_release(hobj); ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("length", "nearest (ann): query dim does not match index dim"); @@ -6867,7 +7295,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { if (nlen >= 4) { ray_t* ev = ray_eval(nlist[3]); if (!ev || RAY_IS_ERR(ev)) { - ray_release(hobj); ray_sys_free(nearest_query_owned); + ray_release(hobj); ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ev ? ev : ray_error("domain", "nearest (ann): ef expression failed to evaluate"); @@ -6876,7 +7304,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { else if (ev->type == -RAY_I32) ef = ev->i32; else { ray_release(ev); ray_release(hobj); - ray_sys_free(nearest_query_owned); + ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "nearest (ann): ef must be an integer atom"); @@ -6892,7 +7320,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { } else if (head->i64 == knn_sym_id) { ray_t* col_expr = nlist[1]; if (col_expr->type != -RAY_SYM) { - ray_sys_free(nearest_query_owned); + ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "nearest (knn): first arg must be an unquoted column name"); @@ -6907,7 +7335,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { else if (mid == ray_sym_find("ip", 2)) metric = RAY_HNSW_IP; else if (mid == ray_sym_find("cosine", 6)) metric = RAY_HNSW_COSINE; else { - ray_sys_free(nearest_query_owned); + ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "nearest (knn): metric must be 'cosine, 'l2, or 'ip"); @@ -6916,14 +7344,14 @@ ray_t* ray_select(ray_t** args, int64_t n) { } root = ray_knn_rerank(g, root, col_sym, nearest_query_owned, dim, k_req, metric); } else { - ray_sys_free(nearest_query_owned); + ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "nearest: expected `ann` or `knn` as the first element"); } if (!root) { if (nearest_handle_owned) ray_release(nearest_handle_owned); - ray_sys_free(nearest_query_owned); + ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); } @@ -6941,18 +7369,18 @@ ray_t* ray_select(ray_t** args, int64_t n) { int64_t src_ncols = ray_table_ncols(tbl); if (src_ncols > 255) { if (nearest_handle_owned) ray_release(nearest_handle_owned); - ray_sys_free(nearest_query_owned); + ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("limit", "nearest: implicit projection exceeds 255 source columns — " "specify output columns explicitly"); } if (src_ncols > 0) { - ray_op_t** col_ops = (ray_op_t**)ray_sys_alloc( + ray_op_t** col_ops = (ray_op_t**)ray_calloc_raw( (size_t)src_ncols * sizeof(ray_op_t*)); if (!col_ops) { if (nearest_handle_owned) ray_release(nearest_handle_owned); - ray_sys_free(nearest_query_owned); + ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); } @@ -6967,17 +7395,17 @@ ray_t* ray_select(ray_t** args, int64_t n) { col_ops[nc++] = scan_op; } if (scan_err) { - ray_sys_free(col_ops); + ray_free_raw(col_ops); if (nearest_handle_owned) ray_release(nearest_handle_owned); - ray_sys_free(nearest_query_owned); + ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); } root = ray_select_op(g, root, col_ops, nc); - ray_sys_free(col_ops); + ray_free_raw(col_ops); if (!root) { if (nearest_handle_owned) ray_release(nearest_handle_owned); - ray_sys_free(nearest_query_owned); + ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); } @@ -7044,9 +7472,9 @@ ray_t* ray_select(ray_t** args, int64_t n) { } } - /* Decide routing. LIST/STR always fall to the eval-level - * grouping because the DAG HT path can't pack them into - * 8-byte key slots. GUID is packed via row-indirection in + /* LIST keys with plain aggregates use v2 structural indexed grouping. + * Eval retains dynamic projections and partitioned LIST shapes. + * GUID is packed via row-indirection in * the HT layout (wide_key_mask), so it uses the parallel DAG * path *except* for queries with non-aggregate expressions * (the non-agg scatter still requires 8-byte-packable key @@ -7057,7 +7485,8 @@ ray_t* ray_select(ray_t** args, int64_t n) { if (key_col) { int8_t kct = key_col->type; if (RAY_IS_PARTED(kct)) kct = (int8_t)RAY_PARTED_BASETYPE(kct); - if (kct == RAY_LIST) + if (kct == RAY_LIST && (any_nonagg || n_out == 0 || + RAY_IS_PARTED(key_col->type) || !ray_agg_engine_v2)) use_eval_group = 1; else if (kct == RAY_GUID && (any_nonagg || n_out == 0)) /* RAY_GUID routes to eval-level ray_group_indices_fn only @@ -7090,9 +7519,10 @@ ray_t* ray_select(ray_t** args, int64_t n) { /* STR keys (dict-encoded OR plain) take the parallel DAG path: * dict-STR substitutes int32 codes; plain/computed STR groups * via the wide-key HT (row-indirected key slots). Both beat the - * O(N*ngroups) eval toy-grouper (33% allocator churn). Only - * RAY_LIST composite keys still need eval's structural compare. */ - if (kct == RAY_LIST) { + * O(N*ngroups) eval toy-grouper (33% allocator churn). LIST + * composites with plain aggregates use v2 structural grouping. */ + if (kct == RAY_LIST && (any_nonagg || n_out == 0 || + RAY_IS_PARTED(key_col->type) || !ray_agg_engine_v2)) { use_eval_group = 1; break; } @@ -7709,6 +8139,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { ray_release(agg_val); } } + if (!agg_vec && out_groups == 0) agg_vec = empty_agg_column(agg_fn_name, src_col_val); ray_release(src_col_val); agg_names[n_agg_out] = kid; agg_results[n_agg_out] = agg_vec; @@ -8088,7 +8519,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { /* Key column first */ { ray_t* sc = ray_table_get_col(eval_tbl, by_key_sym); if (sc) { - ray_t* ev = ray_vec_new(sc->type, 0); + ray_t* ev = ray_group_gather(sc, NULL, 0); if (ev && !RAY_IS_ERR(ev)) { empty = ray_table_add_col(empty, by_key_sym, ev); ray_release(ev); } } } @@ -8181,12 +8612,27 @@ ray_t* ray_select(ray_t** args, int64_t n) { /* For each group, compute aggregation */ ray_t* agg_vec = NULL; ray_t** grp_items = (ray_t**)ray_data(groups); + if (n_groups == 0) { + ray_t* fn_obj = ray_env_get(agg_fn_name->i64); + agg_vec = fn_obj && fn_obj->type == RAY_UNARY + ? empty_unary_group_result((ray_unary_fn)(uintptr_t)fn_obj->i64, src_col_val) + : ray_error("type", "aggregate must be a unary function"); + if (!agg_vec || RAY_IS_ERR(agg_vec)) { + ray_release(src_col_val); + for (int ai = 0; ai < n_agg_out; ai++) ray_release(agg_results[ai]); + scratch_free(aggnames_hdr); scratch_free(aggres_hdr); + ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl); + scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); + return agg_vec ? agg_vec : ray_error("oom", NULL); + } + } + /* Median fast path — flatten `groups` into * (idx_buf, offsets, grp_cnt) then call the parallel * ray_median_per_group_buf kernel. See twin site * above for the design rationale. */ - if (is_med_call(val_expr_item)) { + if (n_groups > 0 && is_med_call(val_expr_item)) { ray_t* ix_hdr = NULL; ray_t* off_hdr = NULL; ray_t* cnt_hdr = NULL; @@ -8251,6 +8697,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { store_typed_elem(agg_vec, gi, agg_val); ray_release(agg_val); } + if (!agg_vec && n_groups == 0) agg_vec = empty_agg_column(agg_fn_name, src_col_val); ray_release(src_col_val); agg_names[n_agg_out] = kid; agg_results[n_agg_out] = agg_vec; @@ -8386,6 +8833,10 @@ ray_t* ray_select(ray_t** args, int64_t n) { size_t slen = ray_str_len(k); key_vec = ray_str_vec_append(key_vec, sp ? sp : "", sp ? slen : 0); } + } else if (ktype == RAY_LIST) { + key_vec = ray_list_new(n_groups); + for (int64_t gi = 0; gi < n_groups && key_vec && !RAY_IS_ERR(key_vec); gi++) + key_vec = ray_list_append(key_vec, grp_items[gi * 2]); } else { uint8_t kattrs = key_col_src ? key_col_src->attrs : 0; if (ktype == RAY_SYM) @@ -8774,8 +9225,19 @@ ray_t* ray_select(ray_t** args, int64_t n) { n_keys++; } } else { - /* Single key expression */ - key_ops[0] = compile_expr_dag(g, by_expr); + /* Single key expression. Over a lone SYM column evaluate it per + * distinct symbol and feed the spread key as a constant node, + * named the way the eval-level path names a computed key. */ + ray_t* dom_key = derived_key_over_sym_domain(by_expr, tbl); + if (dom_key) { + key_ops[0] = ray_const_vec(g, dom_key); + ray_release(dom_key); + } else { + key_ops[0] = compile_expr_dag(g, by_expr); + } + /* Only a real expression is renamed: a bare column symbol lands + * here too and keeps its own name. */ + computed_single_key = (key_ops[0] != NULL && by_expr->type == RAY_LIST); if (!key_ops[0]) { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "select by: failed to compile group key expression"); } n_keys = 1; } @@ -9051,9 +9513,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { if (key_sym >= 0) { ray_t* sc = ray_table_get_col(filtered_tbl, key_sym); if (sc) { - empty_key_vec = (sc->type == RAY_STR) - ? ray_vec_new(RAY_STR, 0) - : ray_vec_new(sc->type, 0); + empty_key_vec = ray_group_gather(sc, NULL, 0); } } else { /* Match the computed-key fallback's naming @@ -9144,7 +9604,8 @@ ray_t* ray_select(ray_t** args, int64_t n) { ray_t* cv = ray_table_get_col_idx(filtered_tbl, c); ray_env_set_query_local(cn, cv); } - ray_t* computed_key = ray_eval(by_expr); + ray_t* computed_key = derived_key_over_sym_domain(by_expr, filtered_tbl); + if (!computed_key) computed_key = ray_eval(by_expr); ray_env_pop_scope(); if (!computed_key || RAY_IS_ERR(computed_key)) { if (filtered_tbl != tbl) ray_release(filtered_tbl); @@ -9756,7 +10217,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { fres = ray_lazy_materialize(fres); if (!fres || RAY_IS_ERR(fres)) { if (nearest_handle_owned) ray_release(nearest_handle_owned); - if (nearest_query_owned) ray_sys_free(nearest_query_owned); + if (nearest_query_owned) ray_free_raw(nearest_query_owned); ray_release(tbl); scratch_free(colops_hdr); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return fres ? fres : ray_error("domain", "select: `where:` filter produced no result"); @@ -9766,7 +10227,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { g = ray_graph_new(tbl); if (!g) { if (nearest_handle_owned) ray_release(nearest_handle_owned); - if (nearest_query_owned) ray_sys_free(nearest_query_owned); + if (nearest_query_owned) ray_free_raw(nearest_query_owned); ray_release(tbl); scratch_free(colops_hdr); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); @@ -9775,7 +10236,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { ray_t* result = ray_table_new(0); if (!result || RAY_IS_ERR(result)) { if (nearest_handle_owned) ray_release(nearest_handle_owned); - if (nearest_query_owned) ray_sys_free(nearest_query_owned); + if (nearest_query_owned) ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(colops_hdr); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result ? result : ray_error("oom", NULL); @@ -9797,7 +10258,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { ray_t* err = col ? col : ray_error("domain", "select: failed to evaluate output column expression"); ray_release(result); if (nearest_handle_owned) ray_release(nearest_handle_owned); - if (nearest_query_owned) ray_sys_free(nearest_query_owned); + if (nearest_query_owned) ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(colops_hdr); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return err; @@ -9812,7 +10273,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { ray_release(col); ray_release(result); if (nearest_handle_owned) ray_release(nearest_handle_owned); - if (nearest_query_owned) ray_sys_free(nearest_query_owned); + if (nearest_query_owned) ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(colops_hdr); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("length", "select: output column length %lld does not match %lld", (long long)col_len, (long long)out_len); @@ -9821,14 +10282,14 @@ ray_t* ray_select(ray_t** args, int64_t n) { ray_release(col); if (RAY_IS_ERR(result)) { if (nearest_handle_owned) ray_release(nearest_handle_owned); - if (nearest_query_owned) ray_sys_free(nearest_query_owned); + if (nearest_query_owned) ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); scratch_free(colops_hdr); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result; } } if (nearest_handle_owned) ray_release(nearest_handle_owned); - if (nearest_query_owned) ray_sys_free(nearest_query_owned); + if (nearest_query_owned) ray_free_raw(nearest_query_owned); ray_graph_free(g); ray_release(tbl); result = apply_sort_take(result, dict_elems, dict_n, asc_id, desc_id, take_id, NULL); @@ -9985,6 +10446,20 @@ ray_t* ray_select(ray_t** args, int64_t n) { /* Optimize and execute */ root = ray_optimize(g, root); ray_t* result = ray_execute(g, root); + /* A computed key takes its column name from its op's ext sym, which is + * not a name (a const node's slot holds the literal; an expression node's + * ext resolves to whatever shares its id) — name it the way the + * eval-level path names a computed key, whichever key path ran. */ + if (computed_single_key && result && !RAY_IS_ERR(result) && ray_is_lazy(result)) + result = ray_lazy_materialize(result); + if (computed_single_key && result && !RAY_IS_ERR(result) && result->type == RAY_TABLE && + ray_table_ncols(result) > 0) { + int64_t kname = derived_key_name(by_expr); + int64_t rc = ray_table_ncols(result); + for (int64_t c = 1; c < rc; c++) + if (ray_table_col_name(result, c) == kname) { kname = ray_sym_intern("key", 3); break; } + ray_table_set_col_name(result, 0, kname); + } if (self_emit_set) ray_group_emit_filter_set(prev_self_emit); if (post_group_where_expr && result && !RAY_IS_ERR(result)) @@ -9993,7 +10468,7 @@ ray_t* ray_select(ray_t** args, int64_t n) { ray_graph_free(g); /* The nearest-query buffer was only referenced by ext->rerank.query_vec * and is safe to free once the graph (and thus the op ext) is gone. */ - if (nearest_query_owned) ray_sys_free(nearest_query_owned); + if (nearest_query_owned) ray_free_raw(nearest_query_owned); /* The HNSW handle was kept alive through ray_execute so the rerank * ext's idx pointer stayed valid. Safe to release now that the * graph (and its ext nodes) has been freed. */ @@ -10141,6 +10616,16 @@ ray_t* ray_select(ray_t** args, int64_t n) { * Non-agg/fallback columns are named at scatter time. */ for (int64_t j = 0; j < n_aggs_real && n_key_cols + j < ncols; j++) ray_table_set_col_name(result, n_key_cols + j, agg_names[j]); + /* The computed key's derived name yields to an output alias + * that spells the same. */ + if (computed_single_key && ncols > 1) { + int64_t kname = ray_table_col_name(result, 0); + for (int64_t c = 1; c < ncols; c++) + if (ray_table_col_name(result, c) == kname) { + ray_table_set_col_name(result, 0, ray_sym_intern("key", 3)); + break; + } + } } else { /* Projection-only: columns are in dict order. Rename each * output column directly — a projection may have any number @@ -10756,6 +11241,14 @@ ray_t* ray_select(ray_t** args, int64_t n) { cd_inner->type == -RAY_SYM && !(cd_inner->attrs & ATTR_QUOTED) && n_groups > 50000); + if (simple_cd_global) { + ray_t* source = ray_table_get_col(tbl, cd_inner->i64); + int8_t type = source ? source->type : 0; + simple_cd_global = type == RAY_BOOL || type == RAY_U8 || + type == RAY_I16 || type == RAY_I32 || type == RAY_I64 || + type == RAY_F32 || type == RAY_F64 || type == RAY_DATE || + type == RAY_TIME || type == RAY_TIMESTAMP || type == RAY_SYM; + } if (!simple_cd_global) needs_slice_idx = 1; } @@ -11105,11 +11598,13 @@ ray_t* ray_select(ray_t** args, int64_t n) { } #undef RELEASE_SCAN_KEY } else { - /* Empty group set: add empty LIST columns so the - * output schema still includes the user-declared - * non-agg columns. */ + /* Empty grouped aggregates retain their result type; + * ordinary row expressions produce per-group LIST cells. */ for (int64_t ni = 0; ni < n_nonaggs; ni++) { - ray_t* empty_list = ray_list_new(0); + ray_t* expr = nonagg_exprs[ni]; + ray_t* empty_list = match_count_distinct(expr) ? ray_vec_new(RAY_I64, 0) + : is_streaming_aggr_unary_call(expr) ? aggr_unary_per_group_buf(expr, tbl, NULL, NULL, NULL, 0) + : can_atom_broadcast(expr) ? atom_broadcast_vec(expr, 0) : ray_list_new(0); if (!empty_list || RAY_IS_ERR(empty_list)) { ray_release(result); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return empty_list ? empty_list : ray_error("oom", NULL); @@ -14751,7 +15246,7 @@ static int64_t upsert_atom_int(ray_t* a) { return a->type == -RAY_BOOL ? (int64_t)a->b8 : elem_as_i64(a); } -static uint64_t upsert_hash_cell(ray_t* col, int64_t r) { +static inline __attribute__((always_inline)) uint64_t upsert_hash_cell(ray_t* col, int64_t r) { switch (col->type) { case RAY_F64: return ray_hash_f64(((const double*)ray_data(col))[r]); case RAY_SYM: return ray_hash_i64(ray_read_sym(ray_data(col), r, col->type, col->attrs)); @@ -14842,6 +15337,20 @@ typedef struct { #define UKEY_SLOT_EMPTY ((int64_t)0) #define UKEY_SLOT_TOMB ((int64_t)-1) +/* An occupied slot carries the key's hash beside the row: the low 32 bits of + * the hash in the high half, row + 1 in the low half. The hash makes two + * things cheap that used to read the table: a probe rejects a colliding slot + * without touching its key cells, and growing the map re-places every entry + * from the slots themselves (ukey_grow) instead of re-hashing every row. + * Both markers stay reserved: row + 1 is never 0 and never all-ones. */ +#define UKEY_PACK(h, row) ((int64_t)(((uint64_t)(uint32_t)(h) << 32) | (uint64_t)((row) + 1))) +#define UKEY_ROW(e) ((int64_t)((uint64_t)(e) & 0xFFFFFFFFULL) - 1) +#define UKEY_H32(e) ((uint32_t)((uint64_t)(e) >> 32)) +/* Rows must fit the low half (the two markers take the ends), and the slot + * index must come from the stored 32 bits, so the table stops at 2^32 slots. */ +#define UKEY_MAX_ROWS ((int64_t)0xFFFFFFFDLL) +#define UKEY_MAX_CAP ((uint64_t)1 << 32) + static bool upsert_map_init(upsert_map_t* mp, int64_t entries) { uint64_t cap = 16; while (cap < (uint64_t)entries * 2) cap <<= 1; @@ -14858,7 +15367,7 @@ static bool upsert_map_init(upsert_map_t* mp, int64_t entries) { static void upsert_map_put(upsert_map_t* mp, uint64_t h, int64_t row) { uint64_t s = h & mp->mask; while (mp->slot[s] != UKEY_SLOT_EMPTY) s = (s + 1) & mp->mask; - mp->slot[s] = row + 1; + mp->slot[s] = UKEY_PACK(h, row); } /* Rows nrows0..n-1 were just appended in row order. Entering them costs @@ -14891,24 +15400,74 @@ static void ukey_extend_after_append(ray_t* tbl, int64_t nrows0) { * `m` more rows at the load factor it was sized for? A row count that moved * without the map means something else wrote the table; a different key * means a different upsert. */ -static bool ukey_fits(ray_index_t* ix, const int64_t* kci, int64_t nk, - int64_t nrows0, int64_t m) { +static bool ukey_describes(ray_index_t* ix, const int64_t* kci, int64_t nk, + int64_t nrows0) { if (ix->u.ukey.nrows != nrows0 || ix->u.ukey.nk != nk) return false; for (int64_t k = 0; k < nk; k++) if (ix->u.ukey.kci[k] != (int16_t)kci[k]) return false; - /* Tombstones hold slots without being rows, so the load test counts them - * alongside the live entries. */ + return true; +} + +/* Tombstones hold slots without being rows, so the load test counts them + * alongside the live entries. */ +static bool ukey_has_room(ray_index_t* ix, int64_t nrows0, int64_t m) { return (uint64_t)(nrows0 + m + ix->u.ukey.n_tomb) * 2 <= ix->u.ukey.mask + 1; } +/* Grow a map that still describes the table but has run out of room, the way + * sym.c's ht_grow_to grows the intern table: every slot already carries its + * key's hash, so the bigger table is filled from the old slots — no key column + * is read and no key is hashed again. Tombstones vanish on the way, which is + * what makes this worth doing after a delete too. On failure the caller keeps + * the old map and falls back to dropping it. */ +static bool ukey_grow(ray_index_t* ix, int64_t entries) { + uint64_t old_cap = ix->u.ukey.mask + 1; + uint64_t cap = 16; + while (cap < (uint64_t)entries * 2) { + if (cap >= UKEY_MAX_CAP) return false; + cap <<= 1; + } + if (cap < old_cap) cap = old_cap; + if (cap > UKEY_MAX_CAP) return false; + ray_t* fresh = ray_vec_new(RAY_I64, (int64_t)cap); + if (!fresh || RAY_IS_ERR(fresh)) { if (fresh) ray_release(fresh); return false; } + fresh->len = (int64_t)cap; + int64_t* dst = (int64_t*)ray_data(fresh); + memset(dst, 0, (size_t)cap * sizeof(int64_t)); + const int64_t* src = (const int64_t*)ray_data(ix->u.ukey.slots); + uint64_t mask = cap - 1; + /* Start at an empty slot and walk the old table cyclically, rather than + * from slot 0: entries that share a probe chain must be re-placed in the + * order they sit in it, or two rows carrying the same key swap places + * whenever their chain wraps past the end of the array — and the next + * keyed upsert then updates the later row instead of the lowest one. A + * chain never contains an empty slot, so starting at one enters every + * chain at its beginning; the load factor of at most a half guarantees + * such a slot exists. */ + uint64_t start = 0; + while (start < old_cap && src[start] != UKEY_SLOT_EMPTY) start++; + for (uint64_t i = 0; i < old_cap; i++) { + int64_t e = src[(start + i) & (old_cap - 1)]; + if (e == UKEY_SLOT_EMPTY || e == UKEY_SLOT_TOMB) continue; + uint64_t t = (uint64_t)UKEY_H32(e) & mask; + while (dst[t] != UKEY_SLOT_EMPTY) t = (t + 1) & mask; + dst[t] = e; + } + ray_release(ix->u.ukey.slots); + ix->u.ukey.slots = fresh; + ix->u.ukey.mask = mask; + ix->u.ukey.n_tomb = 0; + return true; +} + static int64_t upsert_map_find(upsert_map_t* mp, uint64_t h, ray_t** slots, const int64_t* kci, int64_t nk, ray_t** cells) { uint64_t s = h & mp->mask; for (;;) { int64_t e = mp->slot[s]; if (e == UKEY_SLOT_EMPTY) return -1; - if (e != UKEY_SLOT_TOMB && - upsert_row_eq_atoms(slots, kci, nk, e - 1, cells)) return e - 1; + if (e != UKEY_SLOT_TOMB && UKEY_H32(e) == (uint32_t)h && + upsert_row_eq_atoms(slots, kci, nk, UKEY_ROW(e), cells)) return UKEY_ROW(e); s = (s + 1) & mp->mask; } } @@ -15046,6 +15605,302 @@ static void upsert_rollback_appends(ray_t** slots, int64_t ncols, int64_t nrows0 } } +/* Can a multi-row payload be written column by column, without boxing a + * cell? Every target column needs a payload vector of its exact type and + * width class: no coercion, no partial payload, no boxed or STR column, + * and SYM ids already in the runtime domain the target stores. */ +static bool upsert_vec_payload_ok(ray_t* tbl, ray_t** src_cols, int64_t ncols, int64_t m) { + for (int64_t c = 0; c < ncols; c++) { + ray_t* sc = src_cols[c]; + ray_t* tc = ray_table_get_col_idx(tbl, c); + if (!sc || RAY_IS_ERR(sc) || sc->type != tc->type) return false; + if (sc->attrs & RAY_ATTR_SLICE) return false; + if (sc->len != m) return false; + switch (tc->type) { + case RAY_BOOL: case RAY_U8: case RAY_I16: case RAY_I32: case RAY_I64: + case RAY_F64: case RAY_DATE: case RAY_TIME: case RAY_TIMESTAMP: + case RAY_GUID: + break; + case RAY_SYM: + if (ray_sym_vec_domain(sc) != ray_sym_runtime_domain()) return false; + break; + default: + return false; + } + } + return true; +} + +/* Cell-vs-cell equality for two columns of one type, with the semantics of + * upsert_cell_eq_atom: F64 by value, SYM by runtime id, integers widened. */ +static inline __attribute__((always_inline)) bool +upsert_cell_eq_cell(ray_t* a, int64_t ra, ray_t* b, int64_t rb) { + switch (a->type) { + case RAY_F64: + return ((const double*)ray_data(a))[ra] == ((const double*)ray_data(b))[rb]; + case RAY_SYM: + return ray_read_sym(ray_data(a), ra, RAY_SYM, a->attrs) == + ray_read_sym(ray_data(b), rb, RAY_SYM, b->attrs); + case RAY_GUID: + return memcmp((const uint8_t*)ray_data(a) + ra * 16, + (const uint8_t*)ray_data(b) + rb * 16, 16) == 0; + default: + return upsert_int_cell(a, ra) == upsert_int_cell(b, rb); + } +} + +/* Map probe for payload row `r`. An entry past nrows0 names a row this + * batch appends but has not written yet, so its key is read from the + * payload row that will land there (app[row - nrows0]). */ +/* Per-row probe on the column-wise path. Forced inline, with the hash and the + * cell compare below it: once the resolve loop exists in two instances GCC's + * inline budget leaves these as calls, and a call per row cost 10-20% on + * tables that fit in cache. */ +static inline __attribute__((always_inline)) int64_t +upsert_map_find_src(upsert_map_t* mp, uint64_t h, ray_t** slots, + const int64_t* kci, int64_t nk, ray_t** src, + int64_t r, int64_t nrows0, const int64_t* app) { + uint64_t s = h & mp->mask; + for (;;) { + int64_t e = mp->slot[s]; + if (e == UKEY_SLOT_EMPTY) return -1; + if (e != UKEY_SLOT_TOMB && UKEY_H32(e) == (uint32_t)h) { + int64_t row = UKEY_ROW(e); + bool eq = true; + for (int64_t k = 0; k < nk && eq; k++) { + ray_t* sc = src[kci[k]]; + eq = row < nrows0 + ? upsert_cell_eq_cell(slots[kci[k]], row, sc, r) + : upsert_cell_eq_cell(sc, app[row - nrows0], sc, r); + } + if (eq) return row; + } + s = (s + 1) & mp->mask; + } +} + +/* Move `n` elements of `src` into `dst`: dst[di(i)] = src[si(i)], where a + * NULL index array means the identity. Null state travels in-band as the + * sentinel; the HAS_NULLS gate is raised when a written value is null. */ +/* Past this much memory a keyed upsert's probes and scattered writes miss the + * per-core cache, and loading them ahead pays; below it the extra pass and + * the prefetches are pure overhead. Measured with a 512 KiB L2: a 640 KiB + * probe set was 3-8% slower with them, an 890 KiB one 19% faster. */ +#define UPSERT_PF_MIN_BYTES ((size_t)768 << 10) + +static inline __attribute__((always_inline)) void +upsert_vec_move_impl(ray_t* dst, const int64_t* di, int64_t dbase, + ray_t* src, const int64_t* si, int64_t n, bool pf) { + void* d = ray_data(dst); + const void* s = ray_data(src); + #define UPSERT_MOVE(T) do { \ + for (int64_t i = 0; i < n; i++) { \ + if (pf && i + 16 < n && di[i + 16] >= 0) \ + __builtin_prefetch(&((T*)d)[di[i + 16]], 1); \ + int64_t o = di ? di[i] : dbase + i; \ + if (o < 0) continue; \ + ((T*)d)[o] = ((const T*)s)[si ? si[i] : i]; \ + } } while (0) + switch (dst->type) { + case RAY_BOOL: case RAY_U8: UPSERT_MOVE(uint8_t); break; + case RAY_I16: UPSERT_MOVE(int16_t); break; + case RAY_I32: case RAY_DATE: case RAY_TIME: UPSERT_MOVE(int32_t); break; + case RAY_I64: case RAY_TIMESTAMP: UPSERT_MOVE(int64_t); break; + case RAY_F64: UPSERT_MOVE(double); break; + case RAY_GUID: + for (int64_t i = 0; i < n; i++) { + int64_t o = di ? di[i] : dbase + i; + if (o < 0) continue; + memcpy((uint8_t*)d + o * 16, (const uint8_t*)s + (si ? si[i] : i) * 16, 16); + } + break; + case RAY_SYM: { + bool any_null = false; + for (int64_t i = 0; i < n; i++) { + int64_t o = di ? di[i] : dbase + i; + if (o < 0) continue; + int64_t id = ray_read_sym(s, si ? si[i] : i, RAY_SYM, src->attrs); + ((int64_t*)d)[o] = id; + any_null |= (id == 0); + } + if (any_null) dst->attrs |= RAY_ATTR_HAS_NULLS; + return; + } + default: return; + } + #undef UPSERT_MOVE + if (!ray_vec_may_have_nulls(src)) return; + for (int64_t i = 0; i < n; i++) { + int64_t o = di ? di[i] : dbase + i; + if (o >= 0 && ray_vec_is_null(src, si ? si[i] : i)) { + dst->attrs |= RAY_ATTR_HAS_NULLS; + return; + } + } +} + +/* Each look-ahead loop exists twice, with its flag a compile-time constant, + * and the choice is made once per batch or column. A runtime flag tested + * inside the loops cost 15-30% on tables that fit in cache, more than the + * prefetching itself: the plain instances must compile to the loops that + * never had it. */ +static void upsert_vec_move(ray_t* dst, const int64_t* di, int64_t dbase, + ray_t* src, const int64_t* si, int64_t n) { + upsert_vec_move_impl(dst, di, dbase, src, si, n, false); +} + +static void upsert_vec_scatter_ahead(ray_t* dst, const int64_t* di, ray_t* src, int64_t n) { + upsert_vec_move_impl(dst, di, 0, src, NULL, n, true); +} + +/* Resolve every payload row to its target row, in payload order: dst[r] is + * the matched row or -1 for a row this batch appends, app[] lists the payload + * rows to append. A new key enters the map at the row it will take, so a + * repeat inside the batch finds it. + * + * On a table larger than the cache nearly all of a probe's time is two + * misses — the slot, then the key cells of the row it names — and taken one + * row at a time they never overlap. The look-ahead instance hashes the batch + * first (dst[r] holds row r's hash until this pass overwrites it) and loads + * both a few rows ahead. The separate hash pass pays on such a table even for + * a batch shorter than the lookahead, where no prefetch fires. */ +static inline __attribute__((always_inline)) void +upsert_resolve_impl(upsert_map_t* map, ray_t** slots, const int64_t* kci, int64_t nk, + ray_t** src, int64_t m, int64_t nrows0, int64_t* dst, int64_t* app, + int64_t* napp_out, int64_t* nhit_out, bool ahead) { + enum { PF_SLOT = 16, PF_ROW = 8 }; + const char* kbase[RAY_UKEY_MAX_COLS]; + size_t kesz[RAY_UKEY_MAX_COLS]; + int64_t npf = 0; + if (ahead) { + npf = nk < RAY_UKEY_MAX_COLS ? nk : RAY_UKEY_MAX_COLS; + for (int64_t k = 0; k < npf; k++) { + ray_t* kc = slots[kci[k]]; + kbase[k] = (const char*)ray_data(kc); + kesz[k] = ray_sym_elem_size(kc->type, kc->attrs); + } + for (int64_t r = 0; r < m; r++) { + uint64_t h = UPSERT_HASH_SEED; + for (int64_t k = 0; k < nk; k++) + h = ray_hash_combine(h, upsert_hash_cell(src[kci[k]], r)); + dst[r] = (int64_t)h; + } + } + + int64_t napp = 0, nhit = 0; + for (int64_t r = 0; r < m; r++) { + uint64_t h; + if (ahead) { + if (r + PF_SLOT < m) + __builtin_prefetch(&map->slot[(uint64_t)dst[r + PF_SLOT] & map->mask]); + if (r + PF_ROW < m) { + int64_t e = map->slot[(uint64_t)dst[r + PF_ROW] & map->mask]; + if (e != UKEY_SLOT_EMPTY && e != UKEY_SLOT_TOMB && + UKEY_ROW(e) < nrows0) + for (int64_t k = 0; k < npf; k++) + __builtin_prefetch(kbase[k] + (size_t)UKEY_ROW(e) * kesz[k]); + } + h = (uint64_t)dst[r]; + } else { + h = UPSERT_HASH_SEED; + for (int64_t k = 0; k < nk; k++) + h = ray_hash_combine(h, upsert_hash_cell(src[kci[k]], r)); + } + int64_t hit = upsert_map_find_src(map, h, slots, kci, nk, src, r, nrows0, app); + if (hit < 0) { + upsert_map_put(map, h, nrows0 + napp); + app[napp++] = r; + dst[r] = -1; /* written by the append */ + } else { + dst[r] = hit; + nhit++; + } + } + *napp_out = napp; + *nhit_out = nhit; +} + +/* Only the look-ahead instance is out of line; the plain one is expanded in + * upsert_apply_vectors itself, where the loop lived before the look-ahead + * existed — out of line it measured 4-9% slower on tables that fit in cache. + * The look-ahead one must stay out of line: expanded into its only caller it + * measured 12-26% slower on inserts into large tables. */ +__attribute__((noinline)) +static void upsert_resolve_ahead(upsert_map_t* map, ray_t** slots, const int64_t* kci, int64_t nk, + ray_t** src, int64_t m, int64_t nrows0, int64_t* dst, int64_t* app, + int64_t* napp, int64_t* nhit) { + upsert_resolve_impl(map, slots, kci, nk, src, m, nrows0, dst, app, napp, nhit, true); +} + +/* Column-wise body of upsert_apply for a payload upsert_vec_payload_ok + * admitted. Rows resolve in payload order exactly as the per-row loop + * does — a new key enters the map at the row it will take, so a repeat + * inside the batch finds it — then each column is written once: the new + * rows appended in one go, and every matched cell scattered in payload + * order, so a key repeated in the batch still ends at its last value. + * Appends come first because a repeat may target a row just appended. */ +static ray_t* upsert_apply_vectors(ray_t** slots, int64_t ncols, + const int64_t* kci, int64_t nk, + ray_t** src, int64_t m, int64_t nrows0, + upsert_map_t* map, int64_t* nrows) { + ray_t* dst_hdr = NULL; ray_t* app_hdr = NULL; ray_t* buf_hdr = NULL; + int64_t* dst = (int64_t*)scratch_alloc(&dst_hdr, (size_t)m * sizeof(int64_t)); + int64_t* app = dst ? (int64_t*)scratch_alloc(&app_hdr, (size_t)m * sizeof(int64_t)) : NULL; + ray_t* err = NULL; + if (!dst || !app) { err = ray_error("oom", NULL); goto out; } + + /* The probed memory is the slots plus the key columns; past the per-core + * cache the look-ahead instance pays, below it only costs. */ + size_t probe_bytes = (size_t)(map->mask + 1) * sizeof(int64_t); + for (int64_t k = 0; k < nk && k < RAY_UKEY_MAX_COLS; k++) { + ray_t* kc = slots[kci[k]]; + probe_bytes += (size_t)nrows0 * ray_sym_elem_size(kc->type, kc->attrs); + } + int64_t napp = 0, nhit = 0; + if (probe_bytes >= UPSERT_PF_MIN_BYTES) + upsert_resolve_ahead(map, slots, kci, nk, src, m, nrows0, dst, app, &napp, &nhit); + else + upsert_resolve_impl(map, slots, kci, nk, src, m, nrows0, dst, app, &napp, &nhit, false); + + if (napp > 0) { + void* buf = scratch_alloc(&buf_hdr, (size_t)napp * 16); + if (!buf) { err = ray_error("oom", NULL); goto out; } + memset(buf, 0, (size_t)napp * 16); + for (int64_t c = 0; c < ncols; c++) { + /* Grow the column by napp zeroed cells in one step (and drop + * its index), then gather the new rows straight into the tail. */ + int64_t tail = slots[c]->len; + ray_t* res = ray_vec_append_raw(slots[c], buf, napp); + if (RAY_IS_ERR(res)) { err = res; goto out; } + slots[c] = res; + upsert_vec_move(res, NULL, tail, src[c], app, napp); + } + } + + if (nhit > 0) { + for (int64_t c = 0; c < ncols; c++) { + bool is_key = false; + for (int64_t k = 0; k < nk; k++) if (kci[k] == c) { is_key = true; break; } + if (is_key) continue; /* matched: already equal */ + ray_index_drop(&slots[c]); + if (RAY_IS_ERR(slots[c])) { err = slots[c]; goto out; } + slots[c]->attrs &= (uint8_t)~RAY_ATTR_SORTED; + ray_t* col = slots[c]; + if ((size_t)col->len * ray_sym_elem_size(col->type, col->attrs) >= UPSERT_PF_MIN_BYTES) + upsert_vec_scatter_ahead(col, dst, src[c], m); + else + upsert_vec_move(col, dst, 0, src[c], NULL, m); + } + } + *nrows = nrows0 + napp; + +out: + if (buf_hdr) scratch_free(buf_hdr); + if (app_hdr) scratch_free(app_hdr); + if (dst_hdr) scratch_free(dst_hdr); + return err; +} + /* One-pass upsert. `kci[nk]` are the key column positions. The * payload is either `src_cols` (ncols collections-or-NULL, each `m` * rows) or, when src_cols is NULL, `atoms` (ncols atoms-or-NULL, m==1). @@ -15082,10 +15937,14 @@ static ray_t* upsert_apply(ray_t* tbl, int64_t inplace_sym, for (int64_t c = 0; inplace && c < ncols; c++) if (((ray_t**)ray_data(live_cols))[c]->rc != 1) inplace = false; + /* A batch of typed vectors matching the target is written column by + * column; everything else is boxed into cells below. */ + bool vec_path = src_cols && m > 1 && upsert_vec_payload_ok(tbl, src_cols, ncols, m); + /* Materialize the payload cells once: cells[r*ncols + c], with a * parallel ownership byte for atoms allocated out of typed vectors. */ ray_t* cells_hdr = NULL; ray_t* owned_hdr = NULL; - size_t ncell = (size_t)m * (size_t)ncols; + size_t ncell = vec_path ? 1 : (size_t)m * (size_t)ncols; ray_t** cells = (ray_t**)scratch_alloc(&cells_hdr, ncell * sizeof(ray_t*)); uint8_t* owned = cells ? (uint8_t*)scratch_alloc(&owned_hdr, ncell) : NULL; if (!cells || !owned) { @@ -15102,7 +15961,9 @@ static ray_t* upsert_apply(ray_t* tbl, int64_t inplace_sym, ray_index_t* ukey = NULL; /* table-resident map, borrowed from tbl */ ray_t* work = NULL; - if (src_cols) { + if (vec_path) { + /* typed payload vectors are read in place */ + } else if (src_cols) { for (int64_t c = 0; c < ncols && !err; c++) { ray_t* sc = src_cols[c]; if (!sc) continue; @@ -15128,8 +15989,9 @@ static ray_t* upsert_apply(ray_t* tbl, int64_t inplace_sym, for (int64_t c = 0; c < ncols; c++) cells[c] = atoms[c]; } - /* Type-check every cell before writing anything. */ - for (int64_t r = 0; r < m && !err; r++) { + /* Type-check every cell before writing anything. An admitted vector + * payload was checked once per column by upsert_vec_payload_ok. */ + for (int64_t r = 0; r < m && !err && !vec_path; r++) { ray_t** rc = cells + (size_t)r * ncols; for (int64_t k = 0; k < nk && !err; k++) { ray_t* kc = ray_table_get_col_idx(tbl, kci[k]); @@ -15195,24 +16057,36 @@ static ray_t* upsert_apply(ray_t* tbl, int64_t inplace_sym, if (inplace && nk > RAY_UKEY_MAX_COLS) ray_table_ukey_drop(tbl); if (inplace && nk <= RAY_UKEY_MAX_COLS) { ukey = ray_table_ukey_get(tbl); - if (ukey && !ukey_fits(ukey, kci, nk, nrows0, m)) { + /* A map that no longer describes the table is stale and must go. + * One that only ran out of room is grown from its own slots — the + * rebuild it replaces re-reads and re-hashes every key column. */ + if (ukey && (!ukey_describes(ukey, kci, nk, nrows0) || + (!ukey_has_room(ukey, nrows0, m) && + !ukey_grow(ukey, nrows0 + m)))) { ray_table_ukey_drop(tbl); ukey = NULL; } + /* Split on `ukey` first. Folding the row-count test into the + * same condition sent the `ukey == NULL && too many rows` case + * into the else, which dereferences the map that is not there. + * Declining the map for size must fall through to the scratch + * one below, exactly as a failed build does. */ if (!ukey) { - ray_t* idx = ray_index_build_ukey(kci, nk, nrows0 + m); - if (idx && !RAY_IS_ERR(idx)) { - if (ray_table_ukey_attach(tbl, idx)) ukey = ray_table_ukey_get(tbl); - else ray_release(idx); - } - /* A map we could not build or attach is not an error: fall - * through to the scratch one and answer at the old cost. */ - if (ukey) { - map.slot = (int64_t*)ray_data(ukey->u.ukey.slots); - map.mask = ukey->u.ukey.mask; - for (int64_t r = 0; r < nrows0 && !err; r++) - upsert_map_put(&map, upsert_hash_row(slots, kci, nk, r), r); - ukey->u.ukey.nrows = nrows0; + if (nrows0 + m <= UKEY_MAX_ROWS) { + ray_t* idx = ray_index_build_ukey(kci, nk, nrows0 + m); + if (idx && !RAY_IS_ERR(idx)) { + if (ray_table_ukey_attach(tbl, idx)) ukey = ray_table_ukey_get(tbl); + else ray_release(idx); + } + /* A map we could not build or attach is not an error: fall + * through to the scratch one and answer at the old cost. */ + if (ukey) { + map.slot = (int64_t*)ray_data(ukey->u.ukey.slots); + map.mask = ukey->u.ukey.mask; + for (int64_t r = 0; r < nrows0 && !err; r++) + upsert_map_put(&map, upsert_hash_row(slots, kci, nk, r), r); + ukey->u.ukey.nrows = nrows0; + } } } else { map.slot = (int64_t*)ray_data(ukey->u.ukey.slots); @@ -15230,7 +16104,9 @@ static ray_t* upsert_apply(ray_t* tbl, int64_t inplace_sym, } int64_t nrows = nrows0; - for (int64_t r = 0; r < m && !err; r++) { + if (vec_path && !err) + err = upsert_apply_vectors(slots, ncols, kci, nk, src_cols, m, nrows0, &map, &nrows); + for (int64_t r = 0; r < m && !err && !vec_path; r++) { ray_t** rc = cells + (size_t)r * ncols; uint64_t h = 0; int64_t hit; @@ -18216,9 +19092,9 @@ static void ukey_patch_after_delete(ray_t* tbl, const int64_t* rows, int64_t k, for (uint64_t s = 0; s < cap; s++) { int64_t e = slot[s]; if (e == UKEY_SLOT_EMPTY || e == UKEY_SLOT_TOMB) continue; - int64_t nr = newrow[e - 1]; + int64_t nr = newrow[UKEY_ROW(e)]; if (nr < 0) { slot[s] = UKEY_SLOT_TOMB; tomb++; } - else slot[s] = nr + 1; + else slot[s] = UKEY_PACK(UKEY_H32(e), nr); } scratch_free(nr_hdr); ix->u.ukey.n_tomb += tomb; diff --git a/src/ops/rerank.c b/src/ops/rerank.c index 64bfa095b..0c8e80c8f 100644 --- a/src/ops/rerank.c +++ b/src/ops/rerank.c @@ -30,7 +30,6 @@ #include "ops/internal.h" #include "ops/rowsel.h" #include "lang/format.h" -#include "mem/sys.h" #include "store/hnsw.h" #include #include @@ -254,7 +253,7 @@ static int64_t* accepted_rowids(ray_graph_t* g, int64_t nrows, int64_t* count) { return NULL; } - int64_t* dense = (int64_t*)ray_sys_alloc((size_t)n_accepted * sizeof(int64_t)); + int64_t* dense = (int64_t*)ray_calloc_raw((size_t)n_accepted * sizeof(int64_t)); if (!dense) { ray_release(idx_blk); ray_release(sel); @@ -368,12 +367,12 @@ ray_t* exec_ann_rerank(ray_graph_t* g, ray_op_t* op, ray_t* src) { int32_t ef_search = ef; if ((int64_t)ef_search < k) ef_search = (int32_t)k; - int64_t* out_ids = (int64_t*)ray_sys_alloc((size_t)k * sizeof(int64_t)); - double* out_ds = (double*)ray_sys_alloc((size_t)k * sizeof(double)); + int64_t* out_ids = (int64_t*)ray_calloc_raw((size_t)k * sizeof(int64_t)); + double* out_ds = (double*)ray_calloc_raw((size_t)k * sizeof(double)); if (!out_ids || !out_ds) { - if (out_ids) ray_sys_free(out_ids); - if (out_ds) ray_sys_free(out_ds); - if (accepted) ray_sys_free(accepted); + if (out_ids) ray_free_raw(out_ids); + if (out_ds) ray_free_raw(out_ds); + if (accepted) ray_free_raw(accepted); return ray_error("oom", NULL); } @@ -385,9 +384,9 @@ ray_t* exec_ann_rerank(ray_graph_t* g, ray_op_t* op, ray_t* src) { /* Build membership bitmap over the index's row space and hand it * to the filtered iterative scan as a predicate callback. */ size_t bm_size = ((size_t)n_nodes + 7) / 8; - uint8_t* member = (uint8_t*)ray_sys_alloc(bm_size); + uint8_t* member = (uint8_t*)ray_calloc_raw(bm_size); if (!member) { - ray_sys_free(out_ids); ray_sys_free(out_ds); ray_sys_free(accepted); + ray_free_raw(out_ids); ray_free_raw(out_ds); ray_free_raw(accepted); return ray_error("oom", NULL); } memset(member, 0, bm_size); @@ -395,28 +394,28 @@ ray_t* exec_ann_rerank(ray_graph_t* g, ray_op_t* op, ray_t* src) { int64_t rid = accepted[i]; if (rid >= 0 && rid < n_nodes) member[rid / 8] |= (uint8_t)(1u << (rid % 8)); } - ray_sys_free(accepted); + ray_free_raw(accepted); accepted = NULL; rr_member_ctx_t cb_ctx = { .member = member, .n_nodes = n_nodes }; n_found = ray_hnsw_search_filter(idx, query, dim, k, ef_search, rr_member_accept, &cb_ctx, out_ids, out_ds); - ray_sys_free(member); + ray_free_raw(member); } - if (accepted) ray_sys_free(accepted); + if (accepted) ray_free_raw(accepted); /* ray_hnsw_search / _filter return -1 on internal OOM — surface it as * an error rather than silently returning a zero-row table. */ if (n_found < 0) { - ray_sys_free(out_ids); - ray_sys_free(out_ds); + ray_free_raw(out_ids); + ray_free_raw(out_ds); return ray_error("oom", NULL); } ray_t* result = gather_rows_with_dist(src, out_ids, out_ds, n_found); - ray_sys_free(out_ids); - ray_sys_free(out_ds); + ray_free_raw(out_ids); + ray_free_raw(out_ds); if (!result) return ray_error("oom", NULL); return result; } @@ -466,8 +465,8 @@ ray_t* exec_knn_rerank(ray_graph_t* g, ray_op_t* op, ray_t* src) { } /* Convert query float* → double[] + norm. */ - double* q_buf = (double*)ray_sys_alloc((size_t)dim * sizeof(double)); - if (!q_buf) { if (accepted) ray_sys_free(accepted); return ray_error("oom", NULL); } + double* q_buf = (double*)ray_calloc_raw((size_t)dim * sizeof(double)); + if (!q_buf) { if (accepted) ray_free_raw(accepted); return ray_error("oom", NULL); } double q_norm_sq = 0.0; for (int32_t j = 0; j < dim; j++) { q_buf[j] = (double)query[j]; @@ -478,9 +477,9 @@ ray_t* exec_knn_rerank(ray_graph_t* g, ray_op_t* op, ray_t* src) { int64_t k_eff = k; if (k_eff > accepted_count) k_eff = accepted_count; - rr_ent_t* heap = (rr_ent_t*)ray_sys_alloc((size_t)k_eff * sizeof(rr_ent_t)); + rr_ent_t* heap = (rr_ent_t*)ray_calloc_raw((size_t)k_eff * sizeof(rr_ent_t)); if (!heap) { - ray_sys_free(q_buf); if (accepted) ray_sys_free(accepted); + ray_free_raw(q_buf); if (accepted) ray_free_raw(accepted); return ray_error("oom", NULL); } int64_t heap_size = 0; @@ -494,7 +493,7 @@ ray_t* exec_knn_rerank(ray_graph_t* g, ray_op_t* op, ray_t* src) { if (!rr_is_numeric(row) || row->len != dim) { int8_t rt = row ? row->type : RAY_NULL; int64_t rl = (row && rr_is_numeric(row)) ? row->len : -1; - ray_sys_free(heap); ray_sys_free(q_buf); ray_sys_free(accepted); + ray_free_raw(heap); ray_free_raw(q_buf); ray_free_raw(accepted); if (rl >= 0) return ray_error("type", "knn rerank: each row vector must have length %lld, got %lld", (long long)dim, (long long)rl); return ray_error("type", "knn rerank: each row must be a numeric vector (f32/f64/i32/i64), got %s", ray_type_name(rt)); @@ -508,7 +507,7 @@ ray_t* exec_knn_rerank(ray_graph_t* g, ray_op_t* op, ray_t* src) { if (!rr_is_numeric(row) || row->len != dim) { int8_t rt = row ? row->type : RAY_NULL; int64_t rl = (row && rr_is_numeric(row)) ? row->len : -1; - ray_sys_free(heap); ray_sys_free(q_buf); + ray_free_raw(heap); ray_free_raw(q_buf); if (rl >= 0) return ray_error("type", "knn rerank: each row vector must have length %lld, got %lld", (long long)dim, (long long)rl); return ray_error("type", "knn rerank: each row must be a numeric vector (f32/f64/i32/i64), got %s", ray_type_name(rt)); @@ -517,28 +516,28 @@ ray_t* exec_knn_rerank(ray_graph_t* g, ray_op_t* op, ray_t* src) { rr_heap_insert(heap, k_eff, &heap_size, d, i); } } - ray_sys_free(q_buf); - if (accepted) ray_sys_free(accepted); + ray_free_raw(q_buf); + if (accepted) ray_free_raw(accepted); rr_heap_sort(heap, heap_size); - int64_t* out_ids = (int64_t*)ray_sys_alloc((size_t)heap_size * sizeof(int64_t)); - double* out_ds = (double*)ray_sys_alloc((size_t)heap_size * sizeof(double)); + int64_t* out_ids = (int64_t*)ray_calloc_raw((size_t)heap_size * sizeof(int64_t)); + double* out_ds = (double*)ray_calloc_raw((size_t)heap_size * sizeof(double)); if ((!out_ids || !out_ds) && heap_size > 0) { - if (out_ids) ray_sys_free(out_ids); - if (out_ds) ray_sys_free(out_ds); - ray_sys_free(heap); + if (out_ids) ray_free_raw(out_ids); + if (out_ds) ray_free_raw(out_ds); + ray_free_raw(heap); return ray_error("oom", NULL); } for (int64_t i = 0; i < heap_size; i++) { out_ids[i] = heap[i].id; out_ds[i] = heap[i].d; } - ray_sys_free(heap); + ray_free_raw(heap); ray_t* result = gather_rows_with_dist(src, out_ids, out_ds, heap_size); - if (out_ids) ray_sys_free(out_ids); - if (out_ds) ray_sys_free(out_ds); + if (out_ids) ray_free_raw(out_ids); + if (out_ds) ray_free_raw(out_ds); if (!result) return ray_error("oom", NULL); return result; } diff --git a/src/ops/sort.c b/src/ops/sort.c index ffe69768f..b835f71cb 100644 --- a/src/ops/sort.c +++ b/src/ops/sort.c @@ -301,11 +301,15 @@ typedef struct { uint64_t* pw_or; /* per-worker XOR-diff accumulator */ } key_range_ctx_t; +/* Every task diffs against keys[0], the same reference the serial path uses. + * Diffing against the task's own first element only covers differences + * INSIDE a task; a byte that changes only between tasks is then invisible + * and the radix sort silently drops it. */ static void key_range_fn(void* arg, uint32_t wid, int64_t start, int64_t end) { key_range_ctx_t* c = (key_range_ctx_t*)arg; const uint64_t* keys = c->keys; uint64_t local_or = c->pw_or[wid]; - uint64_t first = keys[start]; + uint64_t first = keys[0]; for (int64_t i = start; i < end; i++) local_or |= keys[i] ^ first; c->pw_or[wid] = local_or; @@ -323,14 +327,6 @@ uint8_t compute_key_nbytes(ray_pool_t* pool, const uint64_t* keys, ray_pool_dispatch(pool, key_range_fn, &ctx, n); diff = 0; for (uint32_t w = 0; w < nw; w++) diff |= pw_or[w]; - /* Also XOR the first element from different worker ranges to - * catch cross-worker differences (workers' "first" may differ) */ - uint64_t first = keys[0]; - int64_t chunk = (n + nw - 1) / nw; - for (uint32_t w = 1; w < nw; w++) { - int64_t wstart = (int64_t)w * chunk; - if (wstart < n) diff |= keys[wstart] ^ first; - } } else { diff = 0; uint64_t first = keys[0]; @@ -665,6 +661,7 @@ typedef struct { uint64_t key_mask; /* mask for significant key bytes */ int64_t* pw_unsorted; /* count of forward inversions */ int64_t* pw_not_reverse; /* count of strict ascending pairs */ + int64_t* pw_equal; /* count of equal neighbours (ties) */ } packed_detect_ctx_t; static void packed_detect_fn(void* arg, uint32_t wid, @@ -673,13 +670,17 @@ static void packed_detect_fn(void* arg, uint32_t wid, uint64_t* k = c->keys; uint8_t kb = c->key_bits; uint64_t km = c->key_mask; - int64_t unsorted = 0, not_rev = 0; - uint64_t prev = (start > 0) ? (k[start - 1] & km) : 0; + int64_t unsorted = 0, not_rev = 0, equal = 0; + /* Only compare within this task while keys are packed in place. The + * coordinator checks cross-task pairs after dispatch has joined. Reading + * k[start - 1] here would race with the preceding task's packed store. */ + uint64_t prev = 0; for (int64_t i = start; i < end; i++) { uint64_t cur = k[i] & km; /* mask to significant bytes */ if (i > start) { if (cur < prev) unsorted++; - if (cur > prev) not_rev++; + else if (cur > prev) not_rev++; + else equal++; } /* Pack: significant key bits | (index << key_bits) */ k[i] = cur | ((uint64_t)i << kb); @@ -687,6 +688,7 @@ static void packed_detect_fn(void* arg, uint32_t wid, } c->pw_unsorted[wid] += unsorted; c->pw_not_reverse[wid] += not_rev; + c->pw_equal[wid] += equal; } /* Parallel unpack: extract indices (and optionally sorted keys) from @@ -1238,9 +1240,24 @@ static inline uint64_t strkey_load_part(const char* src, int64_t len, int offset * only fires if both records have len > parts*8 and their packed * prefixes are equal — touches pool memory via ray_str_t_cmp only * at the base case, never during the radix partitioning loop. */ +/* String order only (no row tie-break): 0 means the two strings are equal. */ +static int strkey_cmp_full(const ray_strkey_t* a, const ray_strkey_t* b, + int parts, + const ray_str_t* elems, const char* pool); + static int strkey_cmp(const ray_strkey_t* a, const ray_strkey_t* b, int parts, const ray_str_t* elems, const char* pool) { + int r = strkey_cmp_full(a, b, parts, elems, pool); + if (r != 0) return r; + /* Equal strings: order by source row so every comparison sort below + * is stable, whatever the in-place radix passes did to the bucket. */ + return (a->row > b->row) - (a->row < b->row); +} + +static int strkey_cmp_full(const ray_strkey_t* a, const ray_strkey_t* b, + int parts, + const ray_str_t* elems, const char* pool) { for (int p = 0; p < parts; p++) { if (a->parts[p] < b->parts[p]) return -1; if (a->parts[p] > b->parts[p]) return 1; @@ -1460,6 +1477,7 @@ static void strsort_top_scatter_fn(void* vctx, uint32_t wid, /* Bucket dispatch context: each task sorts one top-level bucket. */ typedef struct { ray_strkey_t* keys; + ray_strkey_t* scratch; /* same size as keys; buckets use disjoint slices */ const int64_t* starts; const int64_t* counts; int parts_bytes; @@ -1470,7 +1488,8 @@ typedef struct { int start_bp; /* byte position to begin radix within bucket */ } strsort_bucket_ctx_t; -static void strsort_aflag(ray_strkey_t* keys, int64_t n, int bp, +static void strsort_aflag(ray_strkey_t* keys, ray_strkey_t* scratch, + int64_t n, int bp, int parts_bytes, int64_t base_offset, const ray_str_t* elems, const char* pool, int parts); @@ -1481,12 +1500,19 @@ static void strsort_bucket_fn(void* vctx, uint32_t wid, int64_t s, int64_t e) { for (int64_t b = s; b < e; b++) { int64_t cnt = c->counts[b]; if (cnt <= 1) continue; - strsort_aflag(c->keys + c->starts[b], cnt, c->start_bp, + strsort_aflag(c->keys + c->starts[b], c->scratch + c->starts[b], cnt, c->start_bp, c->parts_bytes, c->base_offset, c->elems, c->pool, c->parts); } } +/* (len, row) order for records that tie on their packed prefix: shorter + * first, equal length means equal strings, which then keep row order. */ +static inline bool strkey_lenrow_lt(const ray_strkey_t* a, const ray_strkey_t* b) { + if (a->len != b->len) return a->len < b->len; + return a->row < b->row; +} + /* In-place quicksort by packed key `len` field. Used as the * finalization step for buckets where every record's string ended * at or before the current base_offset — such records tied on the @@ -1499,15 +1525,15 @@ static void strkey_qsort_by_len(ray_strkey_t* a, int64_t lo, int64_t hi) { while (hi - lo > 16) { int64_t mid = lo + (hi - lo) / 2; /* Median-of-3. */ - if (a[lo].len > a[hi].len) { ray_strkey_t t=a[lo]; a[lo]=a[hi]; a[hi]=t; } - if (a[mid].len > a[hi].len) { ray_strkey_t t=a[mid]; a[mid]=a[hi]; a[hi]=t; } - if (a[lo].len > a[mid].len) { ray_strkey_t t=a[lo]; a[lo]=a[mid]; a[mid]=t; } - uint32_t pivot = a[mid].len; + if (strkey_lenrow_lt(&a[hi], &a[lo])) { ray_strkey_t t=a[lo]; a[lo]=a[hi]; a[hi]=t; } + if (strkey_lenrow_lt(&a[hi], &a[mid])) { ray_strkey_t t=a[mid]; a[mid]=a[hi]; a[hi]=t; } + if (strkey_lenrow_lt(&a[mid], &a[lo])) { ray_strkey_t t=a[lo]; a[lo]=a[mid]; a[mid]=t; } + ray_strkey_t pivot = a[mid]; /* Hoare partition. */ int64_t i = lo - 1, j = hi + 1; for (;;) { - do { i++; } while (a[i].len < pivot); - do { j--; } while (a[j].len > pivot); + do { i++; } while (strkey_lenrow_lt(&a[i], &pivot)); + do { j--; } while (strkey_lenrow_lt(&pivot, &a[j])); if (i >= j) break; ray_strkey_t t = a[i]; a[i] = a[j]; a[j] = t; } @@ -1524,7 +1550,7 @@ static void strkey_qsort_by_len(ray_strkey_t* a, int64_t lo, int64_t hi) { for (int64_t i = lo + 1; i <= hi; i++) { ray_strkey_t cur = a[i]; int64_t j = i - 1; - while (j >= lo && a[j].len > cur.len) { + while (j >= lo && strkey_lenrow_lt(&cur, &a[j])) { a[j + 1] = a[j]; j--; } @@ -1575,8 +1601,8 @@ static bool strsort_repack_window(ray_strkey_t* keys, int64_t n, * differ in length. A string of length 3 whose bytes match * a prefix of a length-5 string must sort before it (per * ray_str_t_cmp), so finalize the bucket by sorting on len. - * When min_len == max_len every record is bitwise equal and - * any order is valid — we skip the sort entirely. */ + * When min_len == max_len every record is bitwise equal and the + * stable scatter has kept them in source order — nothing to do. */ strkey_qsort_by_len(keys, 0, n - 1); } return any_tail; @@ -1591,7 +1617,8 @@ static bool strsort_repack_window(ray_strkey_t* keys, int64_t n, * * parts_bytes = parts * 8 (cached). base_offset tracks how many bytes * of the original string have already been consumed by earlier windows. */ -static void strsort_aflag(ray_strkey_t* keys, int64_t n, int bp, +static void strsort_aflag(ray_strkey_t* keys, ray_strkey_t* scratch, + int64_t n, int bp, int parts_bytes, int64_t base_offset, const ray_str_t* elems, const char* pool, int parts) { @@ -1653,26 +1680,19 @@ static void strsort_aflag(ray_strkey_t* keys, int64_t n, int bp, } } - /* In-place swap loop: classic American Flag. For each bucket b, - * drain records out of its slice whose current byte != b into - * their correct destination, cycling until the bucket slice - * contains only records that belong in b. */ - int64_t cursors[256]; - memcpy(cursors, starts, sizeof(cursors)); - for (int b = 0; b < 256; b++) { - while (cursors[b] < ends[b]) { - ray_strkey_t v = keys[cursors[b]]; - int bb = strkey_byte_at(&v, bp); - while (bb != b) { - ray_strkey_t tmp = keys[cursors[bb]]; - keys[cursors[bb]] = v; - cursors[bb]++; - v = tmp; - bb = strkey_byte_at(&v, bp); - } - keys[cursors[b]] = v; - cursors[b]++; - } + /* Stable scatter: records go to their bucket in scan order, so + * equal strings keep their source order all the way down and the + * sort stays stable. (The in-place American-Flag swap loop this + * replaces shuffled records inside a bucket.) The scatter goes + * through the caller's scratch slice and comes back with one + * memcpy — the same traffic the swap loop's moves cost. */ + { + int64_t cursors[256]; + memcpy(cursors, starts, sizeof(cursors)); + (void)ends; + for (int64_t i = 0; i < n; i++) + scratch[cursors[strkey_byte_at(&keys[i], bp)]++] = keys[i]; + memcpy(keys, scratch, (size_t)n * sizeof(ray_strkey_t)); } /* Find the largest bucket; recurse on the rest and loop on the @@ -1686,16 +1706,43 @@ static void strsort_aflag(ray_strkey_t* keys, int64_t n, int bp, if (b == big_b) continue; int64_t cnt = counts[b]; if (cnt > 1) { - strsort_aflag(keys + starts[b], cnt, bp + 1, + strsort_aflag(keys + starts[b], scratch + starts[b], cnt, bp + 1, parts_bytes, base_offset, elems, pool, parts); } } keys += starts[big_b]; + scratch += starts[big_b]; n = big_cnt; bp++; } } +/* Emit `keys` (ascending, stable) into `out` in DESCENDING order while + * keeping every run of equal strings in its ascending (source-row) order: + * a stable descending sort. A plain reversal mirrored the ties. */ +static void strsort_emit_desc_stable(int64_t* out, const ray_strkey_t* keys, + int64_t n, int parts, + const ray_str_t* elems, const char* pool) { + (void)parts; + /* Run boundaries are found on the strings themselves: after the radix + * passes a record's packed parts hold whatever window was repacked last, + * so they no longer identify the string. */ + int64_t o = 0; + int64_t run_end = n; + while (run_end > 0) { + int64_t run_start = run_end - 1; + while (run_start > 0) { + const ray_str_t* sa = &elems[keys[run_start - 1].row]; + const ray_str_t* sb = &elems[keys[run_start].row]; + if (sa->len != sb->len) break; /* different strings */ + if (sa->len && memcmp(ray_str_t_ptr(sa, pool), ray_str_t_ptr(sb, pool), sa->len) != 0) break; + run_start--; + } + for (int64_t i = run_start; i < run_end; i++) out[o++] = (int64_t)keys[i].row; + run_end = run_start; + } +} + /* Top-level adaptive string sort. Nulls partitioned first, then the * non-null range runs through probe → single-run short-circuit → * key materialization → American-Flag MSD → scatter row indices back. @@ -1768,15 +1815,16 @@ static bool sort_str_msd_inplace(int64_t* sorted_idx, int64_t nrows, else strsort_emit_fn(&ectx, 0, 0, n_live); } else if (run_dir != 0) { - /* Single run but wrong direction — emit row-indices reversed. */ - for (int64_t i = 0, j = n_live - 1; i < j; i++, j--) { - ray_strkey_t t = keys[i]; keys[i] = keys[j]; keys[j] = t; + /* Single run but wrong direction: emit the runs of equal strings + * in reverse run order, each run in its source order (stable). */ + if (run_dir == 1) { + /* ascending input, descending wanted */ + strsort_emit_desc_stable(sorted_idx, keys, n_live, parts, elems, pool); + } else { + /* descending input, ascending wanted: the same walk — the + * runs from the back, each in its source order. */ + strsort_emit_desc_stable(sorted_idx, keys, n_live, parts, elems, pool); } - strsort_emit_ctx_t ectx = { sorted_idx, keys }; - if (go_parallel) - ray_pool_dispatch(pool_p, strsort_emit_fn, &ectx, n_live); - else - strsort_emit_fn(&ectx, 0, 0, n_live); } else { /* --- Top-level byte-0 partition. --- * When parallel: per-task histograms, prefix-sum, parallel @@ -1785,18 +1833,17 @@ static bool sort_str_msd_inplace(int64_t* sorted_idx, int64_t nrows, * single-pass American-Flag in-place swap loop. */ ray_t* tmp_hdr = NULL; ray_strkey_t* keys_sorted = keys; /* where the final data lands */ - + /* One buffer of n_live records: the stable-scatter scratch when + * sequential, or the top-level scatter target when parallel — + * after which the drained source doubles as the scratch. */ + ray_strkey_t* tmp = (ray_strkey_t*)scratch_alloc(&tmp_hdr, + (size_t)n_live * sizeof(ray_strkey_t)); + if (!tmp) { scratch_free(keys_hdr); return false; } if (!go_parallel || parts_bytes == 0) { - strsort_aflag(keys, n_live, /*bp=*/0, parts_bytes, + strsort_aflag(keys, tmp, n_live, /*bp=*/0, parts_bytes, /*base_offset=*/0, elems, pool, parts); } else { - ray_strkey_t* tmp = (ray_strkey_t*)scratch_alloc(&tmp_hdr, - (size_t)n_live * sizeof(ray_strkey_t)); - if (!tmp) { - /* Fall back to sequential sort on OOM. */ - strsort_aflag(keys, n_live, /*bp=*/0, parts_bytes, - /*base_offset=*/0, elems, pool, parts); - } else { + { uint32_t n_tasks = ray_pool_total_workers(pool_p); if (n_tasks < 1) n_tasks = 1; @@ -1811,7 +1858,7 @@ static bool sort_str_msd_inplace(int64_t* sorted_idx, int64_t nrows, * belongs to the outer cleanup block (line below) and * MUST NOT be freed twice. */ scratch_free(hist_hdr); scratch_free(off_hdr); - strsort_aflag(keys, n_live, /*bp=*/0, parts_bytes, + strsort_aflag(keys, tmp, n_live, /*bp=*/0, parts_bytes, /*base_offset=*/0, elems, pool, parts); } else { strsort_top_ctx_t tctx = { @@ -1850,8 +1897,11 @@ static bool sort_str_msd_inplace(int64_t* sorted_idx, int64_t nrows, scratch_free(off_hdr); /* Pass 4: parallel per-bucket recursive sort. */ + /* keys (the scatter source) is free now: it becomes + * the per-bucket scratch of the recursive passes. */ strsort_bucket_ctx_t bctx = { .keys = tmp, + .scratch = keys, .starts = bucket_starts, .counts = bucket_counts, .parts_bytes = parts_bytes, @@ -1869,23 +1919,21 @@ static bool sort_str_msd_inplace(int64_t* sorted_idx, int64_t nrows, } } - /* Scatter row indices back (ASC order, parallel). */ - strsort_emit_ctx_t ectx = { sorted_idx, keys_sorted }; - if (go_parallel) - ray_pool_dispatch(pool_p, strsort_emit_fn, &ectx, n_live); - else - strsort_emit_fn(&ectx, 0, 0, n_live); - - if (tmp_hdr) scratch_free(tmp_hdr); - - /* DESC reverses the sorted non-null range. */ - if (desc) { - for (int64_t i = 0, j = n_live - 1; i < j; i++, j--) { - int64_t t = sorted_idx[i]; - sorted_idx[i] = sorted_idx[j]; - sorted_idx[j] = t; - } + if (!desc) { + /* Scatter row indices back (ASC order, parallel). */ + strsort_emit_ctx_t ectx = { sorted_idx, keys_sorted }; + if (go_parallel) + ray_pool_dispatch(pool_p, strsort_emit_fn, &ectx, n_live); + else + strsort_emit_fn(&ectx, 0, 0, n_live); + } else { + /* DESC: the runs of equal strings in reverse run order, each + * run kept in ascending (source-row) order — a stable + * descending sort. Walks keys_sorted, so it runs before the + * scratch that may hold it is freed. */ + strsort_emit_desc_stable(sorted_idx, keys_sorted, n_live, parts, elems, pool); } + if (tmp_hdr) scratch_free(tmp_hdr); } scratch_free(keys_hdr); @@ -2342,7 +2390,8 @@ static ray_t* sort_indices_ex(ray_t** cols, uint8_t* descs, uint8_t* nulls_first if (!cols[k]) { can_radix = false; break; } int8_t t = cols[k]->type; if (t == RAY_STR || t == RAY_GUID) { has_wide_key = true; continue; } - if (t == RAY_F32 && n_cols != 1) { can_radix = false; break; } + /* Reuse the single-key float transform, then radix-compose ranks. */ + if (t == RAY_F32 && n_cols != 1) { has_wide_key = true; continue; } if (t != RAY_I64 && t != RAY_F64 && t != RAY_F32 && t != RAY_I32 && t != RAY_I16 && t != RAY_BOOL && t != RAY_U8 && t != RAY_SYM && @@ -2444,15 +2493,17 @@ static ray_t* sort_indices_ex(ray_t** cols, uint8_t* descs, uint8_t* nulls_first if (ptmp) { /* Fuse packing with sortedness + reverse detection */ uint32_t pd_nw = sk_pool ? ray_pool_total_workers(sk_pool) : 1; - int64_t pd_pw[pd_nw], pd_nr[pd_nw]; + int64_t pd_pw[pd_nw], pd_nr[pd_nw], pd_eq[pd_nw]; memset(pd_pw, 0, (size_t)pd_nw * sizeof(int64_t)); memset(pd_nr, 0, (size_t)pd_nw * sizeof(int64_t)); + memset(pd_eq, 0, (size_t)pd_nw * sizeof(int64_t)); uint64_t key_mask_pd = (key_bits < 64) ? ((1ULL << key_bits) - 1) : ~0ULL; packed_detect_ctx_t pd_ctx = { .keys = keys, .key_bits = key_bits, .key_mask = key_mask_pd, .pw_unsorted = pd_pw, .pw_not_reverse = pd_nr, + .pw_equal = pd_eq, }; if (sk_pool) @@ -2461,10 +2512,11 @@ static ray_t* sort_indices_ex(ray_t** cols, uint8_t* descs, uint8_t* nulls_first packed_detect_fn(&pd_ctx, 0, 0, nrows); /* Aggregate sortedness results */ - int64_t total_unsorted = 0, total_not_rev = 0; + int64_t total_unsorted = 0, total_not_rev = 0, total_equal = 0; for (uint32_t t = 0; t < pd_nw; t++) { total_unsorted += pd_pw[t]; total_not_rev += pd_nr[t]; + total_equal += pd_eq[t]; } /* Check cross-task boundaries */ int64_t grain = RAY_DISPATCH_MORSELS * RAY_MORSEL_ELEMS; @@ -2474,19 +2526,40 @@ static ray_t* sort_indices_ex(ray_t** cols, uint8_t* descs, uint8_t* nulls_first uint64_t ka = keys[b-1] & key_mask_s; uint64_t kb2 = keys[b] & key_mask_s; if (kb2 < ka) total_unsorted++; - if (kb2 > ka) total_not_rev++; + else if (kb2 > ka) total_not_rev++; + else total_equal++; } if (total_unsorted == 0) { /* Already sorted - identity permutation */ sorted_idx = indices; radix_done = true; - } else if (total_not_rev == 0 && nrows > 1) { - /* Reverse-sorted - reverse indices in O(n) */ + } else if (total_not_rev == 0 && nrows > 1 && total_equal == 0) { + /* Strictly reverse-sorted: reverse in O(n). */ for (int64_t i = 0; i < nrows; i++) indices[i] = nrows - 1 - i; sorted_idx = indices; radix_done = true; + } else if (total_not_rev == 0 && nrows > 1) { + /* Reverse-sorted with ties: emit the runs of + * equal keys in reverse run order, each run + * in its original row order, so ties stay + * stable. A plain n-1-i mirrored every + * plateau of duplicates. */ + int64_t out = 0; + int64_t run_end = nrows; + while (run_end > 0) { + int64_t run_start = run_end - 1; + uint64_t kr = keys[run_start] & key_mask_s; + while (run_start > 0 && + (keys[run_start - 1] & key_mask_s) == kr) + run_start--; + for (int64_t i = run_start; i < run_end; i++) + indices[out++] = i; + run_end = run_start; + } + sorted_idx = indices; + radix_done = true; } else { /* Packed radix sort - half the memory traffic */ uint64_t* sorted = packed_radix_sort_run( @@ -3929,12 +4002,13 @@ ray_t* ray_rank_fn(ray_t* x) { /* Pool worker for the already-in-order detection scan: each task verifies * consecutive-row order over [start,end) INCLUDING the (start-1,start) * boundary pair, so chunk edges are covered. The shared flag only ever - * transitions 1 -> 0 (benign race) and doubles as a bail signal. */ + * transitions 1 -> 0 atomically and doubles as a bail signal. No payload is + * published through it; the dispatch barrier joins all tasks before return. */ typedef struct { ray_t** key_cols; int64_t n_keys; uint8_t descending; - volatile int* ordered; + _Atomic int* ordered; } sorted_check_ctx_t; static void sorted_check_fn(void* raw, uint32_t wid, int64_t start, int64_t end) { @@ -3944,7 +4018,7 @@ static void sorted_check_fn(void* raw, uint32_t wid, int64_t start, int64_t end) /* Specialized (SYM, i64-family) two-key loop — the canonical * (sym, time) shape. The generic loop below pays a type-switch per - * read and a volatile flag load per row; here adjacent sym ids + * read and an atomic flag load per row; here adjacent sym ids * compare directly (equal ids on ~all rows of a sorted table — the * string comparison only runs at run boundaries), the secondary key * reads raw i64, and the bail flag is polled per 4096-row block. @@ -3966,13 +4040,13 @@ static void sorted_check_fn(void* raw, uint32_t wid, int64_t start, int64_t end) const int64_t* restrict tv = (const int64_t*)ray_data(c->key_cols[1]); struct ray_sym_domain_s* dom = ray_sym_vec_domain(sc); for (int64_t b = start; b < end; b += 4096) { - if (!*c->ordered) return; + if (!atomic_load_explicit(c->ordered, memory_order_relaxed)) return; int64_t e = b + 4096 < end ? b + 4096 : end; for (int64_t i = b; i < e; i++) { int64_t ia = ray_read_sym(sd, i - 1, RAY_SYM, sattrs); int64_t ib = ray_read_sym(sd, i, RAY_SYM, sattrs); if (RAY_LIKELY(ia == ib)) { - if (tv[i] < tv[i - 1]) { *c->ordered = 0; return; } + if (tv[i] < tv[i - 1]) { atomic_store_explicit(c->ordered, 0, memory_order_relaxed); return; } } else { ray_t* sa = ray_sym_domain_str(dom, ia); ray_t* sb = ray_sym_domain_str(dom, ib); @@ -3981,7 +4055,7 @@ static void sorted_check_fn(void* raw, uint32_t wid, int64_t start, int64_t end) uint32_t ml = la < lb ? la : lb; int cmp = ml ? memcmp(ray_str_ptr(sa), ray_str_ptr(sb), ml) : 0; if (cmp == 0) cmp = (la > lb) - (la < lb); - if (cmp > 0) { *c->ordered = 0; return; } + if (cmp > 0) { atomic_store_explicit(c->ordered, 0, memory_order_relaxed); return; } } } } @@ -3989,7 +4063,7 @@ static void sorted_check_fn(void* raw, uint32_t wid, int64_t start, int64_t end) } for (int64_t i = start; i < end; i++) { - if (!*c->ordered) return; /* another task already found a violation */ + if (!atomic_load_explicit(c->ordered, memory_order_relaxed)) return; /* another task already found a violation */ int cmp = 0; for (int64_t k = 0; k < c->n_keys && cmp == 0; k++) { ray_t* col = c->key_cols[k]; @@ -4012,7 +4086,7 @@ static void sorted_check_fn(void* raw, uint32_t wid, int64_t start, int64_t end) cmp = (va > vb) - (va < vb); } } - if (c->descending ? (cmp < 0) : (cmp > 0)) { *c->ordered = 0; return; } + if (c->descending ? (cmp < 0) : (cmp > 0)) { atomic_store_explicit(c->ordered, 0, memory_order_relaxed); return; } } } @@ -4056,7 +4130,7 @@ bool ray_key_cols_sorted(ray_t** key_cols, int64_t n_keys, uint8_t descending, return false; } if (nrows < 2) return true; - volatile int ordered = 1; + _Atomic int ordered = 1; sorted_check_ctx_t sctx = { .key_cols = key_cols, .n_keys = n_keys, .descending = descending, .ordered = &ordered, @@ -4066,7 +4140,7 @@ bool ray_key_cols_sorted(ray_t** key_cols, int64_t n_keys, uint8_t descending, ray_pool_dispatch(pool, sorted_check_fn, &sctx, nrows); else sorted_check_fn(&sctx, 0, 1, nrows); - return ordered != 0; + return atomic_load_explicit(&ordered, memory_order_relaxed) != 0; } static bool sort_part_key_type(int8_t t) { @@ -4224,7 +4298,7 @@ ray_t* sort_table_by_keys(ray_t* tbl, ray_t* keys, uint8_t descending) { detectable = false; } if (detectable) { - volatile int ordered = 1; + _Atomic int ordered = 1; sorted_check_ctx_t sctx = { .key_cols = key_cols, .n_keys = n_keys, .descending = descending, .ordered = &ordered, @@ -4234,7 +4308,7 @@ ray_t* sort_table_by_keys(ray_t* tbl, ray_t* keys, uint8_t descending) { ray_pool_dispatch(pool, sorted_check_fn, &sctx, nrows); else sorted_check_fn(&sctx, 0, 1, nrows); - if (ordered) { + if (atomic_load_explicit(&ordered, memory_order_relaxed)) { ray_t* out = (descending || n_keys <= 1) ? NULL : sort_stamp_ordered_table(tbl, key_ids[0], key_cols[0], n_keys > 1 ? key_ids[1] : -1); diff --git a/src/ops/system.c b/src/ops/system.c index 417b034ae..5a594ed89 100644 --- a/src/ops/system.c +++ b/src/ops/system.c @@ -1432,15 +1432,14 @@ ray_t* ray_hopen_fn(ray_t** args, int64_t n) { if (!ray_is_atom(x) || x->type != -RAY_STR) return ray_error("type", ".ipc.open expects a string \"host:port[:user:password]\", got %s", ray_type_name(x->type)); - /* Optional connect timeout in milliseconds (0 = use default). */ - int timeout_ms = 0; + /* Optional second argument: an integer connect timeout in + * milliseconds (0 = use default), or an options dict carrying + * `timeout` and/or `compress` (#541). */ + int timeout_ms = 0; + size_t compress = RAY_IPC_COMPRESS_AUTO; if (n == 2) { - ray_t* t = args[1]; - if (!ray_is_atom(t) || (t->type != -RAY_I64 && t->type != -RAY_I32)) - return ray_error("type", ".ipc.open timeout must be an integer (milliseconds), got %s", ray_type_name(t->type)); - int64_t tv = (t->type == -RAY_I64) ? t->i64 : t->i32; - if (tv < 0) return ray_error("domain", ".ipc.open timeout must be >= 0, got %lld", (long long)tv); - timeout_ms = (tv > INT_MAX) ? INT_MAX : (int)tv; + ray_t* err = ray_ipc_parse_open_opts(args[1], &timeout_ms, &compress); + if (err) return err; } const char* s = ray_str_ptr(x); @@ -1497,7 +1496,7 @@ ray_t* ray_hopen_fn(ray_t** args, int64_t n) { const char* pw_ptr = (n_parts >= 4) ? password : NULL; const char* us_ptr = (n_parts >= 4) ? user : NULL; - int64_t h = ray_ipc_connect(host, (uint16_t)port, us_ptr, pw_ptr, timeout_ms); + int64_t h = ray_ipc_connect_opts(host, (uint16_t)port, us_ptr, pw_ptr, timeout_ms, compress); if (h == -2) return ray_error("access", "server requires authentication"); if (h == -3) return ray_error("access", "authentication failed"); if (h == -4) return ray_error("io", "wire version mismatch: %s:%d", host, port); diff --git a/src/store/part.c b/src/store/part.c index 00134c89f..e536a7e7a 100644 --- a/src/store/part.c +++ b/src/store/part.c @@ -34,6 +34,7 @@ #include "table/sym.h" #include "table/domain.h" #include "lang/cal.h" /* MONTHDAYS, date_leap_year — calendar-date validation */ +#include "core/numparse.h" /* ray_parse_i64 — overflow-checked integer names */ #include #include #include @@ -63,27 +64,51 @@ static bool is_date_dir(const char* name) { return day <= dim; } -/* Check if string is a pure integer (digits only, possibly with leading minus). */ -static bool is_integer_str(const char* s) { - if (!*s) return false; - if (*s == '-') s++; +/* Digit-only name: integer-shaped. Names get here through the digit/dot + * filter in collect_part_dirs, so a sign never occurs. */ +static bool is_digits(const char* s) { if (!*s) return false; for (; *s; s++) if (*s < '0' || *s > '9') return false; return true; } -/* Infer MAPCOMMON sub-type from partition directory names. */ -static uint8_t infer_mc_type(char** part_dirs, int64_t part_count) { - bool all_date = true, all_int = true; - for (int64_t i = 0; i < part_count; i++) { - if (all_date && !is_date_dir(part_dirs[i])) all_date = false; - if (all_int && !is_integer_str(part_dirs[i])) all_int = false; - if (!all_date && !all_int) break; +/* Parse a digit-only partition name into *out; false when its value does not + * fit int64_t. ray_parse_i64 is the overflow-checked parser the tokenizer + * and the CSV reader use — a 20-digit name used to be accumulated straight + * past INT64_MAX here, signed-overflow UB that wrapped the partition key to + * an unrelated number in release and aborts under UBSan. */ +static bool parse_int_dir(const char* s, int64_t* out) { + size_t n = strlen(s); + return n > 0 && ray_parse_i64(s, n, out) == n; +} + +/* Classify a partition set by its directory names into the MAPCOMMON key + * type: all YYYY.MM.DD → DATE; all digit-only and within int64 → I64; + * anything else → SYM (opaque names in string order). + * + * A digit-only name past int64 in an otherwise all-integer set is + * RAY_ERR_CORRUPT with *out_bad = its index rather than a fall-back to SYM: + * its intent is unambiguous, and silently retyping the whole set would make + * `(== part 2)` match nothing while `(>= part 2)` returned partition 1 too. + * In a mixed set every name is an opaque SYM key, so a long digit-only one + * is as valid there as any other. */ +static ray_err_t infer_mc_type(char** part_dirs, int64_t part_count, + uint8_t* out_type, int64_t* out_bad) { + bool all_date = true, all_digits = true; + int64_t unfit = -1; /* first digit-only name past int64 */ + for (int64_t i = 0; i < part_count && (all_date || all_digits); i++) { + const char* name = part_dirs[i]; + if (all_date && !is_date_dir(name)) all_date = false; + if (all_digits && !is_digits(name)) all_digits = false; + int64_t v; + if (all_digits && unfit < 0 && !parse_int_dir(name, &v)) unfit = i; } - if (all_date) return RAY_MC_DATE; - if (all_int) return RAY_MC_I64; - return RAY_MC_SYM; + if (all_date) { *out_type = RAY_MC_DATE; return RAY_OK; } + if (!all_digits) { *out_type = RAY_MC_SYM; return RAY_OK; } + if (unfit >= 0) { *out_bad = unfit; return RAY_ERR_CORRUPT; } + *out_type = RAY_MC_I64; + return RAY_OK; } /* Parse "YYYY.MM.DD" → days since 2000-01-01 (Rayforce epoch). @@ -101,15 +126,6 @@ static int32_t parse_date_dir(const char* name) { return (int32_t)(era * 146097 + (int64_t)doe - 719468 - 10957); } -/* Parse integer string → int64_t. Caller guarantees is_integer_str(). */ -static int64_t parse_int_dir(const char* s) { - int neg = 0; - if (*s == '-') { neg = 1; s++; } - int64_t v = 0; - for (; *s; s++) v = v * 10 + (*s - '0'); - return neg ? -v : v; -} - /* -------------------------------------------------------------------------- * Partitioned table: date-partitioned directory of splayed tables * @@ -123,19 +139,50 @@ static int64_t parse_int_dir(const char* s) { * cover main attack vector. * -------------------------------------------------------------------------- */ +/* qsort comparators for partition names. Date names (fixed-width + * YYYY.MM.DD) and opaque names sort as strings; an all-integer set sorts by + * value, with the name as tie-break so two spellings of one value ("01" and + * "1") keep a filesystem-independent order — they load as equal keys, which + * the insert validator then reports as corrupt. Every name of an integer + * set passed infer_mc_type's fit check, so the parse here cannot fail. */ +static int name_cmp(const void* a, const void* b) { + return strcmp(*(const char* const*)a, *(const char* const*)b); +} + +static int int_name_cmp(const void* a, const void* b) { + const char* sa = *(const char* const*)a; + const char* sb = *(const char* const*)b; + int64_t va = 0, vb = 0; + parse_int_dir(sa, &va); + parse_int_dir(sb, &vb); + if (va != vb) return va < vb ? -1 : 1; + return strcmp(sa, sb); +} + /* -------------------------------------------------------------------------- * collect_part_dirs — scan db_root for partition directories * - * Collects directory names that match digit/dot pattern, bubble-sorts them. + * Collects directory names that match digit/dot pattern, classifies the set + * (*out_mc_type: RAY_MC_DATE / I64 / SYM) and sorts it into partition-key + * order — by value for an integer set, else by name. Every consumer relies + * on that order: ray_read_parted emits the MAPCOMMON keys in it (and query + * paths take DATE keys as ascending), ray_parted_tables and ray_parted_fill + * read the "most recent" partition as the LAST one. By name, 10 sorted + * before 2 and the last of 1..12 was "9": I64 keys came out unordered and a + * table first added in partition 12 was invisible to .db.parted.tables. * The symfile (".sym") and its lock are dotfiles, and partition names are * digit/dot-only, so neither is ever picked up here. + * Returns NULL on success (count may be 0: an empty or non-parted root), + * else the error object to hand back to the caller. * Caller must free each entry with ray_sys_free and the array itself. * -------------------------------------------------------------------------- */ -static ray_err_t collect_part_dirs(const char* db_root, char*** out_dirs, - int64_t* out_count) { +static ray_t* collect_part_dirs(const char* db_root, char*** out_dirs, + int64_t* out_count, uint8_t* out_mc_type) { + if (out_mc_type) *out_mc_type = RAY_MC_SYM; DIR* d = opendir(db_root); - if (!d) return RAY_ERR_IO; + if (!d) + return ray_error("io", "parted %s: cannot enumerate partition directories", db_root); char** part_dirs = NULL; int64_t part_count = 0; @@ -173,7 +220,8 @@ static ray_err_t collect_part_dirs(const char* db_root, char*** out_dirs, if (err != RAY_OK) { for (int64_t i = 0; i < part_count; i++) ray_free_raw(part_dirs[i]); ray_free_raw(part_dirs); - return err; + return ray_error(ray_err_code_str(err), + "parted %s: cannot enumerate partition directories", db_root); } if (part_count == 0) { @@ -184,24 +232,29 @@ static ray_err_t collect_part_dirs(const char* db_root, char*** out_dirs, ray_free_raw(part_dirs); *out_dirs = NULL; *out_count = 0; - return RAY_OK; + return NULL; } - /* Sort partition names for deterministic order. - * O(n^2) but partition count is typically small (< 1000 daily partitions). */ - for (int64_t i = 0; i < part_count - 1; i++) { - for (int64_t j = i + 1; j < part_count; j++) { - if (strcmp(part_dirs[i], part_dirs[j]) > 0) { - char* tmp = part_dirs[i]; - part_dirs[i] = part_dirs[j]; - part_dirs[j] = tmp; - } - } + /* Classify, then sort into key order (see above). A digit-only name + * past int64 in an otherwise integer set is a key of no type: fail + * naming it rather than hand every consumer a silently retyped set. */ + uint8_t mc_type = RAY_MC_SYM; + int64_t bad = 0; + if (infer_mc_type(part_dirs, part_count, &mc_type, &bad) != RAY_OK) { + ray_t* e = ray_error("corrupt", + "parted %s: partition directory %s: integer name does not fit int64", + db_root, part_dirs[bad]); + for (int64_t i = 0; i < part_count; i++) ray_free_raw(part_dirs[i]); + ray_free_raw(part_dirs); + return e; } + qsort(part_dirs, (size_t)part_count, sizeof(char*), + mc_type == RAY_MC_I64 ? int_name_cmp : name_cmp); *out_dirs = part_dirs; *out_count = part_count; - return RAY_OK; + if (out_mc_type) *out_mc_type = mc_type; + return NULL; } /* -------------------------------------------------------------------------- @@ -245,17 +298,18 @@ ray_t* ray_read_parted(const char* db_root, const char* table_name) { "or missing \"\" at position 0)", sym_path); } - /* Scan db_root for partition directories (the ".sym" dotfile is skipped) */ + /* Scan db_root for partition directories (the ".sym" dotfile is skipped); + * the set comes back in key order together with its MAPCOMMON key type. */ char** part_dirs = NULL; int64_t part_count = 0; - ray_err_t collect_err = collect_part_dirs(db_root, &part_dirs, &part_count); - if (collect_err != RAY_OK) { + uint8_t mc_type = RAY_MC_SYM; + ray_t* collect_err = collect_part_dirs(db_root, &part_dirs, &part_count, &mc_type); + if (collect_err) { if (trace) fprintf(stderr, "parted.get: collect dirs failed err=%s\n", - ray_err_code_str(collect_err)); + ray_err_code(collect_err)); if (dom) ray_sym_domain_release(dom); - return ray_error(ray_err_code_str(collect_err), - "parted %s: cannot enumerate partition directories", db_root); + return collect_err; } if (trace) fprintf(stderr, "parted.get: parts=%" PRId64 "\n", part_count); @@ -356,9 +410,6 @@ ray_t* ray_read_parted(const char* db_root, const char* table_name) { } } - /* Infer MAPCOMMON sub-type from partition directory names */ - uint8_t mc_type = infer_mc_type(part_dirs, part_count); - /* Build result table: 1 MAPCOMMON + ncols data columns */ ray_t* result = ray_table_new(ncols + 2); if (!result || RAY_IS_ERR(result)) goto fail_tables; @@ -389,7 +440,7 @@ ray_t* ray_read_parted(const char* db_root, const char* table_name) { } else if (mc_type == RAY_MC_I64) { int64_t* kv_data = (int64_t*)ray_data(key_values); for (int64_t p = 0; p < part_count; p++) { - kv_data[p] = parse_int_dir(part_dirs[p]); + parse_int_dir(part_dirs[p], &kv_data[p]); rc_data[p] = ray_table_nrows(part_tables[p]); } } else { @@ -609,10 +660,8 @@ ray_t* ray_parted_tables(const char* db_root) { char** part_dirs = NULL; int64_t part_count = 0; - ray_err_t e = collect_part_dirs(db_root, &part_dirs, &part_count); - if (e != RAY_OK) - return ray_error(ray_err_code_str(e), - "parted %s: cannot enumerate partition directories", db_root); + ray_t* e = collect_part_dirs(db_root, &part_dirs, &part_count, NULL); + if (e) return e; if (part_count <= 0) { /* Existing-but-empty (or non-parted) root → no tables. Return an * empty SYM vector rather than an error: a freshly-created db root @@ -670,10 +719,8 @@ ray_t* ray_parted_fill(const char* db_root) { char** part_dirs = NULL; int64_t part_count = 0; - ray_err_t e = collect_part_dirs(db_root, &part_dirs, &part_count); - if (e != RAY_OK) - return ray_error(ray_err_code_str(e), - "parted %s: cannot enumerate partition directories", db_root); + ray_t* e = collect_part_dirs(db_root, &part_dirs, &part_count, NULL); + if (e) return e; if (part_count <= 0) { /* Empty (or non-parted) root → nothing to fill. Matches the * "empty vector when nothing needed fixing" contract, so a fill on diff --git a/src/store/serde.c b/src/store/serde.c index f6676f3be..6bed0ad41 100644 --- a/src/store/serde.c +++ b/src/store/serde.c @@ -33,11 +33,13 @@ #include "vec/str.h" #include "vec/vec.h" #include "ops/ops.h" +#include "ops/hash.h" #ifndef RAY_OS_WINDOWS # include #endif #include "table/sym.h" +#include "table/domain.h" #include "lang/env.h" #include "lang/eval.h" #include "lang/format.h" @@ -151,6 +153,64 @@ static const char* serde_builtin_name(ray_t* obj, size_t* nlen) { return name; } + +/* ---- SYM encode: a per-thread id -> (bytes, len) cache ------------------- + * + * ray_sym_str takes the global sym spinlock around a single array read, so + * walking a SYM column cost one atomic exchange per cell — and both + * ray_serde_size and ray_ser_raw walk it, so a frame paid 2 * rows * cols + * lock round-trips for a handful of distinct strings (#561). On a + * single-threaded tickerplant that spinning is entirely uncontended. + * ray_str_len and ray_str_ptr are out-of-line calls made per cell on top of + * that, and profiled at 30% of encode on their own. + * + * A hit avoids all three. Sym ids are stable positions for the lifetime of + * the table, so the cache persists across calls and needs no per-column + * setup; ray_sym_epoch changes when the table is torn down and re-inited, + * which is the only event that can invalidate it. + * + * Direct-mapped, and a collision simply re-resolves: nothing is ever + * dropped, so the size is a speed/space tradeoff rather than a limit. It + * lives in thread-local storage, so encode allocates nothing — the path it + * replaces allocated nothing either. + * + * A column with far more distinct symbols than slots would otherwise pay + * the probe and the store on top of a resolve it still has to do, which + * measured 15% SLOWER than not caching at all. So the first + * SYM_ENC_SAMPLE cells are sampled and the cache is switched off for the + * rest of the column if fewer than a quarter of them hit. Both numbers + * are a heuristic, not a limit: either way every cell resolves correctly, + * and the worst case is the probe on a bounded prefix. */ +#define SYM_ENC_CACHE_BITS 10 +#define SYM_ENC_CACHE_N (1u << SYM_ENC_CACHE_BITS) + +#define SYM_ENC_SAMPLE 256u + +typedef struct { + const char* ptr; /* NULL = empty slot */ + uint32_t len; + int64_t id; +} sym_enc_ent; + + +static _Thread_local sym_enc_ent g_sym_enc_cache[SYM_ENC_CACHE_N]; +static _Thread_local uint64_t g_sym_enc_epoch; + +/* True when cells may be resolved through the cache: the runtime domain + * (others already resolve lock-free) and no audit hook (which is a + * per-cell contract). */ +static inline bool sym_enc_cacheable(ray_t* obj) { + if (ray_g_sym_audit) return false; + if (ray_sym_vec_domain(obj) != ray_sym_runtime_domain()) return false; + uint64_t ep = ray_sym_epoch(); + if (ep != g_sym_enc_epoch) { + memset(g_sym_enc_cache, 0, sizeof(g_sym_enc_cache)); + g_sym_enc_epoch = ep; + } + return true; +} + + /* -------------------------------------------------------------------------- * ray_serde_size — calculate serialized size (excluding IPC header) * -------------------------------------------------------------------------- */ @@ -220,9 +280,30 @@ int64_t ray_serde_size(ray_t* obj) { * singleton). Also honors narrow W8/16/32 index widths. Must * stay in lockstep with the ray_ser_raw RAY_SYM loop below. */ int64_t size = 1 + 1 + 8; - for (int64_t i = 0; i < obj->len; i++) { - ray_t* s = ray_sym_vec_cell(obj, i); - size += (s ? (int64_t)ray_str_len(s) : 0) + 1; + int64_t i = 0; + if (sym_enc_cacheable(obj)) { + const void* data = ray_data(obj); + uint8_t attrs = obj->attrs; + uint32_t seen = 0, hits = 0; + for (; i < obj->len; i++) { + int64_t id = ray_read_sym(data, i, RAY_SYM, attrs); + sym_enc_ent* e = &g_sym_enc_cache[(uint64_t)id & (SYM_ENC_CACHE_N - 1)]; + if (e->ptr && e->id == id) { size += (int64_t)e->len + 1; hits++; } + else { + ray_t* a = ray_sym_str(id); + uint32_t l = a ? (uint32_t)ray_str_len(a) : 0u; + const char* pp = a ? ray_str_ptr(a) : NULL; + if (pp) { e->ptr = pp; e->len = l; e->id = id; } + size += (int64_t)l + 1; + } + /* Thrashing column: leave the rest to the plain loop below, + * which is what this path costs without a cache. */ + if (++seen == SYM_ENC_SAMPLE && hits * 4u < seen) { i++; break; } + } + } + for (; i < obj->len; i++) { + ray_t* a = ray_sym_vec_cell(obj, i); + size += (a ? (int64_t)ray_str_len(a) : 0) + 1; } return size; } @@ -435,15 +516,37 @@ int64_t ray_ser_raw(uint8_t* buf, ray_t* obj) { buf[0] = wire_attrs; buf++; memcpy(buf, &obj->len, 8); buf += 8; c = 0; - for (int64_t i = 0; i < obj->len; i++) { - ray_t* s = ray_sym_vec_cell(obj, i); - if (s) { - size_t slen = ray_str_len(s); - memcpy(buf + c, ray_str_ptr(s), slen); - c += (int64_t)slen; + { + int64_t i = 0; + if (sym_enc_cacheable(obj)) { + const void* data = ray_data(obj); + uint8_t attrs = obj->attrs; + uint32_t seen = 0, hits = 0; + for (; i < obj->len; i++) { + int64_t id = ray_read_sym(data, i, RAY_SYM, attrs); + sym_enc_ent* e = &g_sym_enc_cache[(uint64_t)id & (SYM_ENC_CACHE_N - 1)]; + const char* pp; uint32_t slen; + if (e->ptr && e->id == id) { pp = e->ptr; slen = e->len; hits++; } + else { + ray_t* a = ray_sym_str(id); + slen = a ? (uint32_t)ray_str_len(a) : 0u; + pp = a ? ray_str_ptr(a) : NULL; + if (pp) { e->ptr = pp; e->len = slen; e->id = id; } + } + if (slen) { memcpy(buf + c, pp, slen); c += (int64_t)slen; } + buf[c++] = '\0'; + if (++seen == SYM_ENC_SAMPLE && hits * 4u < seen) { i++; break; } + } + } + for (; i < obj->len; i++) { + ray_t* a = ray_sym_vec_cell(obj, i); + if (a) { + size_t slen = ray_str_len(a); + memcpy(buf + c, ray_str_ptr(a), slen); + c += (int64_t)slen; + } + buf[c++] = '\0'; } - buf[c] = '\0'; - c++; } return 1 + 1 + 8 + c; } @@ -704,16 +807,83 @@ static ray_t* de_raw_inner(uint8_t* buf, int64_t* len) { if (!vec || RAY_IS_ERR(vec)) return vec; vec->len = l; int64_t* ids = (int64_t*)ray_data(vec); - for (int64_t i = 0; i < l; i++) { - size_t slen = safe_strlen(buf, *len); - if ((int64_t)slen >= *len) { - vec->len = i; + if (l > 0) { + /* Interning cell by cell takes the global sym lock once per row, + * so a column of two distinct values still pays l full interns. + * Instead: dedupe lock-free, then intern the distinct strings + * once, under a single lock. */ + size_t cap = 16; + while (cap < (size_t)l * 2) cap <<= 1; + size_t nd_max = (size_t)l; + size_t work_sz = cap * sizeof(uint32_t) + + nd_max * (sizeof(uint32_t) + sizeof(const char*) + + sizeof(size_t) + sizeof(int64_t)); + uint8_t* work = (uint8_t*)ray_alloc_raw(work_sz); + if (!work) { + vec->len = 0; ray_release(vec); - return ray_error("domain", "deserialize sym vector: unterminated sym at index %lld, no NUL within %lld bytes", (long long)i, (long long)*len); + return ray_error("oom", "deserialize sym vector: scratch alloc failed"); } - ids[i] = ray_sym_intern((const char*)buf, slen); - buf += slen + 1; - *len -= (int64_t)slen + 1; + /* slots holds distinct-index + 1, so 0 means "empty bucket". + * nd <= l <= 1e9 (range-checked above), so 32 bits suffice, and + * only this region needs zeroing — the d_* arrays are written + * before they are read. cap is a power of two, so the 8-byte + * arrays that follow stay 8-byte aligned; the 4-byte hashes go + * last so alignment holds whatever nd_max is. */ + memset(work, 0, cap * sizeof(uint32_t)); + uint32_t* slots = (uint32_t*)work; + uint8_t* cur = work + cap * sizeof(uint32_t); + const char** d_str = (const char**)cur; cur += nd_max * sizeof(const char*); + size_t* d_len = (size_t*)cur; cur += nd_max * sizeof(size_t); + int64_t* d_id = (int64_t*)cur; cur += nd_max * sizeof(int64_t); + uint32_t* d_hash = (uint32_t*)cur; + int64_t nd = 0; + + /* Pass 1, lock-free: delimit and hash every cell, dedupe into a + * local open-addressing table. ids[i] temporarily holds the + * cell's distinct index. */ + for (int64_t i = 0; i < l; i++) { + size_t slen = safe_strlen(buf, *len); + if ((int64_t)slen >= *len) { + ray_free_raw(work); + vec->len = 0; + ray_release(vec); + return ray_error("domain", "deserialize sym vector: unterminated sym at index %lld, no NUL within %lld bytes", (long long)i, (long long)*len); + } + uint32_t h = (uint32_t)ray_hash_bytes((const char*)buf, slen); + size_t s = h & (cap - 1); + int64_t e; + for (;;) { + uint32_t v = slots[s]; + if (v == 0) { + e = nd++; + d_hash[e] = h; + d_str[e] = (const char*)buf; + d_len[e] = slen; + slots[s] = (uint32_t)(e + 1); + break; + } + e = (int64_t)v - 1; + if (d_hash[e] == h && d_len[e] == slen && + memcmp(d_str[e], buf, slen) == 0) break; + s = (s + 1) & (cap - 1); + } + ids[i] = e; + buf += slen + 1; + *len -= (int64_t)slen + 1; + } + + /* Pass 2: one lock, one probe per distinct string. */ + if (ray_sym_intern_batch(d_hash, d_str, d_len, nd, d_id) < 0) { + ray_free_raw(work); + vec->len = 0; + ray_release(vec); + return ray_error("oom", "deserialize sym vector: intern failed"); + } + + /* Pass 3: distinct index -> sym id. */ + for (int64_t i = 0; i < l; i++) ids[i] = d_id[ids[i]]; + ray_free_raw(work); } if (attrs & RAY_ATTR_HAS_NULLS) vec->attrs |= RAY_ATTR_HAS_NULLS; diff --git a/src/table/sym.c b/src/table/sym.c index d91dbb3be..2843827de 100644 --- a/src/table/sym.c +++ b/src/table/sym.c @@ -117,7 +117,14 @@ static int64_t sym_intern_nolock(uint32_t hash, const char* str, size_t len, * ray_sym_init * -------------------------------------------------------------------------- */ +static _Atomic uint64_t g_sym_epoch_ctr = 0; + +uint64_t ray_sym_epoch(void) { + return atomic_load_explicit(&g_sym_epoch_ctr, memory_order_acquire); +} + ray_err_t ray_sym_init(void) { + atomic_fetch_add_explicit(&g_sym_epoch_ctr, 1, memory_order_release); bool expected = false; if (!atomic_compare_exchange_strong_explicit(&g_sym_inited, &expected, true, memory_order_acq_rel, memory_order_acquire)) @@ -213,6 +220,7 @@ ray_err_t ray_sym_init(void) { * -------------------------------------------------------------------------- */ void ray_sym_destroy(void) { + atomic_fetch_add_explicit(&g_sym_epoch_ctr, 1, memory_order_release); if (!atomic_load_explicit(&g_sym_inited, memory_order_acquire)) return; if (g_sym.lazy_map) { @@ -792,6 +800,30 @@ int64_t ray_sym_intern_prehashed(uint32_t hash, const char* str, size_t len) { return sym_intern_nolock(hash, str, len, true); } +/* -------------------------------------------------------------------------- + * ray_sym_intern_batch -- intern n pre-hashed strings under one lock. + * + * For decoders that have already deduplicated their input: each distinct + * string costs one probe, and the whole batch one lock round-trip. The + * per-string work is exactly ray_sym_intern's (search_lazy = true), so ids + * and dotted-segment caching are identical. + * Returns 0, or -1 if any intern failed (out_ids then partially filled). + * -------------------------------------------------------------------------- */ + +int64_t ray_sym_intern_batch(const uint32_t* hashes, const char* const* strs, + const size_t* lens, int64_t n, int64_t* out_ids) { + if (!atomic_load_explicit(&g_sym_inited, memory_order_acquire)) return -1; + if (n <= 0) return 0; + sym_lock(); + for (int64_t i = 0; i < n; i++) { + int64_t id = sym_intern_nolock(hashes[i], strs[i], lens[i], true); + if (id < 0) { sym_unlock(); return -1; } + out_ids[i] = id; + } + sym_unlock(); + return 0; +} + /* -------------------------------------------------------------------------- * ray_sym_intern_no_split — persistence-only bulk intern * -------------------------------------------------------------------------- */ diff --git a/src/table/sym.h b/src/table/sym.h index 2f4873282..15418a179 100644 --- a/src/table/sym.h +++ b/src/table/sym.h @@ -122,6 +122,20 @@ static inline int64_t ray_sym_vec_lookup(ray_t* vec, const char* s, size_t n) { * Caller must guarantee single-threaded access. */ int64_t ray_sym_intern_prehashed(uint32_t hash, const char* str, size_t len); +/* Intern n pre-hashed, already-deduplicated strings under a single lock. + * Semantically identical to n ray_sym_intern calls (same ids, same dotted- + * segment caching); it only amortizes the lock round-trip, which matters + * for decoders that intern a whole column at once. Returns 0, or -1 if + * any intern failed, in which case out_ids is only partially filled. */ +int64_t ray_sym_intern_batch(const uint32_t* hashes, const char* const* strs, + const size_t* lens, int64_t n, int64_t* out_ids); + +/* Monotonic counter bumped by ray_sym_init and ray_sym_destroy. A cache + * keyed on sym ids is valid only while this is unchanged: ids are stable + * for the table's lifetime, but a teardown frees the atoms they point at. */ +uint64_t ray_sym_epoch(void); + + /* ---- Dotted name resolution (namespace paths) --------------------------- * A symbol whose name contains one or more '.' is a *dotted* sym. At intern * time we memchr once, split the name on '.', intern each segment, and cache diff --git a/src/vec/vec.c b/src/vec/vec.c index 2b41fed3d..f5be8d985 100644 --- a/src/vec/vec.c +++ b/src/vec/vec.c @@ -385,7 +385,7 @@ ray_t* ray_vec_set(ray_t* vec, int64_t idx, const void* elem) { * dead inline copy in every TU that includes the public header. */ void* ray_data_slice_path(ray_t* v) { return (char*)v->slice_parent->data - + v->slice_offset * ray_type_sizes[(uint8_t)v->type]; + + v->slice_offset * ray_sym_elem_size(v->type, v->attrs); } void* ray_vec_get(ray_t* vec, int64_t idx) { @@ -1541,6 +1541,50 @@ ray_t* ray_embedding_new(int64_t nrows, int32_t dim) { return v; } +/* Width-specific null scan for text vectors (issue #533). SYM nulls are + * id 0 and STR nulls are length 0, so "any null?" is "any zero in the id / + * length lane". The per-row ray_vec_is_null walk cost an out-of-line call + * plus a width dispatch per cell; this reduces each chunk with a branch-free + * OR that the compiler vectorizes, and exits after the first chunk that + * holds a zero. Still O(n): callers that need the proof once per query + * should also cache or skip it where the predicate is null-safe. */ +#define RAY_TEXT_NULL_SCAN(T, ZERO_EXPR) \ + do { \ + const T* p = (const T*)data; \ + int64_t i = 0; \ + for (; i + 256 <= len; i += 256) { \ + unsigned acc = 0; \ + for (int64_t j = i; j < i + 256; j++) acc |= (ZERO_EXPR); \ + if (acc) return true; \ + } \ + unsigned acc = 0; \ + for (int64_t j = i; j < len; j++) acc |= (ZERO_EXPR); \ + return acc != 0; \ + } while (0) + +bool ray_vec_text_has_nulls(const ray_t* v) { + if (!v || RAY_IS_ERR(v)) return false; + int64_t len = v->len, off = 0; + while ((v->attrs & RAY_ATTR_SLICE) && v->slice_parent) { + off += v->slice_offset; + v = v->slice_parent; + } + if (len <= 0) return false; + if (v->type == RAY_STR) { + const ray_str_t* data = (const ray_str_t*)ray_data((ray_t*)v) + off; + RAY_TEXT_NULL_SCAN(ray_str_t, p[j].len == 0); + } + if (v->type != RAY_SYM) return false; + const uint8_t* base = (const uint8_t*)ray_data((ray_t*)v); + switch (v->attrs & RAY_SYM_W_MASK) { + case RAY_SYM_W8: { const void* data = base + off; RAY_TEXT_NULL_SCAN(uint8_t, p[j] == 0); } + case RAY_SYM_W16: { const void* data = base + off * 2; RAY_TEXT_NULL_SCAN(uint16_t, p[j] == 0); } + case RAY_SYM_W32: { const void* data = base + off * 4; RAY_TEXT_NULL_SCAN(uint32_t, p[j] == 0); } + default: { const void* data = base + off * 8; RAY_TEXT_NULL_SCAN(int64_t, p[j] == 0); } + } +} +#undef RAY_TEXT_NULL_SCAN + bool ray_vec_is_null(ray_t* vec, int64_t idx) { if (!vec || RAY_IS_ERR(vec)) return false; if (idx < 0 || idx >= vec->len) return false; diff --git a/src/vec/vec.h b/src/vec/vec.h index a6aff1feb..45c38388b 100644 --- a/src/vec/vec.h +++ b/src/vec/vec.h @@ -53,8 +53,11 @@ static inline bool ray_vec_may_have_nulls(const ray_t* v) { /* Exact admission check for optimizations which require null-free input. * Text columns cannot prove that from attrs; inspect their payload instead. * Keep this out of per-row loops (use may_have_nulls + is_null there). */ +bool ray_vec_text_has_nulls(const ray_t* v); static inline bool ray_vec_has_nulls(const ray_t* v) { if (!ray_vec_may_have_nulls(v)) return false; + if (v->type == RAY_SYM || v->type == RAY_STR) + return ray_vec_text_has_nulls(v); for (int64_t i = 0; i < v->len; i++) if (ray_vec_is_null((ray_t*)v, i)) return true; return false; diff --git a/test/ipc_harness.h b/test/ipc_harness.h new file mode 100644 index 000000000..5f9066dd3 --- /dev/null +++ b/test/ipc_harness.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2025-2026 Anton Kundenko + * All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RAY_TEST_IPC_HARNESS_H +#define RAY_TEST_IPC_HARNESS_H + +/* + * ipc_harness.h -- the one way to stand up an IPC server in tests. + * + * There used to be two: the poll API that production uses + * (ray_ipc_listen on a ray_poll_t) and a second server implementation + * behind ray_ipc_server_t, which despite its header comment did not wrap + * the poll layer at all — it ran its own epoll/kqueue loop, its own + * conns[] array and its own copies of the handshake and the header + * validator. Nothing in src/ used it; only tests did, so every + * wire-level change had to be made twice and the test path could drift + * from the production one silently. + * + * These helpers are `static inline` so each test translation unit gets + * its own copy without unused-function warnings. + */ + +#include "test.h" +#include +#include "core/ipc.h" +#include "core/poll.h" +#include "core/sock.h" +#include "core/runtime.h" +#include "mem/sys.h" +#include "lang/internal.h" +#include +#include +#include +#include + +static inline void ray_test_sleep_ms(long ms) { + struct timespec ts = { .tv_sec = ms / 1000, .tv_nsec = (ms % 1000) * 1000000L }; + nanosleep(&ts, NULL); +} + +static inline uint16_t ray_test_listen_port(ray_sock_t fd) { + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + if (getsockname((int)fd, (struct sockaddr*)&addr, &len) < 0) return 0; + return ntohs(addr.sin_port); +} + +/* A client-side poll must be published on the runtime: IPC handles are + * selector ids resolved there, exactly as main.c does at startup. */ +static inline ray_poll_t* ray_test_client_poll(void) { + ray_poll_t* p = ray_poll_create(); + if (p) ray_runtime_set_poll(p); + return p; +} + +static inline void ray_test_client_poll_done(void) { + ray_poll_t* p = (ray_poll_t*)ray_runtime_get_poll(); + if (p) { ray_runtime_set_poll(NULL); ray_poll_destroy(p); } +} + +typedef struct { + ray_poll_t* poll; + ray_vm_t* vm; + uint16_t port; + ray_thread_t tid; +} ray_test_server_t; + +static inline void ray_test_server_thread(void* arg) { + ray_test_server_t* s = (ray_test_server_t*)arg; + __VM = s->vm; /* TLS VM so ray_eval_str works here */ + ray_poll_run(s->poll); /* returns once poll->code >= 0 */ +} + +/* Stand up a listener on an ephemeral port and run its poll on a thread. + * auth_secret NULL means no -u; restricted mirrors -U. Both must be set + * before ray_ipc_listen, which is why they are parameters rather than + * fields the caller pokes afterwards. + * Returns 0 on success, -1 on failure (caller fails the test). */ +static inline int ray_test_server_start_opts(ray_test_server_t* s, + const char* auth_secret, + bool restricted) { + memset(s, 0, sizeof(*s)); + s->poll = ray_poll_create(); + if (!s->poll) return -1; + if (auth_secret) { + size_t n = strlen(auth_secret); + if (n >= sizeof(s->poll->auth_secret)) return -1; + memcpy(s->poll->auth_secret, auth_secret, n + 1); + } + s->poll->restricted = restricted; + int64_t id = ray_ipc_listen(s->poll, 0); + if (id < 0) return -1; + ray_selector_t* sel = ray_poll_get(s->poll, id); + if (!sel) return -1; + s->port = ray_test_listen_port((ray_sock_t)sel->fd); + if (s->port == 0) return -1; + + s->vm = (ray_vm_t*)ray_sys_alloc(sizeof(ray_vm_t)); + if (!s->vm) return -1; + ray_vm_init(s->vm, 99); + + ray_thread_create(&s->tid, ray_test_server_thread, s); + ray_test_sleep_ms(20); /* let the thread reach poll_run */ + return 0; +} + +static inline int ray_test_server_start(ray_test_server_t* s) { + return ray_test_server_start_opts(s, NULL, false); +} + +/* ray_poll_run blocks in epoll_wait, so setting the exit code is not + * enough — connect a throwaway socket to generate an accept event that + * wakes it. */ +static inline void ray_test_server_stop(ray_test_server_t* s) { + ray_poll_exit(s->poll, 0); + ray_sock_t k = ray_sock_connect("127.0.0.1", s->port, 200); + if (k != RAY_INVALID_SOCK) ray_sock_close(k); + ray_thread_join(s->tid); + ray_poll_destroy(s->poll); + ray_sys_free(s->vm); + memset(s, 0, sizeof(*s)); +} + +#define RAY_TEST_SERVER_START(s) \ + TEST_ASSERT_EQ_I(ray_test_server_start(&(s)), 0) + +#define RAY_TEST_SERVER_START_OPTS(s, secret, restricted) \ + TEST_ASSERT_EQ_I(ray_test_server_start_opts(&(s), (secret), (restricted)), 0) + +#endif /* RAY_TEST_IPC_HARNESS_H */ diff --git a/test/main.c b/test/main.c index 7c6149d99..21d94af98 100644 --- a/test/main.c +++ b/test/main.c @@ -123,6 +123,7 @@ extern const test_entry_t fvec_entries[]; extern const test_entry_t graph_entries[]; extern const test_entry_t agg_registry_entries[]; extern const test_entry_t agg_engine_entries[]; +extern const test_entry_t agg_contract_entries[]; extern const test_entry_t graph_builtin_entries[]; extern const test_entry_t group_extra_entries[]; extern const test_entry_t group_pushdown_entries[]; @@ -181,6 +182,7 @@ static const test_entry_t* const compiled_groups[] = { format_entries, fvec_entries, graph_entries, graph_builtin_entries, agg_registry_entries, agg_engine_entries, + agg_contract_entries, group_extra_entries, group_pushdown_entries, fused_topk_entries, diff --git a/test/rfl/agg/pearson_degenerate.rfl b/test/rfl/agg/pearson_degenerate.rfl new file mode 100644 index 000000000..6f5724a69 --- /dev/null +++ b/test/rfl/agg/pearson_degenerate.rfl @@ -0,0 +1,28 @@ +;; A correlation coefficient is bounded by |r| <= 1. A group with no +;; variance on either side has no defined correlation, and must come back +;; null rather than as a fabricated finite number (#555). +;; +;; pearson_final_result computed num / sqrt(dx*dy) with no guard. dx and dy +;; are >= 0 mathematically (Cauchy-Schwarz) but a CONSTANT column cancels to +;; a small NEGATIVE residue in doubles. With one side negative the product +;; is negative, sqrt is NaN and the null model caught it; with BOTH negative +;; the product is positive, sqrt is finite, and garbage was emitted. +(set N 400) (set r (til N)) (set k (% r 4)) +(set c (as 'F64 (take [10000001.0] N))) +(set c2 (as 'F64 (take [20000003.0] N))) + +;; both sides constant and identical: used to give -1.0 +(all (== (at (select {from:(table [k x y] (list k c c)) by:k p:(pearson_corr x y)}) 'p) 0Nf)) -- true + +;; two different constants: used to give 2.13 — not a correlation at all +(all (== (at (select {from:(table [k x y] (list k c c2)) by:k p:(pearson_corr x y)}) 'p) 0Nf)) -- true + +;; one side constant, the other varying: still undefined +(all (== (at (select {from:(table [k x y] (list k c (as 'F64 r))) by:k p:(pearson_corr x y)}) 'p) 0Nf)) -- true + +;; a single-row group has no correlation either (n < 2) +(at (select {from:(table [k x y] (list [0] (as 'F64 [5.0]) (as 'F64 [7.0]))) by:k p:(pearson_corr x y)}) 'p) -- [0Nf] + +;; ...and the ordinary cases are untouched +(at (select {from:(table [k x y] (list k (as 'F64 r) (as 'F64 (* 2 r)))) by:k p:(pearson_corr x y)}) 'p) -- [1.0 1.0 1.0 1.0] +(at (select {from:(table [k x y] (list k (as 'F64 r) (as 'F64 (* -3 r)))) by:k p:(pearson_corr x y)}) 'p) -- [-1.0 -1.0 -1.0 -1.0] diff --git a/test/rfl/agg/variance_large_magnitude.rfl b/test/rfl/agg/variance_large_magnitude.rfl new file mode 100644 index 000000000..f33d29208 --- /dev/null +++ b/test/rfl/agg/variance_large_magnitude.rfl @@ -0,0 +1,40 @@ +;; Variance is translation-invariant: shifting every value by a constant +;; must not change var or stddev. The grouped accumulator computed +;; sumsq/cnt - mean*mean in one pass, which cancels catastrophically once +;; values reach ~1e9 — the clamp then turned the negative residue into a +;; clean 0.0, so the wrong answer looked plausible (#554). +;; +;; The oracle here is the identity itself, so the test needs no reference +;; implementation: the same spread at three magnitudes must agree. +(set N 1000) (set r (til N)) (set k (% r 4)) +(set mk (fn [base] (table [k v] (list k (as 'F64 (+ base (% r 7))))))) +(set v0 (at (select {from:(mk 0) by:k s:(var v)}) 's)) +(set v6 (at (select {from:(mk 1000000) by:k s:(var v)}) 's)) +(set v9 (at (select {from:(mk 2000000000) by:k s:(var v)}) 's)) +(all (< (abs (- v0 v6)) 0.000001)) -- true +(all (< (abs (- v0 v9)) 0.000001)) -- true +;; and it is the real variance, not merely self-consistent +(all (< (abs (- v0 4.0)) 0.05)) -- true + +;; stddev likewise +(set s0 (at (select {from:(mk 0) by:k s:(stddev v)}) 's)) +(set s9 (at (select {from:(mk 2000000000) by:k s:(stddev v)}) 's)) +(all (< (abs (- s0 s9)) 0.000001)) -- true + +;; integer inputs: the sum of squares used to wrap in an int64, so I32 +;; near 2^31 overflowed after two rows +(set i0 (at (select {from:(table [k v] (list k (as 'I32 (% r 7)))) by:k s:(var v)}) 's)) +(set i9 (at (select {from:(table [k v] (list k (as 'I32 (+ 2000000000 (% r 7)))))by:k s:(var v)}) 's)) +(all (< (abs (- i0 i9)) 0.000001)) -- true + +(set l0 (at (select {from:(table [k v] (list k (as 'I64 (% r 7)))) by:k s:(var v)}) 's)) +(set l9 (at (select {from:(table [k v] (list k (as 'I64 (+ 4000000000 (% r 7)))))by:k s:(var v)}) 's)) +(all (< (abs (- l0 l9)) 0.000001)) -- true + +;; TIMESTAMP is ns since 2000, so values are ~8e17 — the worst case +(set t0 (at (select {from:(table [k v] (list k (as 'TIMESTAMP (* (% r 7) 1000000)))) by:k s:(var v)}) 's)) +(set t9 (at (select {from:(table [k v] (list k (as 'TIMESTAMP (+ 800000000000000000 (* (% r 7) 1000000))))) by:k s:(var v)}) 's)) +(all (< (abs (- t0 t9)) 1.0)) -- true + +;; a zero-variance group is still exactly zero, not noise +(at (select {from:(table [k v] (list k (as 'F64 (take [2000000000.0] N)))) by:k s:(var v)}) 's) -- [0.0 0.0 0.0 0.0] diff --git a/test/rfl/group/count_distinct_vector_by.rfl b/test/rfl/group/count_distinct_vector_by.rfl new file mode 100644 index 000000000..aeb771ed6 --- /dev/null +++ b/test/rfl/group/count_distinct_vector_by.rfl @@ -0,0 +1,36 @@ +;; count (distinct x) with a composite key written as a symbol vector, +;; `by: [k1 k2]`, must give the same answer as the dict form and as a +;; per-group recomputation, with and without where / desc / take, at a size +;; past the fused kernel's row floor so that path is the one exercised. +(set N 300000) +(set I (til N)) +(set P (as 'SYMBOL (map (fn [k] (format "p%" k)) (til 50)))) +(set M (as 'SYMBOL (map (fn [k] (if (== k 0) "" (format "m%" k))) (til 300)))) +(set T (table [a b u] (list (at P (% (* 7 I) 50)) (at M (% (* 13 I) 300)) (% (* 7919 I) 51000)))) +(set V (select {from: T by: [a b] u: (count (distinct u))})) +(set D (select {from: T by: {a: a b: b} u: (count (distinct u))})) +(count V) -- (count D) +(sum (at V 'u)) -- (sum (at D 'u)) +(cols V) -- ['a 'b 'u] +;; per-group oracle on a few groups +(set ORA (fn [aa bb] (count (distinct (at (select {from: T where: (and (== a aa) (== b bb))}) 'u))))) +(set CHK (fn [i] (== (at (at V 'u) i) (ORA (at (at V 'a) i) (at (at V 'b) i))))) +(all (map CHK (list 0 1 2 (div (count V) 3) (- (count V) 1)))) -- true +;; where + desc + take (the ClickBench Q12 shape): the ten rows must carry +;; the ten largest counts of the full, filtered result — ties past the cut +;; may pick either group, so the keys are checked through their counts +(set VW (select {from: T by: [a b] u: (count (distinct u)) where: (!= b "") desc: u take: 10})) +(set FULL (select {from: T by: [a b] u: (count (distinct u)) where: (!= b "")})) +(count VW) -- 10 +(at VW 'u) -- (at (desc (at FULL 'u)) (til 10)) +(count (select {from: VW where: (== b "")})) -- 0 +(set CHKW (fn [i] (== (at (at VW 'u) i) (ORA (at (at VW 'a) i) (at (at VW 'b) i))))) +(all (map CHKW (til 10))) -- true +;; three keys +(set C (% (* 3 I) 4)) +(set T3 (table [a b c u] (list (at P (% (* 7 I) 50)) (at M (% (* 13 I) 300)) C (% (* 7919 I) 51000)))) +(set V3 (select {from: T3 by: [a b c] u: (count (distinct u))})) +(set D3 (select {from: T3 by: {a: a b: b c: c} u: (count (distinct u))})) +(count V3) -- (count D3) +(sum (at V3 'u)) -- (sum (at D3 'u)) +(cols V3) -- ['a 'b 'c 'u] diff --git a/test/rfl/group/derived_key_sym_domain.rfl b/test/rfl/group/derived_key_sym_domain.rfl new file mode 100644 index 000000000..d89b4c4ba --- /dev/null +++ b/test/rfl/group/derived_key_sym_domain.rfl @@ -0,0 +1,86 @@ +;; A group key computed from ONE symbol column (substr / str-find / if over +;; it) must give the same groups and aggregates whether the engine evaluates +;; the expression per row or once per distinct symbol. Oracle: the key +;; materialised as a column first, then a plain group-by on that column. +;; Sizes past 4096 rows with few distinct symbols so the per-symbol path is +;; taken; a high-cardinality control and a null-bearing column exercise the +;; fallback. +(set N 20000) +(set i (til N)) +(set hosts (map (fn [k] (format "h%.example.com" k)) (til 37))) +(set mk (fn [k] ((fn [j] (if (== 0 (% j 11)) "" (if (== 0 (% j 7)) (format "https://www.%/p/%" (at hosts (% (* 31 j) 37)) (% j 13)) (if (== 0 (% j 5)) (at hosts (% (* 31 j) 37)) (format "http://%/a/%" (at hosts (% (* 31 j) 37)) (% j 17)))))) (% k 500)))) +(set ref (as 'SYMBOL (map mk i))) +(set v (as 'F64 (% (* i 7) 101))) +(set T (table [ref v] (list ref v))) + +;; the derived key, as the engine sees it inside by: +(set KEYQ (fn [t] (select {from: t by: (let p (str-find ref "://") (let s (substr ref (+ p 4) -1) (let r (if (== (str-find s "www.") 0) (substr s 5 -1) s) (let sl (str-find r "/") (if (and (within p [4 5]) (== (substr ref 1 4) "http") (not (nil? sl))) (substr r 1 sl) ref))))) c: (count ref) sv: (sum v) mn: (min ref) where: (!= ref "")}))) +;; oracle: the same expression as a projected column, then group by it +(set KEYCOL (fn [t] (select {from: t p: (let p (str-find ref "://") (let s (substr ref (+ p 4) -1) (let r (if (== (str-find s "www.") 0) (substr s 5 -1) s) (let sl (str-find r "/") (if (and (within p [4 5]) (== (substr ref 1 4) "http") (not (nil? sl))) (substr r 1 sl) ref))))) ref: ref v: v where: (!= ref "")}))) +(set ORA (fn [t] (select {from: (KEYCOL t) by: p c: (count ref) sv: (sum v) mn: (min ref)}))) +(set FP (fn [r] (ser (xasc (xasc (table [k c sv mn] (list (at (value r) 0) (at r 'c) (at r 'sv) (at r 'mn))) 'mn) 'c)))) +(set FP2 (fn [r] (ser (xasc (xasc (table [k c] (list (at (value r) 0) (at r 'c))) 'k) 'c)))) + +(set R (KEYQ T)) +(set O (ORA T)) +(count R) -- 37 +(count O) -- 37 +(all (== (FP R) (FP O))) -- true +;; every host appears (the "" rows are filtered, the bare-host rows keep it) +(count (distinct (at (value R) 0))) -- 37 + +;; a key that stays a symbol of the column (identity-like) still matches +(set R2 (select {from: T by: (if (== (str-find ref "www.") 0) (substr ref 4 -1) ref) c: (count ref)})) +(set O2 (select {from: (select {from: T k: (if (== (str-find ref "www.") 0) (substr ref 4 -1) ref) ref: ref}) by: k c: (count ref)})) +(all (== (FP2 R2) (FP2 O2))) -- true + +;; high cardinality: every row its own symbol — the fallback path, same answer +(set refu (as 'SYMBOL (map (fn [k] (format "http://%/u/%" (at hosts (% k 37)) k)) i))) +(set TU (table [ref v] (list refu v))) +(count (KEYQ TU)) -- 37 +(all (== (FP (KEYQ TU)) (FP (ORA TU)))) -- true + +;; nulls in the symbol column: rows with a null key group together, others unchanged +(set refn (as 'SYMBOL (map (fn [k] (if (== 0 (% k 97)) null (mk k))) i))) +(set TN (table [ref v] (list refn v))) +(set RN (KEYQ TN)) +(set ON (ORA TN)) +(== (count RN) (count ON)) -- true +(all (== (FP RN) (FP ON))) -- true + +;; a second column referenced in the key expression must not take the shortcut +(set R3 (select {from: T by: (if (> v 50) (substr ref 0 4) ref) c: (count ref)})) +(set O3 (select {from: (select {from: T k: (if (> v 50) (substr ref 0 4) ref) ref: ref}) by: k c: (count ref)})) +(all (== (FP2 R3) (FP2 O3))) -- true + +;; the key column is named by the same rule whether or not the per-symbol +;; path ran: below 4096 rows the row-wise path names it identically +(== (at (cols R) 0) 'p) -- true +(== (at (cols (KEYQ (take T 3000))) 0) 'p) -- true +(== (at (cols (select {from: T by: (substr ref 0 3) c: (count v)})) 0) 'ref) -- true +(== (at (cols (select {from: (take T 3000) by: (substr ref 0 3) c: (count v)})) 0) 'ref) -- true + +;; positional operators are NOT functions of the symbol alone: they must be +;; evaluated over the rows, never over the distinct symbols +(set RD (select {from: T by: (differ ref) c: (count v)})) +(set OD (select {from: (select {from: T k: (differ ref) v: v}) by: k c: (count v)})) +(all (== (FP2 RD) (FP2 OD))) -- true +(set RF (select {from: TN by: (fills ref) c: (count v)})) +(set OF (select {from: (select {from: TN k: (fills ref) v: v}) by: k c: (count v)})) +(all (== (FP2 RF) (FP2 OF))) -- true + +;; a free name that is a table-length global vector, not a column +(set G (as 'I64 (% (* i 13) 7))) +(set RG (select {from: T by: (if (> G 3) (substr ref 0 4) ref) c: (count v)})) +(set OG (select {from: (select {from: T k: (if (> G 3) (substr ref 0 4) ref) v: v}) by: k c: (count v)})) +(all (== (FP2 RG) (FP2 OG))) -- true + +;; the derived name yields to an aggregate of the same name +(== (cols (select {from: T by: (substr ref 0 2) ref: (min ref) c: (count v)})) ['key 'ref 'c]) -- [true true true] +;; a quoted symbol that names a column of the table resolves to that column +;; in the compiled key — such a key is not a function of the symbol alone +(set RQ (select {from: T by: (if (== 'v ref) "same" "diff") c: (count v)})) +(set OQ (select {from: (select {from: T k: (if (== 'v ref) "same" "diff") v: v}) by: k c: (count v)})) +(count RQ) -- 1 +(== (at (value RQ) 0) (at (value OQ) 0)) -- [true] +(== (at RQ 'c) (at OQ 'c)) -- [true] diff --git a/test/rfl/group/empty_aggregate_columns.rfl b/test/rfl/group/empty_aggregate_columns.rfl new file mode 100644 index 000000000..bf254fe90 --- /dev/null +++ b/test/rfl/group/empty_aggregate_columns.rfl @@ -0,0 +1,35 @@ +;; No output groups: retain known result types across mixed query routes. +(set t (table [k v] (list ["a" "b"] (as 'TIME [1 2])))) +(set z (select {from:t by:k where:(== k "missing") s:(sum v) m:(med v) n:(count (distinct v)) f:(first v)})) +(count z) -- 0 +(type (at z 'k)) -- 'STR +(type (at z 's)) -- 'TIME +(type (at z 'm)) -- 'F64 +(type (at z 'n)) -- 'I64 +(type (at z 'f)) -- 'TIME +(set e (table [k v] (list (as 'I64 []) (as 'TIME [])))) +(set z (select {from:e by:k m:(med v) d:(dev v) a:(avg v)})) +(count z) -- 0 +(type (at z 'm)) -- 'F64 +(type (at z 'd)) -- 'F64 +(type (at z 'a)) -- 'F64 + +;; Aliases take the generic unary route. Empty LIST aggregates reject direct +;; invocation, but zero groups must not invoke an aggregate semantically. +(set empty_avg avg) +(set empty_min min) +(set empty_max max) +(set t (table [k v] (list ["a" "b"] (list [1 2] [3 4])))) +(set z (select {from:t by:k where:(== k "missing") a:(empty_avg v) lo:(empty_min v) hi:(empty_max v)})) +(count z) -- 0 +(type (at z 'a)) -- 'LIST +(type (at z 'lo)) -- 'LIST +(type (at z 'hi)) -- 'LIST +(set z (select {from:t by:k where:(== k "missing") c:(count k) a:(empty_avg v)})) +(count z) -- 0 +(type (at z 'a)) -- 'LIST +;; Inference still succeeds for an alias with a typed source. +(set empty_med med) +(set t (table [k v] (list [1 2] (as 'TIME [1 2])))) +(set z (select {from:t by:k where:(< k 0) m:(empty_med v)})) +(type (at z 'm)) -- 'F64 diff --git a/test/rfl/group/fast_path_types.rfl b/test/rfl/group/fast_path_types.rfl new file mode 100644 index 000000000..023b60492 --- /dev/null +++ b/test/rfl/group/fast_path_types.rfl @@ -0,0 +1,21 @@ +(set f32 (fn [x] (at (select {from:(table [v] (list x)) v:(as 'F32 v)}) 'v))) +;; Type coverage across streaming, row-index, expression, and sort paths. +(set ft (table [k v] (list (as 'I32 [0N 0N 0 0 1 1]) (as 'TIME [9 3 8 2 7 1])))) +(at (select {from:ft by:k asc:k s:(min v)}) 's) -- (as 'TIME [3 2 1]) +(count (select {from:ft by:k s:(min v) where:(== k 0N)})) -- 1 +(at (select {from:ft by:k s:(min v) where:(== k 0N)}) 's) -- (as 'TIME [3]) +(set exact (table [k v] (list [0 0 1 1] [9007199254740993 9007199254740992 9223372036854775807 9223372036854775806]))) +(at (select {from:exact by:k asc:k m:(min v)}) 'm) -- [9007199254740992 9223372036854775806] +(at (select {from:exact by:k asc:k m:(max v)}) 'm) -- [9007199254740993 9223372036854775807] +(set mixed (table [k v] (list [0 1 0 1 0 1] (f32 [3.0 9.0 1.0 7.0 2.0 8.0])))) +(at (select {from:mixed by:k asc:k m:(med v) s:(sum v) f:(first v) l:(last v)}) 'm) -- [2.0 8.0] +(at (select {from:mixed by:k asc:k m:(med v) s:(sum v) f:(first v) l:(last v)}) 'f) -- [3.0 9.0] +(at (select {from:mixed by:k asc:k t:(top v 2)}) 't) -- (list (f32 [3.0 2.0]) (f32 [9.0 8.0])) +(set words (table [k v] (list [0 0 0 1 1 1] ["long pooled zebra string" "a" "m" "z" "b" "y"]))) +(at (select {from:words by:k asc:k t:(top v 2)}) 't) -- (list ["m" "long pooled zebra string"] ["z" "y"]) +(at (select {from:words by:k asc:k t:(bot v 1)}) 't) -- (list ["a"] ["b"]) +(set tm (table [k a b] (list [0 0 1 1] (as 'TIME [1 2 3 4]) (as 'TIME [5 7 10 12])))) +(at (select {from:tm by:k asc:k s:(sum (- b a))}) 's) -- (as 'TIME [9 15]) +(set fc (table [k v] (list (f32 [0.0 -0.0 1.0 1.0]) [1 1 2 3]))) +(at (select {from:fc by:k asc:k n:(count (distinct v))}) 'n) -- [1 2] +(at (select {from:fc by:k asc:k n:(count v) lit:(at (f32 [1.5]) 0)}) 'lit) -- (f32 [1.5 1.5]) diff --git a/test/rfl/group/fused_count_distinct_nulls.rfl b/test/rfl/group/fused_count_distinct_nulls.rfl new file mode 100644 index 000000000..9696264f2 --- /dev/null +++ b/test/rfl/group/fused_count_distinct_nulls.rfl @@ -0,0 +1,64 @@ +;; Fused grouped count-distinct (ray_cd_fused), on the paths whose +;; null-admission guard was removed. +;; +;; cdfuse.c used to decline a nullable key or value outright: +;; if (ray_vec_may_have_nulls(key_col) || ray_vec_may_have_nulls(val_col)) +;; return NULL; +;; and cdf_type_ok has since widened to BOOL/U8/DATE/TIME/TIMESTAMP/F32/F64. +;; try_count_distinct_v2_rewrite dropped the matching ray_vec_has_nulls +;; checks on K and X. +;; +;; wide_count_distinct.rfl cannot reach any of that: it uses (til 131072), +;; half of CDF_MIN_ROWS (262144), and STR/GUID columns, which cdf_type_ok +;; does not accept. So the fused kernel was never entered by any test. +;; Everything below is >= CDF_MIN_ROWS and typed so the kernel admits it. +;; +;; Four groups over an eight-value cycle, so each group sees exactly two +;; of the vocabulary: +;; g0 -> 0Nf, 0Nf one distinct (all-null group holds one value) +;; g1 -> 1.5, 1.5 one distinct +;; g2 -> -0.0, 0.0 one distinct (cdf_read canonicalises -0.0 to 0) +;; g3 -> 2.5, 3.5 two distinct +(set N 262144) +(set r (til N)) +(set g (% r 4)) +(set vals [0Nf 1.5 -0.0 2.5 0Nf 1.5 0.0 3.5]) +(set v (at vals (% r 8))) + +;; I64 key, nullable F64 value +(set t (table [k x] (list g v))) +(set a (select {from:t by:k n:(count (distinct x))})) +(at a 'k) -- [0 1 2 3] +(at a 'n) -- [1 1 1 2] + +;; SYM key, same values — the other key family the kernel packs +(set t2 (table [k x] (list (at ['g0 'g1 'g2 'g3] g) v))) +(set b (select {from:t2 by:k n:(count (distinct x))})) +(at b 'k) -- ['g0 'g1 'g2 'g3] +(at b 'n) -- [1 1 1 2] + +;; nullable KEY column: the null key is a group of its own +(set t3 (table [k x] (list (at [0N 1 2 3] g) v))) +(set c (select {from:t3 by:k n:(count (distinct x))})) +(at c 'n) -- [1 1 1 2] +(count c) -- 4 + +;; NaN reaches the kernel as the F64 null: it must not read as a separate +;; distinct value from an explicit 0Nf (g0), but must still be distinct +;; from a real value (g2). +(set nan (% 0.0 0.0)) +(set v2 (at (list nan 1.5 nan 2.5 0Nf 1.5 7.5 3.5) (% r 8))) +(set t4 (table [k x] (list g v2))) +(set d (select {from:t4 by:k n:(count (distinct x))})) +(at d 'n) -- [1 1 2 2] + +;; NOTE: the F32 arm of the widened cdf_type_ok is NOT covered here. +;; `as` has no F32 target (src/ops/builtins.c ~1322 lists I64/I32/I16/U8/ +;; F64/BOOL/SYMBOL/DATE/TIME/TIMESTAMP/GUID/STR only), so an F32 column +;; cannot be built from Rayfall at all. Covering it needs either an F32 +;; cast target or a C-level test. + +;; a count-distinct beside another aggregate still rewrites correctly +(set f (select {from:t by:k n:(count (distinct x)) c:(count x)})) +(at f 'n) -- [1 1 1 2] +(at f 'c) -- [65536 65536 65536 65536] diff --git a/test/rfl/group/pivot_first_seen_order.rfl b/test/rfl/group/pivot_first_seen_order.rfl new file mode 100644 index 000000000..1fee6604a --- /dev/null +++ b/test/rfl/group/pivot_first_seen_order.rfl @@ -0,0 +1,17 @@ +;; pivot emits its index rows and pivot columns in first-seen order, whatever +;; the parallel ingest's partitioning did. Past the parallel threshold the +;; old code walked the hash partitions in task-claim order, so the column +;; order differed between worker counts and between runs of one binary. +(set N 200000) +(set T (table [r c v] (list (% (til N) 10) (% (* 7 (til N)) 13) (til N)))) +(set P (pivot T 'r 'c 'v sum)) +(at P 'r) -- (til 10) +(cols P) -- ['r '0 '7 '1 '8 '2 '9 '3 '10 '4 '11 '5 '12 '6] +;; a shape whose first-seen order is not ascending +(set T2 (table [r c v] (list (% (* 7919 (til N)) 1000) (% (- N (til N)) 3) (til N)))) +(set P2 (pivot T2 'r 'c 'v sum)) +(at (at P2 'r) (til 6)) -- [0 919 838 757 676 595] +(cols P2) -- ['r '2 '1 '0] +;; the small (serial) path agrees +(set S (table [r c v] (list (% (til 100) 10) (% (* 7 (til 100)) 13) (til 100)))) +(cols (pivot S 'r 'c 'v sum)) -- (cols P) diff --git a/test/rfl/group/radix_key_emit_parallel.rfl b/test/rfl/group/radix_key_emit_parallel.rfl index 8056f4581..6e37ae7e5 100644 --- a/test/rfl/group/radix_key_emit_parallel.rfl +++ b/test/rfl/group/radix_key_emit_parallel.rfl @@ -12,9 +12,12 @@ ;; 1. WIDE-SYM — 200k singleton groups on a W32 SYM key: the widest ;; emit shape (and the one the 100M q13 profile is about). 200k is ;; enough to clear the dispatch threshold AND still land W32 SYM; -;; every group is a singleton in til() order, so the emitted key -;; column must equal the SOURCE column element-for-element — a -;; per-row oracle, not a spot check. +;; the original single-key fixture also covers dense admission. An +;; additional alternating second key exceeds the input row domain and +;; preserves radix coverage as dense memory admission improves. +;; Every group is a singleton. After normalizing the single-key +;; result by its unique numeric row value, its key column must equal +;; the SOURCE element-for-element — a per-row oracle, not a spot check. ;; 2. WIDE-KEY — 17 keys of MIXED width (I64/I32/I16/U8/BOOL/DATE/ ;; TIME/TIMESTAMP/SYM + I64 constants) at 100k groups. Two things ;; at once: the packed-key stride (a per-column index error shows up @@ -41,12 +44,24 @@ (set KS (as 'SYM (til NS))) (set TS (table [k v] (list KS (til NS)))) (set RS (select {c: (count v) s: (sum v) from: TS by: k})) +;; Group output order is unspecified. Normalize AFTER the original query, +;; using its unique numeric row value, then verify every source key and value. +;; This does not change the query plan or hide a wrong key/value association. +(set RS (xasc RS 's)) (count RS) -- 200000 -;; singleton groups in first-seen order => the key column IS the source column (all (== (at RS 'k) KS)) -- true (all (== (at RS 'c) (+ 1 (* 0 (til NS))))) -- true (all (== (at RS 's) (til NS))) -- true +;; Additional radix shape; keep the original single-key oracle above. +(set TSR (table [k v tag] (list KS (til NS) (% (til NS) 2)))) +(set RSR (select {c: (count v) s: (sum v) from: TSR by: {k:k tag:tag}})) +(count RSR) -- 200000 +(all (== (at RSR 'k) KS)) -- true +(all (== (at RSR 'tag) (% (til NS) 2))) -- true +(all (== (at RSR 'c) (+ 1 (* 0 (til NS))))) -- true +(all (== (at RSR 's) (til NS))) -- true + ;; ---------- 2. seventeen keys, mixed widths, 100k groups ---------- (set N 100000) (set K0 (til N)) diff --git a/test/rfl/group/rank_narrow_types.rfl b/test/rfl/group/rank_narrow_types.rfl new file mode 100644 index 000000000..8bc52ab2c --- /dev/null +++ b/test/rfl/group/rank_narrow_types.rfl @@ -0,0 +1,25 @@ +;; Grouped BOOL/F32 med and quantile share the rank kernel. Check values, +;; output types, nulls, selections and parameter validation end to end. +(set B (table [k v] (list [0 0 1 1] [false true true true]))) +(set BM (select {from:B by:k m:(med v) q:(quantile v 0.25) asc:k})) +(at BM 'm) -- [0.5 1.0] +(at BM 'q) -- [0.25 1.0] +(type (at BM 'm)) -- 'F64 +(at (select {from:B by:k m:(med v) where:(== k 0)}) 'm) -- [0.5] + +(set D (table [k v] (list [0 0 1 1 2 2] [1.25 3.25 0N 4.5 0N 0N]))) +(set F (select {from:D k:k v:(as 'F32 v)})) +(type (at F 'v)) -- 'F32 +(set FM (select {from:F by:k m:(med v) q:(quantile v 0.25) asc:k})) +(at FM 'm) -- [2.25 4.5 0N] +(at FM 'q) -- [1.75 4.5 0N] +(type (at FM 'm)) -- 'F64 +(at (select {from:F by:k m:(med v) where:(< k 2) asc:k}) 'm) -- [2.25 4.5] +(at (select {from:F m:(med v)}) 'm) -- [3.25] +(at (select {from:F by:k q:(percentile v 25) asc:k}) 'q) -- [1.75 4.5 0N] +(count (select {from:F by:k m:(med v) where:(< k 0)})) -- 0 +(select {from:F by:k q:(quantile v 2.0)}) !- domain + +;; Adding another aggregate must not change rank semantics. +(at (select {from:F by:k m:(med v) n:(count v) lo:(min v) asc:k}) 'm) -- [2.25 4.5 0N] +(at (select {from:B by:k m:(med v) n:(count v) a:(any v) asc:k}) 'm) -- [0.5 1.0] diff --git a/test/rfl/group/wide_count_distinct.rfl b/test/rfl/group/wide_count_distinct.rfl new file mode 100644 index 000000000..ecbea9cf4 --- /dev/null +++ b/test/rfl/group/wide_count_distinct.rfl @@ -0,0 +1,138 @@ +;; Full-value equality must survive the count-distinct query rewrite. +;; Every four rows contain (A,x), (A,y), (B,x), (B,x). +;; The vocabulary differs only after a long shared prefix. +(set r (til 131072)) +(set ki (at [0 0 1 1] (% r 4))) +(set vi (at [0 1 0 0] (% r 4))) + +;; STR keys, STR values: pure rewrite, mixed aggregation, and selection. +(set keys ["same sixteen byte prefix alpha" "same sixteen byte prefix omega"]) +(set vals ["same sixteen byte prefix alpha" "same sixteen byte prefix omega"]) +(set t (table [k v i] (list (at keys ki) (at vals vi) r))) +(set result (select {from:t by:k n:(count (distinct v))})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(set result (select {from:t by:k n:(count (distinct v)) c:(count i)})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(at result 'c) -- [65536 65536] +(at (select {from:t by:k n:(count (distinct v)) where:(!= (% i 4) 1)}) 'n) -- [1 1] + +;; STR keys, GUID values: pure rewrite, mixed aggregation, and selection. +(set keys ["same sixteen byte prefix alpha" "same sixteen byte prefix omega"]) +(set vals (as 'GUID (list "00000000-0000-0000-0000-000000000001" "00000000-0000-0000-0000-000000000002"))) +(set t (table [k v i] (list (at keys ki) (at vals vi) r))) +(set result (select {from:t by:k n:(count (distinct v))})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(set result (select {from:t by:k n:(count (distinct v)) c:(count i)})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(at result 'c) -- [65536 65536] +(at (select {from:t by:k n:(count (distinct v)) where:(!= (% i 4) 1)}) 'n) -- [1 1] + +;; STR keys, LIST values: pure rewrite, mixed aggregation, and selection. +(set keys ["same sixteen byte prefix alpha" "same sixteen byte prefix omega"]) +(set vals (list [11 22 33 44 55] [11 22 33 44 66])) +(set t (table [k v i] (list (at keys ki) (at vals vi) r))) +(set result (select {from:t by:k n:(count (distinct v))})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(set result (select {from:t by:k n:(count (distinct v)) c:(count i)})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(at result 'c) -- [65536 65536] +(at (select {from:t by:k n:(count (distinct v)) where:(!= (% i 4) 1)}) 'n) -- [1 1] + +;; GUID keys, STR values: pure rewrite, mixed aggregation, and selection. +(set keys (as 'GUID (list "00000000-0000-0000-0000-000000000001" "00000000-0000-0000-0000-000000000002"))) +(set vals ["same sixteen byte prefix alpha" "same sixteen byte prefix omega"]) +(set t (table [k v i] (list (at keys ki) (at vals vi) r))) +(set result (select {from:t by:k n:(count (distinct v))})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(set result (select {from:t by:k n:(count (distinct v)) c:(count i)})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(at result 'c) -- [65536 65536] +(at (select {from:t by:k n:(count (distinct v)) where:(!= (% i 4) 1)}) 'n) -- [1 1] + +;; GUID keys, GUID values: pure rewrite, mixed aggregation, and selection. +(set keys (as 'GUID (list "00000000-0000-0000-0000-000000000001" "00000000-0000-0000-0000-000000000002"))) +(set vals (as 'GUID (list "00000000-0000-0000-0000-000000000001" "00000000-0000-0000-0000-000000000002"))) +(set t (table [k v i] (list (at keys ki) (at vals vi) r))) +(set result (select {from:t by:k n:(count (distinct v))})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(set result (select {from:t by:k n:(count (distinct v)) c:(count i)})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(at result 'c) -- [65536 65536] +(at (select {from:t by:k n:(count (distinct v)) where:(!= (% i 4) 1)}) 'n) -- [1 1] + +;; GUID keys, LIST values: pure rewrite, mixed aggregation, and selection. +(set keys (as 'GUID (list "00000000-0000-0000-0000-000000000001" "00000000-0000-0000-0000-000000000002"))) +(set vals (list [11 22 33 44 55] [11 22 33 44 66])) +(set t (table [k v i] (list (at keys ki) (at vals vi) r))) +(set result (select {from:t by:k n:(count (distinct v))})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(set result (select {from:t by:k n:(count (distinct v)) c:(count i)})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(at result 'c) -- [65536 65536] +(at (select {from:t by:k n:(count (distinct v)) where:(!= (% i 4) 1)}) 'n) -- [1 1] + +;; LIST keys, STR values: pure rewrite, mixed aggregation, and selection. +(set keys (list [11 22 33 44 55] [11 22 33 44 66])) +(set vals ["same sixteen byte prefix alpha" "same sixteen byte prefix omega"]) +(set t (table [k v i] (list (at keys ki) (at vals vi) r))) +(set result (select {from:t by:k n:(count (distinct v))})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(set result (select {from:t by:k n:(count (distinct v)) c:(count i)})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(at result 'c) -- [65536 65536] +(at (select {from:t by:k n:(count (distinct v)) where:(!= (% i 4) 1)}) 'n) -- [1 1] + +;; LIST keys, GUID values: pure rewrite, mixed aggregation, and selection. +(set keys (list [11 22 33 44 55] [11 22 33 44 66])) +(set vals (as 'GUID (list "00000000-0000-0000-0000-000000000001" "00000000-0000-0000-0000-000000000002"))) +(set t (table [k v i] (list (at keys ki) (at vals vi) r))) +(set result (select {from:t by:k n:(count (distinct v))})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(set result (select {from:t by:k n:(count (distinct v)) c:(count i)})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(at result 'c) -- [65536 65536] +(at (select {from:t by:k n:(count (distinct v)) where:(!= (% i 4) 1)}) 'n) -- [1 1] + +;; LIST keys, LIST values: pure rewrite, mixed aggregation, and selection. +(set keys (list [11 22 33 44 55] [11 22 33 44 66])) +(set vals (list [11 22 33 44 55] [11 22 33 44 66])) +(set t (table [k v i] (list (at keys ki) (at vals vi) r))) +(set result (select {from:t by:k n:(count (distinct v))})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(set result (select {from:t by:k n:(count (distinct v)) c:(count i)})) +(at result 'k) -- keys +(at result 'n) -- [2 1] +(at result 'c) -- [65536 65536] +(at (select {from:t by:k n:(count (distinct v)) where:(!= (% i 4) 1)}) 'n) -- [1 1] + +;; Two GUID fields plus a STR field exceed the former 16-byte key budget. +;; The second GUID distinguishes keys whose first field is identical. +(set ga (as 'GUID (list "11111111-1111-1111-1111-111111111111"))) +(set gb (as 'GUID (list "22222222-2222-2222-2222-222222222221" "22222222-2222-2222-2222-222222222222"))) +(set t (table [a b c v i] (list (at ga (* 0 r)) (at gb ki) (at ["common wide key"] (* 0 r)) (at ["identical prefix value a" "identical prefix value b"] vi) r))) +(set result (select {from:t by:{a:a b:b c:c} n:(count (distinct v))})) +(at result 'a) -- (at ga [0 0]) +(at result 'b) -- gb +(at result 'c) -- ["common wide key" "common wide key"] +(at result 'n) -- [2 1] +(set result (select {from:t by:{a:a b:b c:c} n:(count (distinct v)) crows:(count i)})) +(at result 'b) -- gb +(at result 'n) -- [2 1] +(at result 'crows) -- [65536 65536] diff --git a/test/rfl/group/zero_groups_keep_columns.rfl b/test/rfl/group/zero_groups_keep_columns.rfl new file mode 100644 index 000000000..e61f260f1 --- /dev/null +++ b/test/rfl/group/zero_groups_keep_columns.rfl @@ -0,0 +1,57 @@ +;; A grouped select whose selection matches no row must still return every +;; requested column, whatever aggregate family produces it, for a single key +;; and for a composite key alike. Before the fix the streaming aggregates and +;; the per-group projections were silently dropped (or the composite query +;; failed outright) because their result columns were only created on the +;; first group. +(set T (table [k s v tm sy] (list ["a" "b" "c"] (as 'I64 [1 2 3]) [1.5 2.5 3.5] (as 'TIME [1 2 3]) ['x 'y 'z]))) +(set Z (select {from: T by: k where: (== k "missing") s: (sum s) m: (med v) q: (quantile v 0.5) mo: (mode v) f: (first tm) l: (last sy) tp: (top v 3) bt: (bot s 2) d: (distinct v) cd: (count (distinct v)) n: (count v) mn: (min sy) mx: (max s) av: (avg s) fs: (first s) ls: (last k)})) +(set F (select {from: T by: k s: (sum s) m: (med v) q: (quantile v 0.5) mo: (mode v) f: (first tm) l: (last sy) tp: (top v 3) bt: (bot s 2) d: (distinct v) cd: (count (distinct v)) n: (count v) mn: (min sy) mx: (max s) av: (avg s) fs: (first s) ls: (last k)})) +(count Z) -- 0 +(cols Z) -- (cols F) +(count (cols Z)) -- 17 +;; the empty columns carry the type their non-empty counterparts have +(type (at Z 's)) -- (type (at F 's)) +(type (at Z 'm)) -- (type (at F 'm)) +(type (at Z 'n)) -- (type (at F 'n)) +(type (at Z 'f)) -- (type (at F 'f)) +(type (at Z 'av)) -- (type (at F 'av)) +;; composite key, nothing selected +(set ZC (select {from: T by: [k sy] where: (== k "missing") s: (sum s) n: (count v) f: (first v) m: (med v) d: (distinct v) cd: (count (distinct v)) tp: (top v 2)})) +(set FC (select {from: T by: [k sy] s: (sum s) n: (count v) f: (first v) m: (med v) d: (distinct v) cd: (count (distinct v)) tp: (top v 2)})) +(count ZC) -- 0 +(cols ZC) -- (cols FC) +(count (cols ZC)) -- 9 +(type (at ZC 'n)) -- (type (at FC 'n)) +(type (at ZC 'f)) -- (type (at FC 'f)) +;; a non-empty selection is untouched +(set P (select {from: T by: [k sy] where: (> s 1) s: (sum s) n: (count v) f: (first v) d: (distinct v)})) +(count P) -- 2 +(cols P) -- [k sy s n f d] + +;; ── literal projections at zero groups ───────────────────────────── +;; A constant column is not an aggregate: it is built by broadcasting the +;; atom across the groups. With no groups that broadcast is zero-length, +;; which must still produce a typed empty column. atom_broadcast_vec +;; returned NULL for n == 0 and the caller reported it as `oom`, so these +;; shapes failed with an out-of-memory error on an ordinary query. +(set ZL (select {from: T by: k where: (== k "missing") one: 1 tag: 'x r: 1.5 b: true c: (count v)})) +(set FL (select {from: T by: k one: 1 tag: 'x r: 1.5 b: true c: (count v)})) +(count ZL) -- 0 +(cols ZL) -- (cols FL) +(count (cols ZL)) -- 6 +;; each constant column keeps the type its non-empty counterpart has +(type (at ZL 'one)) -- (type (at FL 'one)) +(type (at ZL 'tag)) -- (type (at FL 'tag)) +(type (at ZL 'r)) -- (type (at FL 'r)) +(type (at ZL 'b)) -- (type (at FL 'b)) + +;; a projection with no aggregate at all still returns its columns +(set ZO (select {from: T by: k where: (== k "missing") one: 1})) +(count ZO) -- 0 +(count (cols ZO)) -- 2 + +;; and the same under a composite key +(set ZLC (select {from: T by: [k sy] where: (== k "missing") one: 1 n: (count v)})) +(count ZLC) -- 0 +(count (cols ZLC)) -- 4 diff --git a/test/rfl/integration/slice_group.rfl b/test/rfl/integration/slice_group.rfl index 30397c638..c522e9293 100644 --- a/test/rfl/integration/slice_group.rfl +++ b/test/rfl/integration/slice_group.rfl @@ -28,8 +28,8 @@ ;; group aa sees v ∈ {0,8,…,3992}: count=500, sum=998000; bb: {1,9,…}: 998500 ;; ── IN set: sums/counts/avg, indexed vs plain vs hand values ── -(set Q1I (select {sv: (sum v) c: (count v) av: (avg v) from: TI by: s where: (in s ['aa 'cc])})) -(set Q1P (select {sv: (sum v) c: (count v) av: (avg v) from: TP by: s where: (in s ['aa 'cc])})) +(set Q1I (select {sv: (sum v) c: (count v) av: (avg v) from: TI by: s asc: s where: (in s ['aa 'cc])})) +(set Q1P (select {sv: (sum v) c: (count v) av: (avg v) from: TP by: s asc: s where: (in s ['aa 'cc])})) (count Q1I) -- 2 (at (at Q1I 'sv) 0) -- 998000 (at (at Q1I 'c) 0) -- 500 @@ -39,22 +39,22 @@ (min (== (at Q1I 's) (at Q1P 's))) -- true ;; ── sorted/contiguous twin: identical values ── -(set Q1S (select {sv: (sum v) c: (count v) av: (avg v) from: TSI by: s where: (in s ['aa 'cc])})) +(set Q1S (select {sv: (sum v) c: (count v) av: (avg v) from: TSI by: s asc: s where: (in s ['aa 'cc])})) (min (== (at Q1S 'sv) (at Q1P 'sv))) -- true (min (== (at Q1S 'c) (at Q1P 'c))) -- true ;; ── fused product (sum/avg of scan*scan, one F64 side) ── -(set Q2I (select {p: (sum (* v f)) q: (avg (* f w)) sw: (sum w) from: TI by: s where: (in s ['bb 'dd 'ff])})) -(set Q2P (select {p: (sum (* v f)) q: (avg (* f w)) sw: (sum w) from: TP by: s where: (in s ['bb 'dd 'ff])})) +(set Q2I (select {p: (sum (* v f)) q: (avg (* f w)) sw: (sum w) from: TI by: s asc: s where: (in s ['bb 'dd 'ff])})) +(set Q2P (select {p: (sum (* v f)) q: (avg (* f w)) sw: (sum w) from: TP by: s asc: s where: (in s ['bb 'dd 'ff])})) (min (== (at Q2I 'p) (at Q2P 'p))) -- true (min (== (at Q2I 'q) (at Q2P 'q))) -- true (min (== (at Q2I 'sw) (at Q2P 'sw))) -- true -(set Q2S (select {p: (sum (* v f)) q: (avg (* f w)) sw: (sum w) from: TSI by: s where: (in s ['bb 'dd 'ff])})) +(set Q2S (select {p: (sum (* v f)) q: (avg (* f w)) sw: (sum w) from: TSI by: s asc: s where: (in s ['bb 'dd 'ff])})) (min (== (at Q2S 'p) (at Q2P 'p))) -- true (min (== (at Q2S 'sw) (at Q2P 'sw))) -- true -(set Q2BI (select {sd: (stddev f) cp: (pearson_corr f v) cs: (pearson_corr v w) cv: (cov f w) sc: (scov f w) ws: (wsum w f) wa: (wavg w f) from: TI by: s where: (in s ['aa 'cc 'ee])})) -(set Q2BP (select {sd: (stddev f) cp: (pearson_corr f v) cs: (pearson_corr v w) cv: (cov f w) sc: (scov f w) ws: (wsum w f) wa: (wavg w f) from: TP by: s where: (in s ['aa 'cc 'ee])})) +(set Q2BI (select {sd: (stddev f) cp: (pearson_corr f v) cs: (pearson_corr v w) cv: (cov f w) sc: (scov f w) ws: (wsum w f) wa: (wavg w f) from: TI by: s asc: s where: (in s ['aa 'cc 'ee])})) +(set Q2BP (select {sd: (stddev f) cp: (pearson_corr f v) cs: (pearson_corr v w) cv: (cov f w) sc: (scov f w) ws: (wsum w f) wa: (wavg w f) from: TP by: s asc: s where: (in s ['aa 'cc 'ee])})) (min (< (abs (- (at Q2BI 'sd) (at Q2BP 'sd))) 1e-9)) -- true (min (< (abs (- (at Q2BI 'cp) (at Q2BP 'cp))) 1e-9)) -- true (min (< (abs (- (at Q2BI 'cs) (at Q2BP 'cs))) 1e-9)) -- true @@ -62,7 +62,7 @@ (min (< (abs (- (at Q2BI 'sc) (at Q2BP 'sc))) 1e-9)) -- true (min (< (abs (- (at Q2BI 'ws) (at Q2BP 'ws))) 1e-9)) -- true (min (< (abs (- (at Q2BI 'wa) (at Q2BP 'wa))) 1e-9)) -- true -(set Q2BS (select {sd: (stddev f) cp: (pearson_corr f v) cs: (pearson_corr v w) cv: (cov f w) sc: (scov f w) ws: (wsum w f) wa: (wavg w f) from: TSI by: s where: (in s ['aa 'cc 'ee])})) +(set Q2BS (select {sd: (stddev f) cp: (pearson_corr f v) cs: (pearson_corr v w) cv: (cov f w) sc: (scov f w) ws: (wsum w f) wa: (wavg w f) from: TSI by: s asc: s where: (in s ['aa 'cc 'ee])})) (min (< (abs (- (at Q2BS 'sd) (at Q2BP 'sd))) 1e-9)) -- true (min (< (abs (- (at Q2BS 'cp) (at Q2BP 'cp))) 1e-9)) -- true (min (< (abs (- (at Q2BS 'cs) (at Q2BP 'cs))) 1e-9)) -- true @@ -73,26 +73,26 @@ ;; ── shared-stream pairing: sum over the product's int side (q27 shape), ;; incl. arith-of-aggs decomposition wrappers ── -(set Q3I (select {wp: (/ (sum (* w f)) (as 'F64 (sum w))) from: TI by: s where: (in s ['aa 'bb 'cc 'dd])})) -(set Q3P (select {wp: (/ (sum (* w f)) (as 'F64 (sum w))) from: TP by: s where: (in s ['aa 'bb 'cc 'dd])})) -(set Q3PI (select {wp: (/ (sum (* w f)) (as 'F64 (sum w))) from: TPI by: s where: (in s ['aa 'bb 'cc 'dd])})) +(set Q3I (select {wp: (/ (sum (* w f)) (as 'F64 (sum w))) from: TI by: s asc: s where: (in s ['aa 'bb 'cc 'dd])})) +(set Q3P (select {wp: (/ (sum (* w f)) (as 'F64 (sum w))) from: TP by: s asc: s where: (in s ['aa 'bb 'cc 'dd])})) +(set Q3PI (select {wp: (/ (sum (* w f)) (as 'F64 (sum w))) from: TPI by: s asc: s where: (in s ['aa 'bb 'cc 'dd])})) (min (== (at Q3I 'wp) (at Q3P 'wp))) -- true (min (== (at Q3I 's) (at Q3P 's))) -- true (min (== (at Q3PI 'wp) (at Q3P 'wp))) -- true (min (== (at Q3PI 's) (at Q3P 's))) -- true ;; ── eq single key ── -(set Q4I (select {sv: (sum v) from: TI by: s where: (== s 'ee)})) -(set Q4P (select {sv: (sum v) from: TP by: s where: (== s 'ee)})) +(set Q4I (select {sv: (sum v) from: TI by: s asc: s where: (== s 'ee)})) +(set Q4P (select {sv: (sum v) from: TP by: s asc: s where: (== s 'ee)})) (count Q4I) -- 1 (min (== (at Q4I 'sv) (at Q4P 'sv))) -- true ;; ── absent + duplicate keys in the set; empty intersection ── -(set Q5I (select {sv: (sum v) from: TI by: s where: (in s ['aa 'zz 'aa])})) +(set Q5I (select {sv: (sum v) from: TI by: s asc: s where: (in s ['aa 'zz 'aa])})) (count Q5I) -- 1 (at (at Q5I 'sv) 0) -- 998000 -(count (select {sv: (sum v) from: TI by: s where: (in s ['zz 'yy])})) -- 0 -(count (select {sv: (sum v) from: TI by: s where: (== s 'zz)})) -- 0 +(count (select {sv: (sum v) from: TI by: s asc: s where: (in s ['zz 'yy])})) -- 0 +(count (select {sv: (sum v) from: TI by: s asc: s where: (== s 'zz)})) -- 0 ;; ── aliased by ({k: s} — rename rides the generic by-dict path) ── (set Q6I (select {sv: (sum v) from: TI by: {k: s} where: (in s ['aa 'cc])})) @@ -101,39 +101,39 @@ ;; ── fallback shapes: results must equal the plain-table run exactly ── ;; min/max (unsupported agg → hint folds to selection) -(set Q7I (select {mn: (min v) mx: (max v) from: TI by: s where: (in s ['aa 'hh])})) -(set Q7P (select {mn: (min v) mx: (max v) from: TP by: s where: (in s ['aa 'hh])})) +(set Q7I (select {mn: (min v) mx: (max v) from: TI by: s asc: s where: (in s ['aa 'hh])})) +(set Q7P (select {mn: (min v) mx: (max v) from: TP by: s asc: s where: (in s ['aa 'hh])})) (min (== (at Q7I 'mn) (at Q7P 'mn))) -- true (min (== (at Q7I 'mx) (at Q7P 'mx))) -- true ;; compound where (second conjunct → no fusion) -(set Q8I (select {sv: (sum v) from: TI by: s where: (and (in s ['aa 'bb]) (> v 1000))})) -(set Q8P (select {sv: (sum v) from: TP by: s where: (and (in s ['aa 'bb]) (> v 1000))})) +(set Q8I (select {sv: (sum v) from: TI by: s asc: s where: (and (in s ['aa 'bb]) (> v 1000))})) +(set Q8P (select {sv: (sum v) from: TP by: s asc: s where: (and (in s ['aa 'bb]) (> v 1000))})) (min (== (at Q8I 'sv) (at Q8P 'sv))) -- true ;; not-in (never fused) -(set Q9I (select {sv: (sum v) from: TI by: s where: (not (in s ['aa 'bb 'cc 'dd 'ee 'ff]))})) -(set Q9P (select {sv: (sum v) from: TP by: s where: (not (in s ['aa 'bb 'cc 'dd 'ee 'ff]))})) +(set Q9I (select {sv: (sum v) from: TI by: s asc: s where: (not (in s ['aa 'bb 'cc 'dd 'ee 'ff]))})) +(set Q9P (select {sv: (sum v) from: TP by: s asc: s where: (not (in s ['aa 'bb 'cc 'dd 'ee 'ff]))})) (min (== (at Q9I 'sv) (at Q9P 'sv))) -- true ;; where on a NON-key column (no fusion; sanity) -(set QAI (select {sv: (sum v) from: TI by: s where: (< v 100)})) -(set QAP (select {sv: (sum v) from: TP by: s where: (< v 100)})) +(set QAI (select {sv: (sum v) from: TI by: s asc: s where: (< v 100)})) +(set QAP (select {sv: (sum v) from: TP by: s asc: s where: (< v 100)})) (min (== (at QAI 'sv) (at QAP 'sv))) -- true ;; count-distinct output (stays on the filter path — saved_selection) -(set QBI (select {cd: (count (distinct w)) from: TI by: s where: (in s ['aa 'bb])})) -(set QBP (select {cd: (count (distinct w)) from: TP by: s where: (in s ['aa 'bb])})) +(set QBI (select {cd: (count (distinct w)) from: TI by: s asc: s where: (in s ['aa 'bb])})) +(set QBP (select {cd: (count (distinct w)) from: TP by: s asc: s where: (in s ['aa 'bb])})) (min (== (at QBI 'cd) (at QBP 'cd))) -- true ;; ── nullable agg column → kernel bails, fallback matches ── (set VN (concat (as 'I64 (til (- N 4))) [0N 1 0N 2])) (set TNI (table [s v] (list (.attr.set 'grouped KS) VN))) (set TNP (table [s v] (list KS VN))) -(set QCI (select {sv: (sum v) c: (count v) from: TNI by: s where: (in s ['bb 'cc])})) -(set QCP (select {sv: (sum v) c: (count v) from: TNP by: s where: (in s ['bb 'cc])})) +(set QCI (select {sv: (sum v) c: (count v) from: TNI by: s asc: s where: (in s ['bb 'cc])})) +(set QCP (select {sv: (sum v) c: (count v) from: TNP by: s asc: s where: (in s ['bb 'cc])})) (min (== (at QCI 'sv) (at QCP 'sv))) -- true (min (== (at QCI 'c) (at QCP 'c))) -- true ;; ── take/sort post-clauses on the fused result ── -(set QDI (select {sv: (sum v) from: TI by: s where: (in s ['aa 'bb 'cc 'dd]) desc: sv take: 2})) -(set QDP (select {sv: (sum v) from: TP by: s where: (in s ['aa 'bb 'cc 'dd]) desc: sv take: 2})) +(set QDI (select {sv: (sum v) from: TI by: s asc: s where: (in s ['aa 'bb 'cc 'dd]) desc: sv take: 2})) +(set QDP (select {sv: (sum v) from: TP by: s asc: s where: (in s ['aa 'bb 'cc 'dd]) desc: sv take: 2})) (count QDI) -- 2 (min (== (at QDI 'sv) (at QDP 'sv))) -- true @@ -148,8 +148,8 @@ ;; plain-table (non-indexed) run — same equivalence idiom as every other ;; cell above, just past the sg agg-count boundary instead of the width ;; guards. -(set Q10I (select {a01: (sum v) a02: (sum v) a03: (sum v) a04: (sum v) a05: (sum v) a06: (sum v) a07: (sum v) a08: (sum v) a09: (sum v) a10: (sum v) a11: (sum v) a12: (sum v) a13: (sum v) a14: (sum v) a15: (sum v) a16: (sum v) a17: (count v) from: TI by: s where: (in s ['aa 'cc])})) -(set Q10P (select {a01: (sum v) a02: (sum v) a03: (sum v) a04: (sum v) a05: (sum v) a06: (sum v) a07: (sum v) a08: (sum v) a09: (sum v) a10: (sum v) a11: (sum v) a12: (sum v) a13: (sum v) a14: (sum v) a15: (sum v) a16: (sum v) a17: (count v) from: TP by: s where: (in s ['aa 'cc])})) +(set Q10I (select {a01: (sum v) a02: (sum v) a03: (sum v) a04: (sum v) a05: (sum v) a06: (sum v) a07: (sum v) a08: (sum v) a09: (sum v) a10: (sum v) a11: (sum v) a12: (sum v) a13: (sum v) a14: (sum v) a15: (sum v) a16: (sum v) a17: (count v) from: TI by: s asc: s where: (in s ['aa 'cc])})) +(set Q10P (select {a01: (sum v) a02: (sum v) a03: (sum v) a04: (sum v) a05: (sum v) a06: (sum v) a07: (sum v) a08: (sum v) a09: (sum v) a10: (sum v) a11: (sum v) a12: (sum v) a13: (sum v) a14: (sum v) a15: (sum v) a16: (sum v) a17: (count v) from: TP by: s asc: s where: (in s ['aa 'cc])})) (at (meta Q10I) 'len) -- 18 (min (== (at Q10I 'a01) (at Q10P 'a01))) -- true (min (== (at Q10I 'a17) (at Q10P 'a17))) -- true diff --git a/test/rfl/sort/asc_key_width.rfl b/test/rfl/sort/asc_key_width.rfl new file mode 100644 index 000000000..ad5b7698e --- /dev/null +++ b/test/rfl/sort/asc_key_width.rfl @@ -0,0 +1,31 @@ +;; The radix key width is derived from the data range. A high byte that only +;; differs BETWEEN dispatch tasks (8192 rows each) must still widen the key; +;; if it is missed, asc/desc silently sort by the low bytes alone. + +;; rows 40960..49151 carry bit 16, everything else lives in [0, 8192) +(set N 200000) +(set X (% (* 7919 (til N)) 8192)) +(set HI (as 'I64 (>= (til N) 40960))) +(set HI (* HI (as 'I64 (< (til N) 49152)))) +(set V (+ X (* 65536 HI))) + +(set SA (asc V)) +(min (>= (- (at SA (+ 1 (til (- N 1)))) (at SA (til (- N 1)))) 0)) -- true +(count SA) -- N +(sum SA) -- (sum V) + +(set SD (desc V)) +(min (<= (- (at SD (+ 1 (til (- N 1)))) (at SD (til (- N 1)))) 0)) -- true + +(set T (table [v] (list V))) +(set SV (at (select {from: T asc: v}) 'v)) +(min (>= (- (at SV (+ 1 (til (- N 1)))) (at SV (til (- N 1)))) 0)) -- true + +;; a nearly sorted vector: one swap past the 65536th element +(set W (til 70000)) +(set W (at W (at (til 70000) (til 70000)))) +(set W (+ W (* (as 'I64 (== (til 70000) 66000)) 1))) +(set W (- W (* (as 'I64 (== (til 70000) 66001)) 1))) +(set SW (asc W)) +(min (>= (- (at SW (+ 1 (til 69999))) (at SW (til 69999))) 0)) -- true +(at SW (+ 65990 (til 4))) -- [65990 65991 65992 65993] diff --git a/test/rfl/sort/stable_ties.rfl b/test/rfl/sort/stable_ties.rfl new file mode 100644 index 000000000..5dad42e1a --- /dev/null +++ b/test/rfl/sort/stable_ties.rfl @@ -0,0 +1,41 @@ +;; Sorting is stable in every size regime and every direction: equal keys keep +;; their source-row order. Each block sits just past a regime boundary where +;; the shortcut or kernel changes (merge sort ≤64 → radix; packed-radix +;; "reverse-sorted" shortcut past 4096; STR American-flag past 64). + +;; --- integer keys, descending-with-duplicates input hits the reverse shortcut +(set N 4097) +(set K (reverse (asc (% (til N) 100)))) +(set T (table [k v] (list K (til N)))) +(at (at (xasc T 'k) 'v) (til 4)) -- [4056 4057 4058 4059] +(at (at (xdesc T 'k) 'v) (til 4)) -- [0 1 2 3] +(set G (select {from: (xasc T 'k) by: k l: (last v) f: (first v)})) +(at (at G 'l) (til 2)) -- [4096 4055] +(at (at G 'f) (til 2)) -- [4056 4015] +;; the same at the boundary itself, where the introsort path runs +(set N2 4096) +(set T2 (table [k v] (list (reverse (asc (% (til N2) 100))) (til N2)))) +(at (at (xasc T2 'k) 'v) (til 4)) -- [4055 4056 4057 4058] +;; iasc/idesc agree with xasc/xdesc on the tie rule +(at (iasc K) (til 4)) -- [4056 4057 4058 4059] +(at (idesc K) (til 4)) -- [0 1 2 3] + +;; --- STR keys, ties everywhere, past the 64-row merge-sort regime +(set MK (fn [n m] (as 'STR (map (fn [i] (format "s%" (% i m))) (til n))))) +(at (iasc (MK 64 4)) (til 6)) -- [0 4 8 12 16 20] +(at (iasc (MK 100 4)) (til 6)) -- [0 4 8 12 16 20] +(at (iasc (MK 100000 4)) (til 6)) -- [0 4 8 12 16 20] +(at (idesc (MK 100 4)) (til 6)) -- [3 7 11 15 19 23] +(at (idesc (MK 100000 4)) (til 6)) -- [3 7 11 15 19 23] +;; descending on already-sorted STR input (the run shortcut) +(set SA (as 'STR (map (fn [i] (format "s%" i)) (asc (% (til 100) 4))))) +(at (idesc SA) (til 6)) -- [75 76 77 78 79 80] +(at (iasc (as 'STR (map (fn [i] (format "s%" i)) (desc (% (til 100) 4))))) (til 6)) -- [75 76 77 78 79 80] +;; strings longer than the packed prefix, ties on the tail +(set LK (as 'STR (map (fn [i] (format "a-long-common-prefix-of-many-bytes-%" (% i 3))) (til 300)))) +(at (iasc LK) (til 4)) -- [0 3 6 9] +(at (idesc LK) (til 4)) -- [2 5 8 11] +;; a table sort by a STR column +(set ST (table [k v] (list (MK 100 4) (til 100)))) +(at (at (xasc ST 'k) 'v) (til 4)) -- [0 4 8 12] +(at (at (xdesc ST 'k) 'v) (til 4)) -- [3 7 11 15] diff --git a/test/rfl/system/ipc_diff.rfl b/test/rfl/system/ipc_diff.rfl index edd6f9740..e02f1f08f 100644 --- a/test/rfl/system/ipc_diff.rfl +++ b/test/rfl/system/ipc_diff.rfl @@ -71,11 +71,12 @@ ;; ── string ops ───────────────────────────────────────────────────── (like "hello" "h*") -- (.ipc.send h "(like \"hello\" \"h*\")") -;; ── compression boundary ─────────────────────────────────────────── -;; RAY_IPC_COMPRESS_THRESHOLD = 2000 bytes (src/core/ipc.h). An i64 -;; vector of length 1000 is 8 KB serialised, so the response payload -;; goes through ray_ipc_compress / ray_ipc_decompress — coverage that -;; the small-payload cases above cannot reach. +;; ── large payloads ───────────────────────────────────────────────── +;; Multi-KB results over the framing path. These no longer exercise +;; ray_ipc_compress: since #541 a loopback link never compresses, so +;; both sides here take the raw path however large the payload. The +;; compressed path has its own coverage in test_ipc.c +;; (ipc/compressed_roundtrip), which forces a threshold on the link. (count (til 10000)) -- (.ipc.send h "(count (til 10000))") (sum (til 1000)) -- (.ipc.send h "(sum (til 1000))") diff --git a/test/rfl/system/ipc_open_opts.rfl b/test/rfl/system/ipc_open_opts.rfl new file mode 100644 index 000000000..0ef7b6d22 --- /dev/null +++ b/test/rfl/system/ipc_open_opts.rfl @@ -0,0 +1,44 @@ +;; ipc_open_opts.rfl — `.ipc.open`'s second argument (#541). +;; +;; It stays an integer connect timeout, or becomes an options dict with +;; optional `timeout` and `compress`. `compress` is a threshold in bytes, +;; not a flag: 0N never compresses, 0 always does, n compresses payloads +;; larger than n. Absent means auto — loopback and UNIX-domain links +;; default to never compressing, everything else to the built-in 2000. +;; +;; Nothing is bound on 19974, so a well-formed call reaches connect and +;; fails with `io`; a malformed one is rejected before that, with `type` +;; or `domain`. The two error classes are what distinguish "the option +;; parsed" from "the option was accepted silently". + +;; the existing integer form still means timeout +(.ipc.open "127.0.0.1:19974" 200) !- io + +;; options dict: each accepted key reaches connect +(.ipc.open "127.0.0.1:19974" (dict [timeout] [200])) !- io +(.ipc.open "127.0.0.1:19974" (dict [compress] [0N])) !- io +(.ipc.open "127.0.0.1:19974" (dict [compress] [0])) !- io +(.ipc.open "127.0.0.1:19974" (dict [compress] [5000])) !- io +(.ipc.open "127.0.0.1:19974" (dict [timeout compress] [200 0N])) !- io + +;; an empty dict is valid — everything defaults +(.ipc.open "127.0.0.1:19974" (dict [] [])) !- io + +;; a typo must not silently mean "default" +(.ipc.open "127.0.0.1:19974" (dict [compres] [0N])) !- domain +(.ipc.open "127.0.0.1:19974" (dict [timeuot] [200])) !- domain + +;; wrong value types and out-of-range values are rejected up front +(.ipc.open "127.0.0.1:19974" (dict [compress] ["lots"])) !- type +(.ipc.open "127.0.0.1:19974" (dict [compress] [-5])) !- domain +(.ipc.open "127.0.0.1:19974" (dict [timeout] [-1])) !- domain + +;; a second argument that is neither an integer nor a dict +(.ipc.open "127.0.0.1:19974" "soon") !- type + +;; string keys are NOT symbols: dict lookups would miss and every option +;; would silently default, so the whole dict is refused up front. +(.ipc.open "127.0.0.1:19974" (dict ["compress"] [0N])) !- type +(.ipc.open "127.0.0.1:19974" (dict ["timeout"] [200])) !- type +;; ...including a typo that would otherwise be swallowed with them +(.ipc.open "127.0.0.1:19974" (dict ["compres"] [0N])) !- type diff --git a/test/rfl/system/log_journal_advanced.rfl b/test/rfl/system/log_journal_advanced.rfl index dd8974362..7d1a7e228 100644 --- a/test/rfl/system/log_journal_advanced.rfl +++ b/test/rfl/system/log_journal_advanced.rfl @@ -9,6 +9,7 @@ ;; abort replay (commit message documents this; basic test ;; only exercises framing badtail) ;; Phase D: .log.write is rejected inside auto-journaled IPC eval +;; Phase E: closure-bearing functions saved in a snapshot survive restart ;; ── IPC connect with handshake-level retry ────────────────────────── ;; A /dev/tcp probe reports ready the instant the server calls listen(), @@ -148,3 +149,34 @@ (.sys.exec "for i in $(seq 30); do pgrep -f '[r]ayforce -l /tmp/rftest_jwr -p 19994' >/dev/null || exit 0; sleep 0.1; done; exit 1") -- 0 (.sys.exec "pkill -KILL -f '[r]ayforce -l /tmp/rftest_jwr' 2>/dev/null; true") (.sys.exec "rm -f /tmp/rftest_jwr*") + +;; ════════════════════════════════════════════════════════════════════ +;; Phase E — function bindings survive snapshot restart +;; ════════════════════════════════════════════════════════════════════ +;; A function-bearing snapshot must load into a fresh server runtime. +;; The binding should be callable after the .qdb restore, before any +;; post-snapshot log tail can recreate it. + +(.sys.exec "pkill -KILL -f '[r]ayforce -l /tmp/rftest_jsnapfn' 2>/dev/null; true") +(.sys.exec "rm -f /tmp/rftest_jsnapfn*") +(.sys.exec "./rayforce -l /tmp/rftest_jsnapfn -p 19995 /dev/null 2>&1 &") +(.sys.exec "for i in $(seq 30); do bash -c '(echo > /dev/tcp/127.0.0.1/19995) 2>/dev/null' && exit 0; sleep 0.1; done; exit 1") -- 0 + +(set hE (wait_ipc "127.0.0.1:19995" 50)) +(.ipc.send hE "(set snapshot_inc ((fn [offset] (fn [x] (+ x offset))) 1))") +(.ipc.send hE "(snapshot_inc 41)") -- 42 +(.ipc.send hE "(.log.snapshot)") +(.ipc.close hE) +(.sys.exec "test -s /tmp/rftest_jsnapfn.qdb") -- 0 +(.sys.exec "pkill -TERM -f '[r]ayforce -l /tmp/rftest_jsnapfn -p 19995' 2>/dev/null; true") +(.sys.exec "for i in $(seq 30); do pgrep -f '[r]ayforce -l /tmp/rftest_jsnapfn -p 19995' >/dev/null || exit 0; sleep 0.1; done; exit 1") -- 0 +(.sys.exec "rm -f /tmp/rftest_jsnapfn.log") -- 0 + +(.sys.exec "./rayforce -l /tmp/rftest_jsnapfn -p 19995 /dev/null 2>&1 &") +(.sys.exec "for i in $(seq 30); do bash -c '(echo > /dev/tcp/127.0.0.1/19995) 2>/dev/null' && exit 0; sleep 0.1; done; exit 1") -- 0 + +(set hE2 (wait_ipc "127.0.0.1:19995" 50)) +(.ipc.send hE2 "(snapshot_inc 41)") -- 42 +(.ipc.close hE2) +(.sys.exec "pkill -KILL -f '[r]ayforce -l /tmp/rftest_jsnapfn' 2>/dev/null; true") +(.sys.exec "rm -f /tmp/rftest_jsnapfn*") diff --git a/test/rfl/system/part.rfl b/test/rfl/system/part.rfl index 978978232..9084f24f3 100644 --- a/test/rfl/system/part.rfl +++ b/test/rfl/system/part.rfl @@ -62,7 +62,7 @@ ;; non-date partition keys land under the 'part column name (first (key Pi)) -- 'part (key Pi) -- ['part 'n] -;; verify the actual column VALUES read off disk, in lexical partition order +;; verify the actual column VALUES read off disk, in numeric partition order ;; (100, 200, 300): IT-A=[100 101], IT-B=[200 201 202], IT-C=[300] ;; BUG: a whole-column read of a parted INT column returns it UN-materialized ;; (still segmented) — `(at Pi 'n)` yields "" instead of the vector. @@ -117,10 +117,10 @@ (at (select {from: Psp where: (> x 7)}) 'x) -- [8 9 10] ;; ────────────── three-partition concatenation ordering ────────────── -;; Bubble-sort in collect_part_dirs (lines 164-172) reorders dir -;; entries lexically. Insert in reverse to prove the sort runs; -;; the row count would be the same either way, but iterating the -;; outer loop more than once exercises the i-then-j swap branch. +;; collect_part_dirs sorts dir entries into key order — by name for a +;; date set, whose fixed-width names sort chronologically. Insert in +;; reverse to prove the sort runs; the row count would be the same +;; either way. (set TR-1 (table [n] (list [1]))) (set TR-2 (table [n] (list [2 2]))) (set TR-3 (table [n] (list [3 3 3]))) @@ -423,5 +423,62 @@ (sum (at Pleap 'v)) -- 15 (.sys.exec "rm -rf /tmp/rfl_part_badcal /tmp/rfl_part_leap") +;; ────── integer partitions order by VALUE, not by name ────── +;; collect_part_dirs sorted every scheme with strcmp, so an integer set with +;; keys of unequal width came out 1, 10, 2: the I64 partition-key column was +;; unordered, rows interleaved across keys, and the "most recent" (last) +;; partition that .db.parted.tables / .db.parted.fill read was "9" rather +;; than "12" — a table first added in the newest partition was invisible. +(.sys.exec "rm -rf /tmp/rfl_part_intord /tmp/rfl_part_intovf") +(set IO-1 (table [n] (list [1]))) +(set IO-2 (table [n] (list [2]))) +(set IO-10 (table [n] (list [10]))) +(.db.splayed.set "/tmp/rfl_part_intord/1/t/" IO-1) +(.db.splayed.set "/tmp/rfl_part_intord/2/t/" IO-2) +(.db.splayed.set "/tmp/rfl_part_intord/10/t/" IO-10) +;; a table that exists only in the newest (largest-key) partition +(.db.splayed.set "/tmp/rfl_part_intord/10/late/" IO-10) +(set Pord (.db.parted.get "/tmp/rfl_part_intord/" 't)) +(first (key Pord)) -- 'part +;; keys and rows follow numeric key order (by name this was [1 10 2]) +(at (select {from: Pord where: (> n 0)}) 'part) -- [1 2 10] +(at (select {from: Pord where: (> n 0)}) 'n) -- [1 2 10] +;; the newest partition is 10, so its table set includes 'late +(.db.parted.tables "/tmp/rfl_part_intord/") -- ['late 't] +;; and an int-partition range predicate still prunes on the ordered keys +(at (select {from: Pord where: (>= part 2)}) 'n) -- [2 10] + +;; ────── an integer name that does not fit int64 is a corrupt partition set ────── +;; parse_int_dir accumulated a 20-digit name straight past INT64_MAX — +;; signed-overflow UB (a UBSan abort here) that in release wrapped the key +;; to an unrelated number. A digit-only name can only mean an integer key, +;; so rather than silently retyping the whole set to sym keys — where +;; (== part 2) matches nothing and (>= part 2) returns partition 1 too — +;; every reader of the set fails with `corrupt`, naming the directory. +(set OV-A (table [n] (list [1]))) +(set OV-B (table [n] (list [2]))) +(.db.splayed.set "/tmp/rfl_part_intovf/1/t/" OV-A) +(.db.splayed.set "/tmp/rfl_part_intovf/99999999999999999999/t/" OV-B) +(.db.parted.get "/tmp/rfl_part_intovf/" 't) !- corrupt +(.db.parted.tables "/tmp/rfl_part_intovf/") !- corrupt +(.db.parted.fill "/tmp/rfl_part_intovf/") !- corrupt +;; beside a non-integer name the set is sym-keyed and the long name is just a +;; name like any other, kept literal +(.db.splayed.set "/tmp/rfl_part_intovf/1.5/t/" OV-A) +(set Pmix (.db.parted.get "/tmp/rfl_part_intovf/" 't)) +(first (key Pmix)) -- 'part +(at Pmix 'part) -- (as 'sym ["1" "1.5" "99999999999999999999"]) +;; the largest value that does fit is still an integer key ... +(.sys.exec "rm -rf /tmp/rfl_part_intovf") +(.db.splayed.set "/tmp/rfl_part_intovf/1/t/" OV-A) +(.db.splayed.set "/tmp/rfl_part_intovf/9223372036854775807/t/" OV-B) +(set Pmax (.db.parted.get "/tmp/rfl_part_intovf/" 't)) +(at (select {from: Pmax where: (> n 0)}) 'part) -- [1 9223372036854775807] +;; ... and one past it is not +(.sys.exec "rm -rf /tmp/rfl_part_intovf/9223372036854775807") +(.db.splayed.set "/tmp/rfl_part_intovf/9223372036854775808/t/" OV-B) +(.db.parted.get "/tmp/rfl_part_intovf/" 't) !- corrupt +(.sys.exec "rm -rf /tmp/rfl_part_intord /tmp/rfl_part_intovf") + ;; ────────────── teardown ────────────── (.sys.exec "rm -rf /tmp/rfl_part_date /tmp/rfl_part_int /tmp/rfl_part_sym /tmp/rfl_part_single /tmp/rfl_part_empty /tmp/rfl_part_missing /tmp/rfl_part_three /tmp/rfl_part_cd /tmp/rfl_part_cd_sym /tmp/rfl_part_minute /tmp/rfl_part_like /tmp/rfl_part_mc /tmp/rfl_part_mb") diff --git a/test/rfl/table/upsert_vector.rfl b/test/rfl/table/upsert_vector.rfl new file mode 100644 index 000000000..e57cc1e9f --- /dev/null +++ b/test/rfl/table/upsert_vector.rfl @@ -0,0 +1,210 @@ +;; upsert_vector.rfl — a multi-row upsert whose payload columns are typed +;; vectors of the target's own types is written column by column (#535): +;; keys resolve in payload order, the new rows append once per column, and +;; the matched cells scatter in payload order. Every case applies one batch +;; twice — as a vector payload (the column-wise path) and row by row as atoms +;; (the per-cell path) — and requires the two tables to be identical. The +;; batches repeat keys, mix hits with misses, and carry nulls. + +(set rowwise (fn [tn nk P cols] (map (fn [r] (upsert tn nk (map (fn [c] (at (at P c) r)) cols))) (til (count P))))) +(set NI 37) +(set insts (as 'SYMBOL (map (fn [i] (format "i%" i)) (til NI)))) +(set TS 2026.09.15D12:00:00.000000000) +(set G (guid 64)) +;; 400 keys over 100 distinct ids in 350..649: each repeated four times, +;; ids below 500 update, the rest append (and then update themselves) +(set KP (+ 350 (% (* 7919 (til 400)) 300))) + +;; ── the order-book shape: sym/sym/i64 key, i64 and timestamp values ── +(set book (fn [ids vals] (table [instrument side px sz ts_evt ts_recv] (list (at insts (% ids NI)) (at ['B 'A] (% ids 2)) (+ 100000 ids) vals (+ TS vals) (+ TS (* 2 vals)))))) +(set A (book (til 500) (til 500))) +(set B (book (til 500) (til 500))) +(set P (book KP (+ 1000 (til 400)))) +(upsert 'A 3 P) -- 'A +(rowwise 'B 3 P [instrument side px sz ts_evt ts_recv]) +A -- B +(count A) -- 650 +(.idx.has? A) -- true +;; a second batch rides the map the first one left +(set P (book (% (* 104729 (til 400)) 700) (+ 5000 (til 400)))) +(upsert 'A 3 P) +(rowwise 'B 3 P [instrument side px sz ts_evt ts_recv]) +A -- B + +;; ── every fixed-width value type behind an i64 key ── +(set wide (fn [ks vs] (table [k a b c d e f g h i j] (list ks (as 'I16 vs) (as 'I32 vs) (/ (as 'F64 vs) 4) (+ 2024.01.01 vs) (as 'TIME vs) (+ TS vs) (== 0 (% vs 3)) (as 'U8 (% vs 250)) (at G (% vs 64)) (at insts (% vs NI)))))) +(set A (wide (til 500) (til 500))) +(set B (wide (til 500) (til 500))) +(set P (wide KP (+ 7 (til 400)))) +(upsert 'A 'k P) +(rowwise 'B 'k P [k a b c d e f g h i j]) +A -- B + +;; ── key types: each built from the same ids ── +(set kcase (fn [conv] (do (set A (table [k v w] (list (conv (til 500)) (til 500) (as 'F64 (til 500))))) (set B (table [k v w] (list (conv (til 500)) (til 500) (as 'F64 (til 500))))) (set P (table [k v w] (list (conv KP) (+ 1000 (til 400)) (/ (as 'F64 (til 400)) 8)))) (upsert 'A 'k P) (rowwise 'B 'k P [k v w]) (count A)))) +(kcase (fn [x] (as 'I32 x))) -- 650 +A -- B +(kcase (fn [x] (as 'I16 x))) -- 650 +A -- B +(kcase (fn [x] (+ 2024.01.01 x))) -- 650 +A -- B +(kcase (fn [x] (as 'TIME x))) -- 650 +A -- B +(kcase (fn [x] (+ TS x))) -- 650 +A -- B +(kcase (fn [x] (/ (as 'F64 x) 2))) -- 650 +A -- B +(kcase (fn [x] (at insts (% x NI)))) +A -- B +(kcase (fn [x] (at G (% x 64)))) +A -- B +;; duplicate keys already in the target: the lowest row is the one updated +(kcase (fn [x] (as 'U8 (% x 256)))) +A -- B +(kcase (fn [x] (== 0 (% x 2)))) +A -- B + +;; ── nulls: payload nulls land and raise the null gate; null keys match ── +(set nul (fn [ids] (table [k q p s i] (list ids (at [1 0Nl 3 4 0Nl] (% ids 5)) (at [1.5 0Nf 2.5] (% ids 3)) (at ['x ' 'y] (% ids 3)) (at [1i 2i 0Ni] (% ids 3)))))) +(set A (table [k q p s i] (list (til 500) (til 500) (as 'F64 (til 500)) (at insts (% (til 500) NI)) (as 'I32 (til 500))))) +(set B (table [k q p s i] (list (til 500) (til 500) (as 'F64 (til 500)) (at insts (% (til 500) NI)) (as 'I32 (til 500))))) +(set P (nul KP)) +(upsert 'A 'k P) +(rowwise 'B 'k P [k q p s i]) +A -- B +(sum (at A 'q)) -- (sum (at B 'q)) +(avg (at A 'p)) -- (avg (at B 'p)) +(sum (at A 'i)) -- (sum (at B 'i)) +(count (select {from: A where: (nil? q)})) -- (count (select {from: B where: (nil? q)})) +(count (select {from: A where: (== s ')})) -- (count (select {from: B where: (== s ')})) +;; the null gate itself travels with a serialized column +(ser (at A 'q)) -- (ser (at B 'q)) +(ser (at A 'p)) -- (ser (at B 'p)) +(ser (at A 's)) -- (ser (at B 's)) +(ser (at A 'i)) -- (ser (at B 'i)) +;; a null i64 key matches the null already in the target +(set A (table [k v w] (list [1 0Nl 3] [1 2 3] [1.0 2.0 3.0]))) +(upsert 'A 'k (table [k v w] (list [0Nl 4 0Nl] [22 44 222] [2.2 4.4 22.2]))) +(at A 'v) -- [1 222 3 44] +;; a NaN f64 key never matches, so each one appends +(set A (table [k v] (list [1.0 0Nf] [1 2]))) +(upsert 'A 'k (table [k v] (list [0Nf 1.0 0Nf] [3 11 4]))) +(at A 'v) -- [11 2 3 4] + +;; ── a table past the prefetch threshold resolves through the look-ahead +;; pass (hashes first, probes loaded ahead); the result must not move. +;; Compared as serialized bytes: the whole table, attrs included ── +(set A (book (til 100000) (til 100000))) +(set B (book (til 100000) (til 100000))) +(set P (book (+ 99000 (% (* 7919 (til 3000)) 2000)) (+ 1000 (til 3000)))) +(upsert 'A 3 P) +(rowwise 'B 3 P [instrument side px sz ts_evt ts_recv]) +(count A) -- 101000 +(count (ser A)) -- (count (ser B)) +(sum (== (ser A) (ser B))) -- (count (ser A)) +(set A (table [k q p s i] (list (til 200000) (til 200000) (as 'F64 (til 200000)) (at insts (% (til 200000) NI)) (as 'I32 (til 200000))))) +(set B (table [k q p s i] (list (til 200000) (til 200000) (as 'F64 (til 200000)) (at insts (% (til 200000) NI)) (as 'I32 (til 200000))))) +(set P (nul (+ 199000 (% (* 7919 (til 3000)) 2000)))) +(upsert 'A 'k P) +(rowwise 'B 'k P [k q p s i]) +(count A) -- 201000 +(count (ser A)) -- (count (ser B)) +(sum (== (ser A) (ser B))) -- (count (ser A)) +(sum (at A 'q)) -- (sum (at B 'q)) + +;; ── the vector-row form takes the same path ── +(set A (table [k v w] (list (til 500) (til 500) (as 'F64 (til 500))))) +(set B (table [k v w] (list (til 500) (til 500) (as 'F64 (til 500))))) +(upsert 'A 'k (list KP (til 400) (as 'F64 (til 400)))) -- 'A +(rowwise 'B 'k (table [k v w] (list KP (til 400) (as 'F64 (til 400)))) [k v w]) +A -- B + +;; ── a shared target is copied, not written through ── +(set A (book (til 500) (til 500))) +(set AL A) +(set B (book (til 500) (til 500))) +(set P (book KP (+ 1000 (til 400)))) +(upsert 'A 3 P) +(rowwise 'B 3 P [instrument side px sz ts_evt ts_recv]) +A -- B +AL -- (book (til 500) (til 500)) +;; and a value target returns a fresh table +(upsert (book (til 500) (til 500)) 3 P) -- B + +;; ── indexes and the sorted attribute never answer from stale cells ── +(set H (table [k v] (list (til 1000) (til 1000)))) +(set H (update {from: H v: (.idx.hash v)})) +(upsert 'H 'k (table [k v] (list [5 7 1000 1001 7] [-5 -70 -1000 -1001 -7]))) +(select {from: H where: (== v 5)}) -- (table [k v] (list (as 'I64 []) (as 'I64 []))) +(select {from: H where: (== v -7)}) -- (table [k v] (list [7] [-7])) +(select {from: H where: (== v -1001)}) -- (table [k v] (list [1001] [-1001])) +;; an update-only batch appends nothing, so only the scatter drops the index +(set H (table [k v] (list (til 1000) (til 1000)))) +(set H (update {from: H v: (.idx.hash v)})) +(.idx.has? (at H 'v)) -- true +(upsert 'H 'k (table [k v] (list [5 7 9 7] [-5 -70 -9 -7]))) +(count H) -- 1000 +(select {from: H where: (== v 7)}) -- (table [k v] (list (as 'I64 []) (as 'I64 []))) +(select {from: H where: (== v -7)}) -- (table [k v] (list [7] [-7])) +(set H (table [k v] (list (til 1000) (til 1000)))) +(set H (update {from: H k: (.idx.hash k)})) +(upsert 'H 'k (table [k v] (list [1001 3 1002] [1 -3 2]))) +(select {from: H where: (== k 1002)}) -- (table [k v] (list [1002] [2])) +(count H) -- 1002 +(set S (table [k v] (list (til 10) (.attr.set 'sorted (til 10))))) +(upsert 'S 'k (table [k v] (list [2 3] [100 -1]))) +(.attr.get (at S 'v)) -- (take ['a] 0) +(select {from: S where: (== v -1)}) -- (table [k v] (list [3] [-1])) + +;; ── growing the map past its capacity keeps every entry ── +;; A batch that does not fit re-places the live entries from the slots +;; themselves (each carries its key's hash) instead of rebuilding from the +;; columns; a delete first leaves tombstones for that pass to drop. +(set A (table [k v w] (list (til 300) (til 300) (as 'F64 (til 300))))) +(set B (table [k v w] (list (til 300) (til 300) (as 'F64 (til 300))))) +(set P (table [k v w] (list (+ 150 (til 4000)) (+ 1000 (til 4000)) (/ (as 'F64 (til 4000)) 8)))) +(upsert 'A 'k P) +(rowwise 'B 'k P [k v w]) +A -- B +(count A) -- 4150 +(delete {from: 'A where: (< k 100)}) +(delete {from: 'B where: (< k 100)}) +(set P (table [k v w] (list (+ 4150 (til 12000)) (+ 5000 (til 12000)) (/ (as 'F64 (til 12000)) 4)))) +(upsert 'A 'k P) +(rowwise 'B 'k P [k v w]) +A -- B +(count A) -- 16050 +;; two keys the grown map holds, and one the delete took out — appended again +(set P (table [k v w] (list [4149 16149 99] [-1 -2 -3] [1.0 2.0 3.0]))) +(upsert 'A 'k P) +(rowwise 'B 'k P [k v w]) +A -- B +(count A) -- 16051 + +;; ── duplicate keys keep their row order when the map grows ── +;; `insert` can append a second row carrying a key the table already holds. +;; The map enters rows in row order, so a keyed upsert updates the lowest of +;; them; re-placing the map into a bigger table must not change that, which it +;; does whenever a probe chain wraps past the end of the slot array. Both +;; sizes below expose such a chain. +(set dupgrow (fn [n dk] (do (set A (table [k v] (list (til n) (til n)))) (set B (table [k v] (list (til n) (til n)))) (upsert 'A 'k (table [k v] (list [0 1] [0 1]))) (upsert 'B 'k (table [k v] (list [0 1] [0 1]))) (insert 'A (list dk 999)) (insert 'B (list dk 999)) (set P (table [k v] (list (+ (* 10 n) (til (* 4 n))) (take [7] (* 4 n))))) (upsert 'A 'k P) (rowwise 'B 'k P [k v]) (set Q (table [k v] (list (as 'I64 (list dk (+ (* 10 n) 1))) (as 'I64 (list -1 -2))))) (upsert 'A 'k Q) (rowwise 'B 'k Q [k v]) (list (at (at A 'v) dk) (at (at A 'v) n) (count A))))) +(dupgrow 480 470) -- (list -1 999 2401) +A -- B +(dupgrow 640 470) -- (list -1 999 3201) +A -- B + +;; ── the key map stays exact across mixed writers ── +(set A (book (til 500) (til 500))) +(set B (book (til 500) (til 500))) +(set P (book KP (+ 1000 (til 400)))) +(upsert 'A 3 P) +(rowwise 'B 3 P [instrument side px sz ts_evt ts_recv]) +(upsert 'A 3 (list 'i3 'A 100003 -1 TS TS)) +(upsert 'B 3 (list 'i3 'A 100003 -1 TS TS)) +(delete {from: 'A where: (< sz 100)}) +(delete {from: 'B where: (< sz 100)}) +(set P (book (% (* 104729 (til 400)) 700) (+ 5000 (til 400)))) +(upsert 'A 3 P) +(rowwise 'B 3 P [instrument side px sz ts_evt ts_recv]) +A -- B +(count A) -- (count (distinct (at A 'px))) diff --git a/test/rfl/window/partitions_past_task_cap.rfl b/test/rfl/window/partitions_past_task_cap.rfl new file mode 100644 index 000000000..7b1d34cf2 --- /dev/null +++ b/test/rfl/window/partitions_past_task_cap.rfl @@ -0,0 +1,18 @@ +;; A window with more partitions than the pool's task ring can hold at once +;; (65536) must still compute every partition. Before the fix the pool +;; silently dropped the tasks past the ring, and the tail partitions came back +;; as the NULL placeholder the result was pre-filled with. +(set N 70000) +(set K (til N)) +(set V (til N)) +(set T (table [k v] (list K V))) +(set R (window {from: T part: [k] funcs: {mx: (max v)}})) +(count R) -- N +(all (== (at R 'mx) V)) -- true +(sum (as 'I64 (== (at R 'mx) 0Nl))) -- 0 +(set R2 (window {from: T part: [k] order: [v] frame: 'running funcs: {cs: (sum v)}})) +(all (== (at R2 'cs) V)) -- true +;; exactly one past the ring +(set N1 65537) +(set T1 (table [k v] (list (til N1) (til N1)))) +(all (== (at (window {from: T1 part: [k] funcs: {mx: (max v)}}) 'mx) (til N1))) -- true diff --git a/test/test_agg_contract.c b/test/test_agg_contract.c new file mode 100644 index 000000000..b5f1531d4 --- /dev/null +++ b/test/test_agg_contract.c @@ -0,0 +1,1761 @@ +/* Scalar/grouped semantic contracts, independent of optimized admission. + * Baseline census: docs/aggregation-type-census.csv. Extend this table when + * language semantics change; adding a vtable alone must not change it. */ +#include "test.h" +#include "test_rfl.h" +#include "ops/agg_engine.h" +#include "ops/agg_registry.h" +#include "core/pool.h" +#include "mem/heap.h" +#include "ops/fused_pred.h" +#include "ops/cdfuse.h" +#include "ops/internal.h" +#include "lang/internal.h" +#include "lang/env.h" +#include + +static ray_runtime_t* contract_runtime; +static uint32_t contract_cores; +static void contract_setup(void) { + contract_runtime = ray_runtime_create(0, NULL); + contract_cores = ray_pool_total_workers(ray_pool_get()); + ray_agg_engine_v2 = true; + agg_route_reset(); +} +static void contract_teardown(void) { + ray_agg_engine_v2 = true; + ray_runtime_destroy(contract_runtime); + if (ray_pool_total_workers(ray_pool_get()) != contract_cores) { + ray_pool_destroy(); + ray_pool_init_total(contract_cores); + } +} + +static const char* const unary_names[] = { + "count", "sum", "avg", "min", "max", "first", "last", "prod", + "var", "var_pop", "stddev", "stddev_pop", "all", "any", "med" +}; +static const struct { + int8_t input; + int8_t output[15]; /* 0: deliberately illegal, otherwise logical vector type */ +} unary_contracts[] = { + { RAY_BOOL, { RAY_I64, RAY_I64, RAY_F64, RAY_BOOL, RAY_BOOL, RAY_BOOL, RAY_BOOL, RAY_I64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_BOOL, RAY_BOOL, RAY_F64 } }, + { RAY_U8, { RAY_I64, RAY_I64, RAY_F64, RAY_U8, RAY_U8, RAY_U8, RAY_U8, RAY_I64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_BOOL, RAY_BOOL, RAY_F64 } }, + { RAY_I16, { RAY_I64, RAY_I64, RAY_F64, RAY_I16, RAY_I16, RAY_I16, RAY_I16, RAY_I64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_BOOL, RAY_BOOL, RAY_F64 } }, + { RAY_I32, { RAY_I64, RAY_I64, RAY_F64, RAY_I32, RAY_I32, RAY_I32, RAY_I32, RAY_I64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_BOOL, RAY_BOOL, RAY_F64 } }, + { RAY_I64, { RAY_I64, RAY_I64, RAY_F64, RAY_I64, RAY_I64, RAY_I64, RAY_I64, RAY_I64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_BOOL, RAY_BOOL, RAY_F64 } }, + { RAY_F32, { RAY_I64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_BOOL, RAY_BOOL, RAY_F64 } }, + { RAY_F64, { RAY_I64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_BOOL, RAY_BOOL, RAY_F64 } }, + { RAY_DATE, { RAY_I64, 0, RAY_F64, RAY_DATE, RAY_DATE, RAY_DATE, RAY_DATE, 0, RAY_F64, RAY_F64, RAY_F64, RAY_F64, 0, 0, RAY_F64 } }, + { RAY_TIME, { RAY_I64, RAY_TIME, RAY_F64, RAY_TIME, RAY_TIME, RAY_TIME, RAY_TIME, 0, RAY_F64, RAY_F64, RAY_F64, RAY_F64, 0, 0, RAY_F64 } }, + { RAY_TIMESTAMP, { RAY_I64, 0, RAY_F64, RAY_TIMESTAMP, RAY_TIMESTAMP, RAY_TIMESTAMP, RAY_TIMESTAMP, 0, RAY_F64, RAY_F64, RAY_F64, RAY_F64, 0, 0, RAY_F64 } }, + { RAY_GUID, { RAY_I64, 0, 0, RAY_GUID, RAY_GUID, RAY_GUID, RAY_GUID, 0, 0, 0, 0, 0, 0, 0, 0 } }, + { RAY_SYM, { RAY_I64, 0, 0, RAY_SYM, RAY_SYM, RAY_SYM, RAY_SYM, 0, 0, 0, 0, 0, 0, 0, 0 } }, + { RAY_STR, { RAY_I64, 0, 0, RAY_STR, RAY_STR, RAY_STR, RAY_STR, 0, 0, 0, 0, 0, 0, 0, 0 } }, + { RAY_LIST, { RAY_I64, RAY_I64, RAY_F64, RAY_I64, RAY_I64, RAY_I64, RAY_I64, RAY_I64, RAY_F64, RAY_F64, RAY_F64, RAY_F64, RAY_BOOL, RAY_BOOL, RAY_F64 } }, +}; + +static ray_t* contract_fixture(int8_t type) { + if (type == RAY_GUID) return ray_eval_str("(guid 4)"); + if (type == RAY_SYM) return ray_eval_str("['b 'a 'b 'c]"); + if (type == RAY_STR) return ray_eval_str("[\"b\" \"a\" \"b\" \"c\"]"); + if (type == RAY_LIST) return ray_eval_str("(list 1 2 3 4)"); + ray_t* v = ray_vec_new(type, 4); + if (!v || RAY_IS_ERR(v)) return v; + v->len = 4; + for (int i = 0; i < 4; i++) { + switch (type) { + case RAY_BOOL: ((uint8_t*)ray_data(v))[i] = i % 2; break; + case RAY_U8: ((uint8_t*)ray_data(v))[i] = i + 1; break; + case RAY_I16: ((int16_t*)ray_data(v))[i] = i + 1; break; + case RAY_I32: case RAY_DATE: case RAY_TIME: + ((int32_t*)ray_data(v))[i] = i + 1; break; + case RAY_I64: case RAY_TIMESTAMP: + ((int64_t*)ray_data(v))[i] = i + 1; break; + case RAY_F32: ((float*)ray_data(v))[i] = (float)i + 1.25f; break; + case RAY_F64: ((double*)ray_data(v))[i] = (double)i + 1.25; break; + } + } + return v; +} + +static test_result_t test_unary_contracts(void) { + for (size_t t = 0; t < sizeof(unary_contracts)/sizeof(unary_contracts[0]); t++) { + const int8_t type = unary_contracts[t].input; + ray_t* v = contract_fixture(type); + TEST_ASSERT_NOT_NULL(v); + TEST_ASSERT_FALSE(RAY_IS_ERR(v)); + TEST_ASSERT_EQ_I(v->type, type); + ray_env_set(ray_sym_intern("v", 1), v); + ray_release(v); + ray_t* init = ray_eval_str("(set t (table [k v] (list [0 0 1 1] v)))"); + TEST_ASSERT_FALSE(RAY_IS_ERR(init)); ray_release(init); + for (size_t a = 0; a < sizeof(unary_names)/sizeof(unary_names[0]); a++) { + char source[256]; + snprintf(source, sizeof(source), + "(at (select {from:t by:k s:(%s v) asc:k}) 's)", unary_names[a]); + ray_t* got = ray_eval_str(source); + int8_t output = unary_contracts[t].output[a]; + if (!output) { + bool error = RAY_IS_ERR(got); + if (error) ray_error_free(got); else ray_release(got); + TEST_ASSERT_FMT(error, "%s(%s) grouped must reject input", + unary_names[a], ray_type_name(type)); + snprintf(source, sizeof(source), "(%s v)", unary_names[a]); + got = ray_eval_str(source); + error = RAY_IS_ERR(got); + if (error) ray_error_free(got); else ray_release(got); + TEST_ASSERT_FMT(error, "%s(%s) scalar must reject input", + unary_names[a], ray_type_name(type)); + continue; + } + TEST_ASSERT_FMT(got && !RAY_IS_ERR(got), "%s(%s) grouped failed", + unary_names[a], ray_type_name(type)); + TEST_ASSERT_FMT(got->type == output && got->len == 2, + "%s(%s): got %s, expected %s[2]", + unary_names[a], ray_type_name(type), + ray_type_name(got->type), ray_type_name(output)); + for (int64_t group = 0; group < 2; group++) { + snprintf(source, sizeof(source), "(%s (at v [%lld %lld]))", + unary_names[a], (long long)(2*group), (long long)(2*group+1)); + ray_t* want = ray_eval_str(source); + TEST_ASSERT_FMT(want && !RAY_IS_ERR(want), "%s scalar failed", source); + ray_t* idx = ray_i64(group); + ray_t* cell = ray_at_fn(got, idx); ray_release(idx); + TEST_ASSERT_FMT(cell->type == want->type, "%s: grouped/scalar type mismatch", source); + bool equal; + if (cell->type == -RAY_F64) { + equal = (isnan(cell->f64) && isnan(want->f64)) || + fabs(cell->f64 - want->f64) <= 1e-12; + } else { + ray_t* eq = ray_eq_fn(cell, want); + equal = eq && eq->type == -RAY_BOOL && eq->b8; + ray_release(eq); + } + ray_release(cell); ray_release(want); + TEST_ASSERT_FMT(equal, "%s(%s), group %lld differs from scalar slice", + unary_names[a], ray_type_name(type), (long long)group); + } + ray_release(got); + } + } + PASS(); +} + +/* Exercise the direct serial emitter against scalar language semantics, + * including states with no rows, no live rows, and wrapped/null results. */ +static test_result_t test_native_streaming_output(void) { + const uint16_t ops[] = { OP_COUNT, OP_SUM, OP_AVG, OP_MIN, OP_MAX, OP_FIRST, + OP_LAST, OP_PROD, OP_VAR, OP_VAR_POP, OP_STDDEV, OP_STDDEV_POP, OP_ALL, OP_ANY, OP_MEDIAN }; + const uint32_t gids[] = { 0, 0, 1, 1 }; + for (size_t t = 0; t < 10; t++) { + int8_t type = unary_contracts[t].input; + for (int shape = 0; shape < 3; shape++) { + ray_t* v = contract_fixture(type); + if (shape == 1 && type != RAY_BOOL && type != RAY_U8) { + for (int i = 0; i < 4; i++) ray_vec_set_null(v, i, true); + } else if (shape == 2) { + if (type == RAY_I64) { + int64_t* d = ray_data(v); + d[0] = INT64_MAX; d[1] = 1; d[2] = INT64_MAX; d[3] = 2; + } else if (type == RAY_TIME) { + int32_t* d = ray_data(v); + d[0] = INT32_MAX; d[1] = 1; d[2] = INT32_MAX; d[3] = 2; + } else if (type == RAY_F64) { + double* d = ray_data(v); + d[0] = 1e308; d[1] = 1e308; d[2] = INFINITY; d[3] = -INFINITY; + } + } + ray_env_set(ray_sym_intern("v", 1), v); + for (size_t a = 0; a < sizeof(ops)/sizeof(ops[0]); a++) { + /* Integer grouped statistics intentionally retain wrapped + * sum-of-squares semantics; scalar variance uses another + * algorithm. Overflow here tests sum/product output only. */ + if (shape == 2 && ops[a] != OP_SUM && ops[a] != OP_PROD) continue; + const agg_vtable_t* vt = agg_resolve(ops[a], type); + if (!vt || vt->kind != ACC_STREAMING) continue; + ray_t* out = agg_run_one(vt, v, gids, 4, 3, 0); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + for (int g = 0; g < 3; g++) { + /* Non-nullable extrema have no representable empty vector + * cell; real grouping never emits an absent group. */ + if (g == 2 && (type == RAY_BOOL || type == RAY_U8) && + (ops[a] == OP_MIN || ops[a] == OP_MAX)) continue; + char source[128]; + if (g == 2) snprintf(source, sizeof(source), "(%s (take v 0))", unary_names[a]); + else snprintf(source, sizeof(source), "(%s (at v [%d %d]))", unary_names[a], 2*g, 2*g+1); + /* Grouped product is null when there are no live rows. */ + ray_t* want = ops[a] == OP_PROD && (g == 2 || + (shape == 1 && type != RAY_BOOL && type != RAY_U8)) + ? ray_typed_null(-vt->out_type) : ray_eval_str(source); + TEST_ASSERT_NOT_NULL(want); TEST_ASSERT_FALSE(RAY_IS_ERR(want)); + ray_t* index = ray_i64(g); + ray_t* got = ray_at_fn(out, index); ray_release(index); + TEST_ASSERT_EQ_I(got->type, want->type); + bool same; + if (want->type == -RAY_F64) + same = (isnan(got->f64) && isnan(want->f64)) || + fabs(got->f64 - want->f64) <= 1e-12 * fmax(1.0, fabs(want->f64)); + else { + ray_t* gs = ray_fmt(got, 0); ray_t* ws = ray_fmt(want, 0); + same = strcmp(ray_str_ptr(gs), ray_str_ptr(ws)) == 0; + ray_release(gs); ray_release(ws); + } + if (RAY_ATOM_IS_NULL(want)) TEST_ASSERT_TRUE(out->attrs & RAY_ATTR_HAS_NULLS); + ray_release(got); ray_release(want); + TEST_ASSERT_FMT(same, "native %s/%s shape %d group %d", unary_names[a], ray_type_name(type), shape, g); + } + ray_release(out); + } + ray_release(v); + } + } + PASS(); +} + +/* Registry presence and execution admission are different contracts: buffered + * I64/F64 vtables exist today, but must not silently pass streaming admission. */ +static test_result_t test_registry_admission_contracts(void) { + const uint16_t operations[] = { + OP_COUNT, OP_SUM, OP_MIN, OP_MAX, OP_AVG, OP_VAR, OP_VAR_POP, + OP_STDDEV, OP_STDDEV_POP, OP_FIRST, OP_LAST, OP_PROD, OP_ALL, OP_ANY, + OP_MEDIAN, OP_TOP_N, OP_BOT_N, OP_QUANTILE, OP_MODE, + OP_PEARSON_CORR, OP_COV, OP_SCOV, OP_WSUM, OP_WAVG + }; + for (size_t t = 0; t < sizeof(unary_contracts)/sizeof(unary_contracts[0]); t++) { + int8_t type = unary_contracts[t].input; + ray_t* v = contract_fixture(type); + TEST_ASSERT_NOT_NULL(v); + ray_t* k = ray_eval_str("[0 0 1 1]"); + ray_t* tbl = ray_table_new(2); + tbl = ray_table_add_col(tbl, ray_sym_intern("k", 1), k); + tbl = ray_table_add_col(tbl, ray_sym_intern("v", 1), v); + ray_release(k); ray_release(v); + for (size_t a = 0; a < sizeof(operations)/sizeof(operations[0]); a++) { + uint16_t op = operations[a]; + bool numeric = type == RAY_BOOL || type == RAY_U8 || type == RAY_I16 || + type == RAY_I32 || type == RAY_I64 || type == RAY_F32 || type == RAY_F64; + bool wide_numeric = type == RAY_I64 || type == RAY_F64; + bool binary = agg_is_binary_agg(op); + bool buffered = op == OP_MEDIAN || op == OP_TOP_N || op == OP_BOT_N; + bool temporal = type == RAY_DATE || type == RAY_TIME || type == RAY_TIMESTAMP; + bool registered = op == OP_COUNT || + ((op == OP_MIN || op == OP_MAX || op == OP_AVG || + op == OP_VAR || op == OP_VAR_POP || op == OP_STDDEV || + op == OP_STDDEV_POP) && (numeric || temporal)) || + (op == OP_SUM && (numeric || type == RAY_TIME)) || + (op == OP_PROD && numeric) || + (buffered && wide_numeric) || + ((op == OP_ALL || op == OP_ANY || binary) && numeric); + const agg_vtable_t* vt = agg_resolve(op, type); + TEST_ASSERT_FMT((vt != NULL) == registered, "registry %u/%s changed", op, ray_type_name(type)); + if (vt && vt->kind == ACC_STREAMING) + TEST_ASSERT_FMT(vt->finalize_value != NULL, + "streaming %u/%s must emit native values", op, ray_type_name(type)); + ray_graph_t* graph = ray_graph_new(tbl); + ray_op_t* keys[] = { ray_scan(graph, "k") }; + ray_op_t* ins[] = { ray_scan(graph, "v") }; + int64_t param[] = { op == OP_TOP_N || op == OP_BOT_N ? 2 : 0 }; + ray_op_t* group = ray_group_build(graph, keys, 1, &op, ins, + binary ? ins : NULL, param, 1); + bool indexed = ((op == OP_FIRST || op == OP_LAST) && (numeric || temporal || type == RAY_GUID || type == RAY_SYM || type == RAY_STR || type == RAY_LIST)) || + ((op == OP_MIN || op == OP_MAX) && (type == RAY_GUID || type == RAY_SYM || type == RAY_STR)) || + ((op == OP_MEDIAN || op == OP_QUANTILE) && (numeric || temporal)) || + ((op == OP_MODE || op == OP_TOP_N || op == OP_BOT_N) && (numeric || temporal || type == RAY_GUID || type == RAY_SYM || type == RAY_STR)); + agg_v2_reason_t want = indexed ? AGG_V2_ADMITTED : !registered ? AGG_V2_AGG_TYPE + : buffered ? AGG_V2_BUFFERED : AGG_V2_ADMITTED; + TEST_ASSERT_FMT(agg_v2_admission(graph, group, tbl) == want, + "admission %u/%s differs", op, ray_type_name(type)); + TEST_ASSERT_EQ_I(agg_v2_can_handle(graph, group, tbl), want == AGG_V2_ADMITTED); + ray_graph_free(graph); + } + ray_release(tbl); + } + /* Pure inspection must not fabricate any execution. */ + agg_route_stats_t stats = agg_route_stats(); + for (int i = 0; i < AGG_ROUTE_COUNT; i++) TEST_ASSERT_EQ_I(stats.routes[i], 0); + PASS(); +} + +/* A single-expression `by:` over one SYM column with few distinct symbols + * is evaluated once per symbol (agg_route_stats().key_domain_evals); a + * positional expression, a two-column expression, a shadowed builtin and a + * table below the row gate all take the row-wise key. */ +static test_result_t test_derived_key_per_symbol_route(void) { + static const char* const setup = + "(set i (til 8192)) " + "(set s (as 'SYMBOL (map (fn [k] (format \"h%.x\" (% k 16))) i))) " + "(set v (as 'F64 (% i 7))) " + "(set T (table [s v] (list s v))) " + "(set T3 (take T 3000))"; + ray_t* r = ray_eval_str(setup); + TEST_ASSERT_NOT_NULL(r); TEST_ASSERT_FALSE(RAY_IS_ERR(r)); ray_release(r); + const struct { const char* q; uint64_t evals; } cases[] = { + { "(select {from: T by: (substr s 0 2) c: (count v)})", 1 }, + { "(select {from: T by: (let p (str-find s \".\") (if (> p 1) (substr s 0 p) s)) c: (count v)})", 1 }, + { "(select {from: T by: (differ s) c: (count v)})", 0 }, + { "(select {from: T by: (if (> v 3) (substr s 0 2) s) c: (count v)})", 0 }, + { "(select {from: T3 by: (substr s 0 2) c: (count v)})", 0 }, + }; + for (size_t c = 0; c < sizeof(cases)/sizeof(cases[0]); c++) { + agg_route_reset(); + ray_t* out = ray_eval_str(cases[c].q); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + ray_release(out); + TEST_ASSERT_EQ_I(agg_route_stats().key_domain_evals, cases[c].evals); + } + /* A user lambda shadowing `substr` is what the compiled key would call. */ + r = ray_eval_str("(set substr (fn [x a b] x))"); + TEST_ASSERT_NOT_NULL(r); TEST_ASSERT_FALSE(RAY_IS_ERR(r)); ray_release(r); + agg_route_reset(); + r = ray_eval_str("(select {from: T by: (substr s 0 2) c: (count v)})"); + TEST_ASSERT_NOT_NULL(r); TEST_ASSERT_FALSE(RAY_IS_ERR(r)); ray_release(r); + TEST_ASSERT_EQ_I(agg_route_stats().key_domain_evals, 0); + PASS(); +} + +static test_result_t test_group_routes_and_bool_outputs(void) { + /* Route expectations below use two cores regardless of harness settings. */ + ray_pool_destroy(); + TEST_ASSERT_EQ_I(ray_pool_init_total(2), RAY_OK); + /* 65536 rows exercises parallel finalization with one output per group. + * all/any BOOL outputs previously wrote at 8-byte strides into byte vectors. */ + const struct { int64_t n; int mode; agg_route_t route; } cases[] = { + { 32, 0, AGG_ROUTE_V2_SERIAL_DENSE }, + { 32, 1, AGG_ROUTE_V2_SERIAL_HASH }, + { RAY_PARALLEL_THRESHOLD, 0, AGG_ROUTE_V2_DENSE }, + { RAY_PARALLEL_THRESHOLD, 1, AGG_ROUTE_V2_RADIX }, + { RAY_PARALLEL_THRESHOLD, 2, AGG_ROUTE_V2_DENSE }, + { RAY_PARALLEL_THRESHOLD, 3, AGG_ROUTE_V2_DENSE }, + { RAY_PARALLEL_THRESHOLD, 4, AGG_ROUTE_V2_RADIX }, + }; + for (size_t c = 0; c < sizeof(cases)/sizeof(cases[0]); c++) { + int64_t n = cases[c].n; + ray_t* k = ray_vec_new(RAY_I64, n); k->len = n; + ray_t* v = ray_vec_new(RAY_BOOL, n); v->len = n; + for (int64_t i = 0; i < n; i++) { + int64_t key = cases[c].mode >= 3 ? i : i % 4; + ((int64_t*)ray_data(k))[i] = (cases[c].mode == 1 || cases[c].mode == 4) ? key * 1000000 : key; + ((uint8_t*)ray_data(v))[i] = key % 2; + } + if (cases[c].mode == 2) ray_vec_set_null(k, 0, true); + ray_t* tbl = ray_table_new(2); + tbl = ray_table_add_col(tbl, ray_sym_intern("k", 1), k); + tbl = ray_table_add_col(tbl, ray_sym_intern("v", 1), v); + ray_release(k); ray_release(v); + ray_graph_t* graph = ray_graph_new(tbl); + ray_op_t* keys[] = { ray_scan(graph, "k") }; + ray_op_t* val = ray_scan(graph, "v"); + ray_op_t* inputs[] = { val, val }; + uint16_t ops[] = { OP_ALL, OP_ANY }; + ray_op_t* group = ray_group(graph, keys, 1, ops, inputs, 2); + agg_route_reset(); + ray_t* out = ray_execute(graph, group); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + agg_route_stats_t stats = agg_route_stats(); + TEST_ASSERT_EQ_I(stats.routes[cases[c].route], 1); + TEST_ASSERT_EQ_I(stats.routes[AGG_ROUTE_LEGACY], 0); + TEST_ASSERT_EQ_I(stats.nullable_key, cases[c].mode == 2); + TEST_ASSERT_FALSE(stats.dense_worker_budget); + ray_t* out_keys = ray_table_get_col_idx(out, 0); + for (int a = 1; a <= 2; a++) { + ray_t* col = ray_table_get_col_idx(out, a); + TEST_ASSERT_EQ_I(col->type, RAY_BOOL); + for (int64_t i = 0; i < col->len; i++) { + int64_t key = ((int64_t*)ray_data(out_keys))[i]; + if (key == NULL_I64) key = 0; + if (cases[c].mode == 1 || cases[c].mode == 4) key /= 1000000; + TEST_ASSERT_EQ_I(((uint8_t*)ray_data(col))[i], key % 2); + } + } + ray_release(out); ray_graph_free(graph); ray_release(tbl); + } + agg_route_reset(); + ray_t* r = ray_eval_str("(select {from:(table [k v] (list [0 0 1 1] (as 'TIME [1 2 3 4]))) by:k s:(min v)})"); + TEST_ASSERT_FALSE(RAY_IS_ERR(r)); ray_release(r); + agg_route_stats_t stats = agg_route_stats(); + TEST_ASSERT_EQ_I(stats.routes[AGG_ROUTE_V2_SERIAL_DENSE], 1); + TEST_ASSERT_EQ_I(stats.last_v2_reason, AGG_V2_ADMITTED); + /* Shared extrema keep one state per group even when every input range + * visits the whole key domain. Pool size no longer multiplies state. */ + ray_pool_destroy(); + TEST_ASSERT_EQ_I(ray_pool_init_total(8), RAY_OK); + ray_t* setup = ray_eval_str("(set traffic_i (til 1000000)) (set traffic_g (% (* traffic_i 17) 80000)) (set traffic_t (table [k v] (list (as 'I32 (+ (* traffic_g 2) (div traffic_g 4))) (as 'TIME traffic_g))))"); + TEST_ASSERT_NOT_NULL(setup); TEST_ASSERT_FALSE(RAY_IS_ERR(setup)); ray_release(setup); + agg_route_reset(); + r = ray_eval_str("(select {from:traffic_t by:k s:(min v)})"); + TEST_ASSERT_NOT_NULL(r); TEST_ASSERT_FALSE(RAY_IS_ERR(r)); + stats = agg_route_stats(); + TEST_ASSERT_TRUE(stats.dense_plan_available); + TEST_ASSERT_FALSE(stats.dense_worker_budget); + TEST_ASSERT_EQ_I(stats.routes[AGG_ROUTE_V2_DENSE], 1); + TEST_ASSERT_EQ_I(stats.dense_strategy, AGG_DENSE_PARTITIONED); + TEST_ASSERT_EQ_I(ray_table_nrows(r), 80000); + ray_release(r); + /* A larger machine must not lose dense execution solely because a slab + * for every physical worker would exceed the budget. Logical task IDs + * own the slabs, including selected-row tasks run by a larger pool. */ + ray_pool_destroy(); + TEST_ASSERT_EQ_I(ray_pool_init_total(20), RAY_OK); + setup = ray_eval_str("(set traffic_g (div traffic_i 13)) (set traffic_t (table [k v] (list (as 'I32 (+ (* traffic_g 2) (div traffic_g 4))) (as 'TIME traffic_g))))"); + TEST_ASSERT_NOT_NULL(setup); TEST_ASSERT_FALSE(RAY_IS_ERR(setup)); ray_release(setup); + const char* queries[] = { + "(select {from:traffic_t by:k s:(min v)})", + "(select {from:traffic_t by:k s:(min v) where:(> v 100)})" + }; + for (int selected = 0; selected < 2; selected++) { + agg_route_reset(); r = ray_eval_str(queries[selected]); + TEST_ASSERT_NOT_NULL(r); TEST_ASSERT_FALSE(RAY_IS_ERR(r)); + stats = agg_route_stats(); + TEST_ASSERT_EQ_I(stats.routes[AGG_ROUTE_V2_DENSE], 1); + TEST_ASSERT_TRUE(stats.dense_tasks > 0); + TEST_ASSERT_EQ_I(stats.dense_strategy, AGG_DENSE_PARTITIONED); + TEST_ASSERT_TRUE(stats.dense_tasks <= RAY_POOL_INIT_TASKS); + TEST_ASSERT_TRUE(stats.dense_local_slots < 400000); + TEST_ASSERT_EQ_I(ray_pool_total_workers(ray_pool_get()), 20); + TEST_ASSERT_EQ_I(ray_table_nrows(r), 76924 - selected * 101); + ray_t* keys = ray_table_get_col_idx(r, 0); + ray_t* values = ray_table_get_col_idx(r, 1); + TEST_ASSERT_EQ_I(values->type, RAY_TIME); + for (int64_t row = 0; row < values->len; row++) { + int32_t value = ((int32_t*)ray_data(values))[row]; + TEST_ASSERT_EQ_I(((int32_t*)ray_data(keys))[row], value * 2 + value / 4); + TEST_ASSERT_TRUE(!selected || value > 100); + } + ray_release(r); + } + PASS(); +} + +static test_result_t test_narrow_extrema_limits(void) { + const int8_t types[] = {RAY_BOOL, RAY_U8, RAY_I16, RAY_I32, RAY_DATE, RAY_TIME}; + for (size_t t = 0; t < sizeof(types) / sizeof(types[0]); t++) { + for (int maximum = 0; maximum < 2; maximum++) { + int8_t type = types[t]; + int width = type == RAY_BOOL || type == RAY_U8 ? 1 : type == RAY_I16 ? 2 : 4; + int32_t expected = width == 1 ? (type == RAY_BOOL ? 1 : 255) + : maximum ? (width == 2 ? INT16_MIN + 1 : INT32_MIN + 1) + : (width == 2 ? INT16_MAX : INT32_MAX); + ray_t* val = ray_vec_new(type, 2); + val->len = 2; + if (width == 1) { ((uint8_t*)ray_data(val))[0] = expected; ((uint8_t*)ray_data(val))[1] = expected; } + else if (width == 2) { ((int16_t*)ray_data(val))[0] = expected; ((int16_t*)ray_data(val))[1] = NULL_I16; } + else { ((int32_t*)ray_data(val))[0] = expected; ((int32_t*)ray_data(val))[1] = NULL_I32; } + const agg_vtable_t* vt = agg_resolve(maximum ? OP_MAX : OP_MIN, type); + TEST_ASSERT_EQ_I(vt->state_size, 8); + uint64_t states[2]; + vt->init(&states[0]); vt->init(&states[1]); + uint32_t gids[] = {0, 0}; + ray_valid_t valid = {ray_data(val), type, width != 1}; + vt->update_batch(states, vt->state_size, gids, ray_data(val), &valid, 2, NULL); + vt->merge(&states[1], &states[0], NULL); + union { uint64_t align[2]; uint8_t bytes[16]; } output; + memset(output.bytes, 0xa5, sizeof(output.bytes)); + TEST_ASSERT_FALSE(vt->finalize_value(&states[1], output.bytes)); + int64_t got = width == 1 ? output.bytes[0] : width == 2 + ? *(int16_t*)output.bytes : *(int32_t*)output.bytes; + TEST_ASSERT_EQ_I(got, expected); + for (int i = width; i < 16; i++) TEST_ASSERT_EQ_I(output.bytes[i], 0xa5); + vt->init(&states[0]); + TEST_ASSERT_TRUE(vt->finalize_value(&states[0], output.bytes)); + ray_release(val); + } + } + PASS(); +} + +/* Compare shared and partitioned execution with an independent row oracle. + * A frequent null key stresses contention/skew; one live key has no valid + * aggregate values. Native widths, mixed COUNT, binary inputs, and repeated + * scratch reuse all pass through the common output stage. */ +static test_result_t test_dense_strategies(void) { + ray_pool_destroy(); + TEST_ASSERT_EQ_I(ray_pool_init_total(4), RAY_OK); + const int8_t types[] = {RAY_BOOL, RAY_U8, RAY_I16, RAY_I32, RAY_DATE, RAY_TIME, RAY_F32, RAY_F64, RAY_I64, RAY_TIMESTAMP}; + const int64_t n = 262144; + for (size_t t = 0; t < sizeof(types) / sizeof(types[0]); t++) { + int8_t type = types[t]; + bool floating = type == RAY_F32 || type == RAY_F64; + bool wide_int = type == RAY_I64 || type == RAY_TIMESTAMP; + int width = type == RAY_BOOL || type == RAY_U8 ? 1 : type == RAY_I16 ? 2 : type == RAY_F64 || wide_int ? 8 : 4; + ray_t* key = ray_vec_new(RAY_I32, n); + ray_t* val = ray_vec_new(type, n); + key->len = val->len = n; + if (type == RAY_LIST) memset(ray_data(key), 0, (size_t)n * sizeof(ray_t*)); + key->attrs |= RAY_ATTR_HAS_NULLS; + if (width != 1) val->attrs |= RAY_ATTR_HAS_NULLS; + int64_t mn[8193], mx[8193], counts[8193] = {0}, sums[8193] = {0}; + for (int k = 0; k <= 8192; k++) { mn[k] = INT64_MAX; mx[k] = INT64_MIN; } + for (int64_t i = 0; i < n; i++) { + int32_t k = i / 2 % 8192 - 4096; + bool null_key = i % 3 == 0; + int group = null_key ? 8192 : k + 4096; + bool null_value = width != 1 && (i % 97 == 0 || k == -1000); + int32_t value = width == 1 ? i % (type == RAY_BOOL ? 2 : 251) : i % 10007 - 5000; + ((int32_t*)ray_data(key))[i] = null_key ? NULL_I32 : k * 8; + if (type == RAY_F32) ((float*)ray_data(val))[i] = null_value ? NAN : value; + else if (type == RAY_F64) ((double*)ray_data(val))[i] = null_value ? NAN : value; + else if (width == 1) ((uint8_t*)ray_data(val))[i] = value; + else if (width == 2) ((int16_t*)ray_data(val))[i] = null_value ? NULL_I16 : value; + else if (wide_int) ((int64_t*)ray_data(val))[i] = null_value ? NULL_I64 : value; + else ((int32_t*)ray_data(val))[i] = null_value ? NULL_I32 : value; + counts[group]++; + if (!null_value) { + sums[group] += value; + if (value < mn[group]) mn[group] = value; + if (value > mx[group]) mx[group] = value; + } + } + ray_t* tbl = ray_table_new(2); + tbl = ray_table_add_col(tbl, ray_sym_intern("k", 1), key); + tbl = ray_table_add_col(tbl, ray_sym_intern("v", 1), val); + ray_release(key); ray_release(val); + for (int mode = 0; mode < 4; mode++) { + if (mode == 2 && type != RAY_I32) continue; + if (mode == 3 && (type == RAY_DATE || type == RAY_TIMESTAMP)) continue; + for (int repeat = 0; repeat < 2; repeat++) { + ray_graph_t* graph = ray_graph_new(tbl); + ray_op_t* keys[] = {ray_scan(graph, "k")}; + ray_op_t* v = ray_scan(graph, "v"); + ray_op_t* inputs[] = {v, v, v}; + uint16_t ops[] = {OP_MIN, OP_MAX, OP_COUNT}; + ray_op_t* group; + if (mode == 2) { + uint16_t binary = OP_PEARSON_CORR; + group = ray_group_build(graph, keys, 1, &binary, inputs, inputs, NULL, 1); + } else if (mode == 3) { + uint16_t sum = OP_SUM; + group = ray_group(graph, keys, 1, &sum, inputs, 1); + } else group = ray_group(graph, keys, 1, ops, inputs, mode ? 3 : 2); + agg_route_reset(); + ray_t* out = ray_execute(graph, group); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + agg_route_stats_t stats = agg_route_stats(); + TEST_ASSERT_EQ_I(stats.routes[AGG_ROUTE_V2_DENSE], 1); +#if ATOMIC_LLONG_LOCK_FREE == 2 + TEST_ASSERT_EQ_I(stats.dense_strategy, mode || floating || wide_int ? AGG_DENSE_PARTITIONED : AGG_DENSE_SHARED); +#endif + TEST_ASSERT_TRUE(stats.dense_local_slots <= 2 * 65792); + TEST_ASSERT_EQ_I(ray_table_nrows(out), 8193); + ray_t* ko = ray_table_get_col_idx(out, 0); + for (int64_t row = 0; row < ko->len; row++) { + int32_t k = ((int32_t*)ray_data(ko))[row]; + int index = k == NULL_I32 ? 8192 : k / 8 + 4096; + TEST_ASSERT_TRUE(index >= 0 && index <= 8192); + if (mode == 3) { + ray_t* col = ray_table_get_col_idx(out, 1); + TEST_ASSERT_EQ_I(col->type, floating ? RAY_F64 : type == RAY_TIME ? RAY_TIME : RAY_I64); + if (floating) TEST_ASSERT_TRUE(((double*)ray_data(col))[row] == sums[index]); + else if (type == RAY_TIME) TEST_ASSERT_EQ_I(((int32_t*)ray_data(col))[row], sums[index]); + else TEST_ASSERT_EQ_I(((int64_t*)ray_data(col))[row], sums[index]); + } else if (mode == 2) { + double got = ((double*)ray_data(ray_table_get_col_idx(out, 1)))[row]; + TEST_ASSERT_TRUE(mn[index] == INT64_MAX ? isnan(got) : fabs(got - 1.0) < 1e-12); + } else { + for (int a = 0; a < 2; a++) { + ray_t* col = ray_table_get_col_idx(out, a + 1); + TEST_ASSERT_EQ_I(col->type, type == RAY_F32 ? RAY_F64 : type); + if (floating) { + double got = ((double*)ray_data(col))[row]; + TEST_ASSERT_TRUE(mn[index] == INT64_MAX ? isnan(got) : got == (a ? mx[index] : mn[index])); + continue; + } + int64_t got = width == 1 ? ((uint8_t*)ray_data(col))[row] + : width == 2 ? ((int16_t*)ray_data(col))[row] + : wide_int ? ((int64_t*)ray_data(col))[row] : ((int32_t*)ray_data(col))[row]; + int64_t want = mn[index] == INT64_MAX ? (width == 2 ? NULL_I16 : wide_int ? NULL_I64 : NULL_I32) + : a ? mx[index] : mn[index]; + TEST_ASSERT_EQ_I(got, want); + } + if (mode) TEST_ASSERT_EQ_I(((int64_t*)ray_data(ray_table_get_col_idx(out, 3)))[row], counts[index]); + } + } + ray_release(out); ray_graph_free(graph); + } + } + ray_release(tbl); + } + PASS(); +} + +static test_result_t test_dense_symbol_output(void) { + ray_pool_destroy(); + TEST_ASSERT_EQ_I(ray_pool_init_total(8), RAY_OK); + const int64_t n = 65536; + int64_t ids[8192]; + for (int k = 0; k < 8192; k++) { + char name[40]; + int len = snprintf(name, sizeof(name), "dense_key_%d", k); + ids[k] = ray_sym_intern(name, len); + } + const uint8_t widths[] = {RAY_SYM_W32, RAY_SYM_W64}; + for (size_t w = 0; w < sizeof(widths); w++) { + ray_t* key = ray_sym_vec_new(widths[w], n); + ray_t* val = ray_vec_new(RAY_TIME, n); + key->len = val->len = n; + for (int64_t row = 0; row < n; row++) { + ray_write_sym(ray_data(key), row, ids[row % 8192], RAY_SYM, key->attrs); + ((int32_t*)ray_data(val))[row] = row % 8192; + } + ray_t* tbl = ray_table_new(2); + tbl = ray_table_add_col(tbl, ray_sym_intern("k", 1), key); + tbl = ray_table_add_col(tbl, ray_sym_intern("v", 1), val); + ray_release(key); ray_release(val); + for (int mixed = 0; mixed < 2; mixed++) { + ray_graph_t* graph = ray_graph_new(tbl); + ray_op_t* keys[] = {ray_scan(graph, "k")}; + ray_op_t* value = ray_scan(graph, "v"); + ray_op_t* inputs[] = {value, value}; + uint16_t ops[] = {OP_MIN, OP_COUNT}; + ray_op_t* group = ray_group(graph, keys, 1, ops, inputs, mixed ? 2 : 1); + agg_route_reset(); + ray_t* out = ray_execute(graph, group); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + TEST_ASSERT_EQ_I(agg_route_stats().routes[AGG_ROUTE_V2_DENSE], 1); +#if ATOMIC_LLONG_LOCK_FREE == 2 + TEST_ASSERT_EQ_I(agg_route_stats().dense_strategy, AGG_DENSE_PARTITIONED); +#endif + TEST_ASSERT_EQ_I(ray_table_nrows(out), 8192); + ray_t* ko = ray_table_get_col_idx(out, 0); + ray_t* vo = ray_table_get_col_idx(out, 1); + TEST_ASSERT_EQ_I(ko->attrs & RAY_SYM_W_MASK, widths[w]); + TEST_ASSERT_EQ_I(vo->type, RAY_TIME); + for (int64_t row = 0; row < ko->len; row++) { + int32_t index = ((int32_t*)ray_data(vo))[row]; + TEST_ASSERT_TRUE(index >= 0 && index < 8192); + TEST_ASSERT_EQ_I(ray_read_sym(ray_data(ko), row, RAY_SYM, ko->attrs), ids[index]); + if (mixed) TEST_ASSERT_EQ_I(((int64_t*)ray_data(ray_table_get_col_idx(out, 2)))[row], n / 8192); + } + ray_release(out); ray_graph_free(graph); + } + ray_release(tbl); + } + PASS(); +} + +/* Task input ranges can be disjoint, cross bitmap words, and contain only nulls. + * Repeating the query also verifies that reused occupancy bits are cleared. */ +static test_result_t test_dense_task_local(void) { + ray_pool_destroy(); + TEST_ASSERT_EQ_I(ray_pool_init_total(4), RAY_OK); + const int8_t types[] = { RAY_I16, RAY_I32, RAY_I64, RAY_DATE, RAY_TIME, RAY_TIMESTAMP }; + const int64_t n = 400000; + for (size_t t = 0; t < sizeof(types) / sizeof(types[0]); t++) { + ray_t* key = ray_vec_new(types[t], n); + ray_t* val = ray_vec_new(RAY_TIME, n); + key->len = val->len = n; + key->attrs |= RAY_ATTR_HAS_NULLS; + val->attrs |= RAY_ATTR_HAS_NULLS; + int64_t counts[513] = {0}; + for (int64_t i = 0; i < n; i++) { + int64_t k = i / 100000 * 128 + i % 64 - 257; + bool null = i < 100000 || i % 10007 == 0; + counts[null ? 512 : k + 257]++; + if (types[t] == RAY_I16) ((int16_t*)ray_data(key))[i] = null ? NULL_I16 : k; + else if (types[t] == RAY_I64 || types[t] == RAY_TIMESTAMP) + ((int64_t*)ray_data(key))[i] = null ? NULL_I64 : k; + else ((int32_t*)ray_data(key))[i] = null ? NULL_I32 : k; + ((int32_t*)ray_data(val))[i] = null ? -123 : k == -100 ? NULL_I32 : k * 3; + } + ray_t* tbl = ray_table_new(2); + tbl = ray_table_add_col(tbl, ray_sym_intern("k", 1), key); + tbl = ray_table_add_col(tbl, ray_sym_intern("v", 1), val); + ray_release(key); ray_release(val); + for (int repeat = 0; repeat < 3; repeat++) { + ray_graph_t* graph = ray_graph_new(tbl); + ray_op_t* keys[] = {ray_scan(graph, "k")}; + ray_op_t* values[] = {ray_scan(graph, "v"), ray_scan(graph, "v"), ray_scan(graph, "v")}; + uint16_t ops[] = {OP_MIN, OP_MAX, OP_COUNT}; + ray_op_t* group = ray_group(graph, keys, 1, ops, values, 3); + agg_route_reset(); + int64_t watermark = ray_heap_anon_watermark(); + if (repeat == 2) { + /* Allow four task slabs and the final state, but not sixteen. + * A small spill budget must constrain extra scheduling tasks. */ + size_t block = 2 * agg_resolve(OP_MIN, RAY_TIME)->state_size + + agg_resolve(OP_COUNT, RAY_TIME)->state_size; + int64_t slab = 321 * (block + sizeof(int64_t) + 1); + ray_heap_set_anon_watermark(4 * (n * (int64_t)sizeof(uint32_t) + 5 * slab)); + } + ray_t* out = ray_execute(graph, group); + ray_heap_set_anon_watermark(watermark); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + agg_route_stats_t stats = agg_route_stats(); + TEST_ASSERT_EQ_I(stats.routes[AGG_ROUTE_V2_DENSE], 1); + TEST_ASSERT_TRUE(stats.dense_tasks >= 4 && stats.dense_tasks <= 16); + if (repeat == 2) TEST_ASSERT_EQ_I(stats.dense_tasks, 4); + TEST_ASSERT_EQ_I(stats.dense_strategy, AGG_DENSE_TASK_LOCAL); + TEST_ASSERT_EQ_I(stats.dense_local_slots, stats.dense_tasks * 321); + TEST_ASSERT_EQ_I(ray_table_nrows(out), 193); + ray_t* ko = ray_table_get_col_idx(out, 0); + ray_t* mn = ray_table_get_col_idx(out, 1); + ray_t* mx = ray_table_get_col_idx(out, 2); + ray_t* count = ray_table_get_col_idx(out, 3); + TEST_ASSERT_EQ_I(ko->type, types[t]); + TEST_ASSERT_EQ_I(mn->type, RAY_TIME); TEST_ASSERT_EQ_I(mx->type, RAY_TIME); + for (int64_t r = 0; r < ko->len; r++) { + int64_t k = types[t] == RAY_I16 ? ((int16_t*)ray_data(ko))[r] + : types[t] == RAY_I64 || types[t] == RAY_TIMESTAMP ? ((int64_t*)ray_data(ko))[r] + : ((int32_t*)ray_data(ko))[r]; + int64_t null = types[t] == RAY_I16 ? NULL_I16 + : types[t] == RAY_I64 || types[t] == RAY_TIMESTAMP ? NULL_I64 : NULL_I32; + int32_t expected = k == null ? -123 : k == -100 ? NULL_I32 : k * 3; + TEST_ASSERT_EQ_I(((int32_t*)ray_data(mn))[r], expected); + TEST_ASSERT_EQ_I(((int32_t*)ray_data(mx))[r], expected); + TEST_ASSERT_EQ_I(((int64_t*)ray_data(count))[r], counts[k == null ? 512 : k + 257]); + } + ray_release(out); ray_graph_free(graph); + } + ray_release(tbl); + } + PASS(); +} + +static test_result_t test_rank_widths_nulls_and_slices(void) { + const int8_t types[] = { RAY_BOOL, RAY_F32 }; + for (int t = 0; t < 2; t++) { + ray_t* empty = ray_vec_new(types[t], 0); + int64_t zero = 0; + ray_t* empty_group = ray_median_per_group_buf(empty, NULL, &zero, &zero, 1); + TEST_ASSERT_NOT_NULL(empty_group); + TEST_ASSERT_EQ_I(empty_group->type, RAY_F64); + TEST_ASSERT_TRUE(ray_vec_is_null(empty_group, 0)); + ray_release(empty_group); ray_release(empty); + for (int large = 0; large < 2; large++) { + int64_t group_size = large ? 513 : 3; + int64_t n = 16 * group_size; + ray_t* src = ray_vec_new(types[t], n + 7); src->len = n + 7; + for (int64_t i = 0; i < src->len; i++) { + int64_t g = i < 7 ? 99 : (i - 7) / group_size; + if (t) ((float*)ray_data(src))[i] = g == 0 ? NULL_F32 : (float)g + 0.25f; + else ((uint8_t*)ray_data(src))[i] = g % 2; + } + if (t) src->attrs |= RAY_ATTR_HAS_NULLS; + ray_t* slice = ray_vec_slice(src, 7, n); + if (t) { + /* Views may inherit null metadata from their parent. */ + slice->attrs &= (uint8_t)~RAY_ATTR_HAS_NULLS; + int64_t winners[] = {0, n - 1}; + ray_t* gathered = ray_group_gather(slice, winners, 2); + TEST_ASSERT_FMT(gathered && !RAY_IS_ERR(gathered), "slice gather failed"); + TEST_ASSERT_TRUE(gathered->attrs & RAY_ATTR_HAS_NULLS); + TEST_ASSERT_TRUE(ray_vec_is_null(gathered, 0)); + TEST_ASSERT_EQ_F(((float*)ray_data(gathered))[1], 15.25, 1e-12); + ray_release(gathered); + } + int64_t offsets[16], counts[16]; + ray_t* indices = ray_vec_new(RAY_I64, n); indices->len = n; + for (int64_t g = 0; g < 16; g++) { + offsets[g] = g * group_size; counts[g] = group_size; + for (int64_t j = 0; j < group_size; j++) + ((int64_t*)ray_data(indices))[offsets[g]+j] = offsets[g]+group_size-1-j; + } + for (int q = 0; q < 2; q++) { + ray_t* out = q + ? ray_quantile_per_group_buf(slice, ray_data(indices), offsets, counts, 16, 0.25) + : ray_median_per_group_buf(slice, ray_data(indices), offsets, counts, 16); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + TEST_ASSERT_EQ_I(out->type, RAY_F64); + for (int64_t g = 0; g < 16; g++) { + if (t && g == 0) { + TEST_ASSERT_TRUE(ray_vec_is_null(out, g)); + TEST_ASSERT_TRUE(out->attrs & RAY_ATTR_HAS_NULLS); + } else TEST_ASSERT_EQ_F(((double*)ray_data(out))[g], t ? g+0.25 : g%2, 1e-12); + } + ray_release(out); + } + ray_release(indices); ray_release(slice); ray_release(src); + } + } + PASS(); +} + +static test_result_t test_pairwise_numeric_contracts(void) { + const int8_t types[] = { RAY_BOOL, RAY_U8, RAY_I16, RAY_I32, RAY_I64, RAY_F32, RAY_F64 }; + const char* names[] = { "pearson_corr", "cov", "scov", "wsum", "wavg" }; + for (size_t x = 0; x < sizeof(types)/sizeof(types[0]); x++) { + for (size_t y = 0; y < sizeof(types)/sizeof(types[0]); y++) { + ray_t* vx = contract_fixture(types[x]); + ray_t* vy = contract_fixture(types[y]); + ray_env_set(ray_sym_intern("x", 1), vx); + ray_env_set(ray_sym_intern("y", 1), vy); + ray_release(vx); ray_release(vy); + ray_t* init = ray_eval_str("(set t (table [k x y] (list [0 0 1 1] x y)))"); + TEST_ASSERT_FALSE(RAY_IS_ERR(init)); ray_release(init); + for (size_t a = 0; a < sizeof(names)/sizeof(names[0]); a++) { + char source[256]; + snprintf(source, sizeof(source), + "(at (select {from:t by:k s:(%s x y) asc:k}) 's)", names[a]); + ray_t* got = ray_eval_str(source); + TEST_ASSERT_FMT(got && !RAY_IS_ERR(got), "%s(%s,%s) grouped failed", + names[a], ray_type_name(types[x]), ray_type_name(types[y])); + TEST_ASSERT_EQ_I(got->type, RAY_F64); + TEST_ASSERT_EQ_I(got->len, 2); + for (int g = 0; g < 2; g++) { + snprintf(source, sizeof(source), "(%s (at x [%d %d]) (at y [%d %d]))", + names[a], 2*g, 2*g+1, 2*g, 2*g+1); + ray_t* want = ray_eval_str(source); + TEST_ASSERT_FMT(want && !RAY_IS_ERR(want), "%s scalar failed", source); + TEST_ASSERT_EQ_I(want->type, -RAY_F64); + double actual = ((double*)ray_data(got))[g]; + bool equal = (isnan(actual) && isnan(want->f64)) || fabs(actual - want->f64) <= 1e-12; + ray_release(want); + TEST_ASSERT_FMT(equal, "%s(%s,%s) group %d differs from scalar slice", + names[a], ray_type_name(types[x]), ray_type_name(types[y]), g); + } + ray_release(got); + } + } + } + PASS(); +} + +static test_result_t test_native_binary_output(void) { + const uint16_t ops[] = { OP_PEARSON_CORR, OP_COV, OP_SCOV, OP_WSUM, OP_WAVG }; + const double expected[][4] = { + {1.0, NULL_F64, NULL_F64, NULL_F64}, + {0.5, 0.0, NULL_F64, NULL_F64}, + {1.0, 0.0, NULL_F64, NULL_F64}, + {10.0, 0.0, 0.0, 0.0}, + {10.0/3.0, NULL_F64, NULL_F64, NULL_F64}, + }; + const double xs[] = {1, 2, 0, 0, NULL_F64, NULL_F64}; + const double ys[] = {2, 4, 3, 5, 7, NULL_F64}; + const uint32_t gids[] = {0, 0, 1, 1, 2, 2}; + ray_t* x = ray_vec_new(RAY_F64, 6); x->len = 6; + ray_t* y = ray_vec_new(RAY_F64, 6); y->len = 6; + memcpy(ray_data(x), xs, sizeof(xs)); memcpy(ray_data(y), ys, sizeof(ys)); + x->attrs |= RAY_ATTR_HAS_NULLS; y->attrs |= RAY_ATTR_HAS_NULLS; + for (size_t a = 0; a < sizeof(ops)/sizeof(ops[0]); a++) { + ray_t* out = agg_run_one_bin(agg_resolve(ops[a], RAY_F64), x, y, gids, 6, 4, 0); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + TEST_ASSERT_EQ_I(out->type, RAY_F64); TEST_ASSERT_EQ_I(out->len, 4); + for (int g = 0; g < 4; g++) { + if (isnan(expected[a][g])) { + TEST_ASSERT_TRUE(ray_vec_is_null(out, g)); + TEST_ASSERT_TRUE(out->attrs & RAY_ATTR_HAS_NULLS); + } else TEST_ASSERT_EQ_F(((double*)ray_data(out))[g], expected[a][g], 1e-12); + } + ray_release(out); + } + ray_release(x); ray_release(y); + PASS(); +} + +static test_result_t test_nullable_differential(void) { + const char* types[] = {"I16", "I32", "I64", "F32", "F64", "DATE", "TIME", "TIMESTAMP", "SYM", "STR"}; + const char* operations[] = {"sum v", "min v", "max v", "avg v", "var v", "var_pop v", "stddev v", "stddev_pop v", "prod v", "first v", "last v", "med v", "mode v", "quantile v 0.25", "top v 2", "bot v 2"}; + for (size_t t = 0; t < sizeof(types)/sizeof(types[0]); t++) { + char source[512]; + if (t < 8) { + const int8_t ts[] = { RAY_I16, RAY_I32, RAY_I64, RAY_F32, RAY_F64, RAY_DATE, RAY_TIME, RAY_TIMESTAMP }; + ray_t* v = ray_vec_new(ts[t], 6); v->len = 6; + const int vals[] = {0, 7, 0, 0, 9, 3}; + for (int i = 0; i < 6; i++) { + if (ts[t] == RAY_F32) ((float*)ray_data(v))[i] = vals[i]; + else if (ts[t] == RAY_F64) ((double*)ray_data(v))[i] = vals[i]; + else if (ts[t] == RAY_I16) ((int16_t*)ray_data(v))[i] = vals[i]; + else if (ts[t] == RAY_I64 || ts[t] == RAY_TIMESTAMP) ((int64_t*)ray_data(v))[i] = vals[i]; + else ((int32_t*)ray_data(v))[i] = vals[i]; + } + ray_vec_set_null(v, 0, true); ray_vec_set_null(v, 2, true); ray_vec_set_null(v, 3, true); + ray_env_set(ray_sym_intern("v", 1), v); ray_release(v); + snprintf(source, sizeof(source), "(set t (table [k v] (list [0 0 1 1 2 2] v)))"); + } else snprintf(source, sizeof(source), "(set t (table [k v] (list [0 0 1 1 2 2] (as '%s [\"\" \"b\" \"\" \"\" \"z\" \"a\"]))))", types[t]); + ray_t* init = ray_eval_str(source); + TEST_ASSERT_FMT(init && !RAY_IS_ERR(init), "fixture %s", types[t]); ray_release(init); + for (size_t a = 0; a < sizeof(operations)/sizeof(operations[0]); a++) { + snprintf(source, sizeof(source), "(at (select {from:t by:k asc:k s:(%s)}) 's)", operations[a]); + ray_agg_engine_v2 = false; ray_t* old = ray_eval_str(source); + ray_agg_engine_v2 = true; ray_t* got = ray_eval_str(source); + bool oe = old && RAY_IS_ERR(old), ge = got && RAY_IS_ERR(got); + TEST_ASSERT_FMT(oe == ge, "%s/%s error parity", types[t], operations[a]); + if (oe) { ray_error_free(old); ray_error_free(got); continue; } + TEST_ASSERT_FMT(old && got && old->type == got->type && old->len == got->len, + "%s/%s output shape", types[t], operations[a]); + ray_t* os = ray_fmt(old, 0); ray_t* gs = ray_fmt(got, 0); + const char* of = ray_str_ptr(os); const char* gf = ray_str_ptr(gs); + bool same = of && gf && strcmp(of, gf) == 0; + TEST_ASSERT_FMT(same, "%s/%s: old %s, new %s", types[t], operations[a], of, gf); + ray_release(os); ray_release(gs); ray_release(old); ray_release(got); + } + } + PASS(); +} + +static test_result_t test_wide_key_routes(void) { + const int8_t types[] = {RAY_F32, RAY_F64, RAY_GUID, RAY_STR, RAY_LIST}; + for (size_t t = 0; t < sizeof(types)/sizeof(types[0]); t++) { + ray_t* k; + if (types[t] == RAY_STR) k = ray_eval_str("[\"long pooled duplicate value\" \"long pooled duplicate value\" \"a\" \"a\" \"\" \"\"]"); + else if (types[t] == RAY_LIST) k = ray_eval_str("(list ['a 'b] ['a 'b] [\"long pooled string value\" \"x\"] [\"long pooled string value\" \"x\"] [1 2] [1 2])"); + else { + k = ray_vec_new(types[t], 6); k->len = 6; + if (types[t] == RAY_GUID) { + memset(ray_data(k), 0, 6 * 16); + for (int i = 0; i < 4; i++) ((uint8_t*)ray_data(k))[i * 16] = (uint8_t)(i / 2 + 1); + } else for (int i = 0; i < 6; i++) { + double v = i < 2 ? (i ? -0.0 : 0.0) : i < 4 ? 1 : NAN; + if (types[t] == RAY_F32) ((float*)ray_data(k))[i] = (float)v; + else ((double*)ray_data(k))[i] = v; + } + ray_vec_set_null(k, 4, true); + } + TEST_ASSERT_NOT_NULL(k); TEST_ASSERT_FALSE(RAY_IS_ERR(k)); + if (types[t] == RAY_F32 || types[t] == RAY_F64) { + k->attrs &= (uint8_t)~RAY_ATTR_HAS_NULLS; + if (types[t] == RAY_F32) { + uint32_t bits[] = { UINT32_C(0x7fc00001), UINT32_C(0x7fc00002) }; + memcpy((float*)ray_data(k) + 4, bits, sizeof(bits)); + } else { + uint64_t bits[] = { UINT64_C(0x7ff8000000000001), UINT64_C(0x7ff8000000000002) }; + memcpy((double*)ray_data(k) + 4, bits, sizeof(bits)); + } + agg_groups_t groups = {0}; ray_t* one_key[] = {k}; + TEST_ASSERT_EQ_I(agg_group_keys(one_key, 1, 6, &groups), 0); + TEST_ASSERT_EQ_I(groups.ngroups, 4); /* unflagged payloads stay distinct */ + agg_groups_free(&groups); + k->attrs |= RAY_ATTR_HAS_NULLS; /* flagged NaNs merge as null below */ + } + ray_t* tbl = ray_table_new(1); + tbl = ray_table_add_col(tbl, ray_sym_intern("k", 1), k); ray_release(k); + ray_graph_t* graph = ray_graph_new(tbl); + ray_op_t* keys[] = {ray_scan(graph, "k")}; + uint16_t op = OP_COUNT; + ray_op_t* group = ray_group(graph, keys, 1, &op, keys, 1); + agg_route_reset(); + ray_t* out = ray_execute(graph, group); + TEST_ASSERT_FMT(out && !RAY_IS_ERR(out), "key type %s failed", ray_type_name(types[t])); + TEST_ASSERT_EQ_I(agg_route_stats().routes[AGG_ROUTE_V2_INDEXED], 1); + TEST_ASSERT_EQ_I(ray_table_nrows(out), 3); + TEST_ASSERT_EQ_I(ray_table_get_col_idx(out, 0)->type, types[t]); + ray_t* counts = ray_table_get_col_idx(out, 1); + for (int i = 0; i < 3; i++) TEST_ASSERT_EQ_I(((int64_t*)ray_data(counts))[i], 2); + ray_release(out); ray_graph_free(graph); ray_release(tbl); + } + agg_route_reset(); + ray_t* large = ray_eval_str("(select {from:(table [k] (list (take [\"a\" \"b\"] 65536))) by:k n:(count k)})"); + TEST_ASSERT_NOT_NULL(large); TEST_ASSERT_FALSE(RAY_IS_ERR(large)); + TEST_ASSERT_EQ_I(agg_route_stats().last_v2_reason, AGG_V2_ADMITTED); + TEST_ASSERT_EQ_I(agg_route_stats().routes[AGG_ROUTE_V2_INDEXED], 1); + TEST_ASSERT_EQ_I(ray_table_nrows(large), 2); + ray_release(large); + agg_route_reset(); + large = ray_eval_str("(select {from:(table [k j] (list (take [\"a\" \"b\"] 65536) (map (fn [x] (list (% x 2))) (til 65536)))) by:[k j] n:(count k)})"); + TEST_ASSERT_NOT_NULL(large); TEST_ASSERT_FALSE(RAY_IS_ERR(large)); + TEST_ASSERT_EQ_I(agg_route_stats().routes[AGG_ROUTE_V2_INDEXED], 1); + TEST_ASSERT_EQ_I(ray_table_nrows(large), 2); + ray_release(large); + agg_route_reset(); + ray_t* out = ray_eval_str("(select {from:(table [k a b] (list [0 0 1 1] (as 'TIME [1 2 3 4]) (as 'TIME [5 7 10 12]))) by:k s:(sum (- b a))})"); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + TEST_ASSERT_EQ_I(agg_route_stats().routes[AGG_ROUTE_V2_SERIAL_DENSE], 1); + TEST_ASSERT_EQ_I(ray_table_get_col_idx(out, 1)->type, RAY_TIME); + ray_release(out); + PASS(); +} + +static test_result_t test_fused_typed_comparisons(void) { + const int8_t types[] = {RAY_I64, RAY_F32, RAY_F64, RAY_TIME, RAY_GUID, RAY_STR}; + ray_op_t* (*ctors[])(ray_graph_t*, ray_op_t*, ray_op_t*) = {ray_eq, ray_ne, ray_lt, ray_le, ray_gt, ray_ge}; + ray_t* (*fns[])(ray_t*, ray_t*) = {ray_eq_fn, ray_neq_fn, ray_lt_fn, ray_lte_fn, ray_gt_fn, ray_gte_fn}; + for (size_t t = 0; t < sizeof(types)/sizeof(types[0]); t++) { + ray_t* col = contract_fixture(types[t]); + ray_vec_set_null(col, 0, true); + ray_t* tbl = ray_table_new(1); + tbl = ray_table_add_col(tbl, ray_sym_intern("v", 1), col); + for (int c = 0; c < 2; c++) { + int allocated = 0; + ray_t* constant = collection_elem(col, c, &allocated); + for (int op = 0; op < 6; op++) { + ray_graph_t* graph = ray_graph_new(tbl); + ray_op_t* lhs = ray_scan(graph, "v"); + uint32_t id = lhs->id; + ray_op_t* rhs = ray_const_atom(graph, constant); + ray_op_t* predicate = ctors[op](graph, &graph->nodes[id], rhs); + fp_pred_t compiled = {0}; + TEST_ASSERT_FMT(fp_compile_pred(graph, predicate, tbl, &compiled) == 0, + "fused %s comparison %d declined", ray_type_name(types[t]), op); + uint8_t bits[4]; fp_eval_pred(&compiled, 0, 4, bits); + for (int r = 0; r < 4; r++) { + int al = 0; ray_t* cell = collection_elem(col, r, &al); + ray_t* expected = fns[op](cell, constant); + TEST_ASSERT_FMT(expected && !RAY_IS_ERR(expected), "comparison oracle failed"); + TEST_ASSERT_FMT(bits[r] == expected->b8, "%s op %d row %d null constant %d", ray_type_name(types[t]), op, r, c == 0); + ray_release(expected); if (al) ray_release(cell); + } + fp_pred_cleanup(&compiled); ray_graph_free(graph); + } + if (allocated) ray_release(constant); + } + ray_release(col); ray_release(tbl); + } + PASS(); +} + +static test_result_t test_count_distinct_typed_routes(void) { + ray_pool_destroy(); TEST_ASSERT_EQ_I(ray_pool_init_total(2), RAY_OK); + const int8_t types[] = { RAY_BOOL, RAY_U8, RAY_I32, RAY_TIME, RAY_TIMESTAMP, RAY_SYM, RAY_F32, RAY_F64 }; + const int64_t n = 262144; + for (size_t t = 0; t < sizeof(types)/sizeof(types[0]); t++) { + int8_t type = types[t]; + ray_t* k = type == RAY_SYM ? ray_sym_vec_new(RAY_SYM_W64, n) : ray_vec_new(type, n); + ray_t* v = ray_vec_new(RAY_F32, n); k->len = v->len = n; + int64_t sym = ray_sym_intern("typed_cdf", 9); + for (int64_t i = 0; i < n; i++) { + int64_t key = i % 2; + if (type == RAY_BOOL || type == RAY_U8) ((uint8_t*)ray_data(k))[i] = (uint8_t)key; + else if (type == RAY_I32 || type == RAY_TIME) ((int32_t*)ray_data(k))[i] = key ? 1 : NULL_I32; + else if (type == RAY_TIMESTAMP) ((int64_t*)ray_data(k))[i] = key ? 1 : NULL_I64; + else if (type == RAY_SYM) ((int64_t*)ray_data(k))[i] = key ? sym : 0; + else if (type == RAY_F32) ((float*)ray_data(k))[i] = key ? 1 : NAN; + else ((double*)ray_data(k))[i] = key ? 1 : NAN; + int value = (int)((i / 2) % 3); + ((float*)ray_data(v))[i] = value ? (float)value : NAN; + } + if (type != RAY_BOOL && type != RAY_U8) k->attrs |= RAY_ATTR_HAS_NULLS; + v->attrs |= RAY_ATTR_HAS_NULLS; + ray_t* result = ray_cd_fused(k, v, n); + TEST_ASSERT_FMT(result && !RAY_IS_ERR(result), "count-distinct route %s declined", ray_type_name(type)); + TEST_ASSERT_EQ_I(ray_table_nrows(result), 2); + ray_t* counts = ray_table_get_col_idx(result, 1); + TEST_ASSERT_EQ_I(((int64_t*)ray_data(counts))[0], 3); + TEST_ASSERT_EQ_I(((int64_t*)ray_data(counts))[1], 3); + ray_release(result); ray_release(k); ray_release(v); + } + /* Large first-seen ordering and repeated-pair skew. The bitmap oracle + * counts values independently and retains the earliest original row. */ + const int64_t ng = 16384; + for (int skew = 0; skew < 2; skew++) { + ray_t* k = ray_vec_new(RAY_I64, n); + ray_t* v = ray_vec_new(RAY_I64, n); + TEST_ASSERT_NOT_NULL(k); TEST_ASSERT_NOT_NULL(v); + k->len = v->len = n; + uint8_t seen[16385] = {0}; + int64_t first[16385]; + for (int64_t g = 0; g <= ng; g++) first[g] = -1; + for (int64_t i = 0; i < n; i++) { + bool hot = skew && i % 3 == 0; + int64_t group = hot ? ng : (i * 17) % ng; + int value = hot ? (int)((i / 3) % 4) : (int)((i / ng) % 3); + ((int64_t*)ray_data(k))[i] = hot ? -1 : group; + ((int64_t*)ray_data(v))[i] = value; + seen[group] |= (uint8_t)(1u << value); + if (first[group] < 0) first[group] = i; + } + ray_t* result = ray_cd_fused(k, v, n); + TEST_ASSERT_FMT(result && !RAY_IS_ERR(result), "large count-distinct declined"); + TEST_ASSERT_EQ_I(ray_table_nrows(result), ng + skew); + int64_t* keys = ray_data(ray_table_get_col_idx(result, 0)); + int64_t* counts = ray_data(ray_table_get_col_idx(result, 1)); + int64_t* firsts = ray_data(ray_table_get_col_idx(result, 2)); + int64_t previous = -1; + for (int64_t i = 0; i < ng + skew; i++) { + int64_t group = keys[i] == -1 ? ng : keys[i]; + TEST_ASSERT_TRUE(group >= 0 && group <= ng); + TEST_ASSERT_EQ_I(counts[i], __builtin_popcount(seen[group])); + TEST_ASSERT_EQ_I(firsts[i], first[group]); + TEST_ASSERT_TRUE(firsts[i] > previous); previous = firsts[i]; + } + ray_release(result); ray_release(k); ray_release(v); + } + PASS(); +} + +/* Independent membership/order/reduction oracle for the parallel shared + * directory and row slices. A dominant null key crosses many reduction tasks. */ +static test_result_t test_wide_count_distinct(void) { + ray_pool_destroy(); TEST_ASSERT_EQ_I(ray_pool_init_total(4), RAY_OK); + const char* vocabularies[] = { + "[\"pooled long alpha value\" \"beta\" \"pooled long alpha value\" \"\"]", + "(as 'GUID (list \"00000000-0000-0000-0000-000000000001\" \"00000000-0000-0000-0000-000000000002\" \"00000000-0000-0000-0000-000000000001\" \"\"))", + "(list [1 2] [3 4] [1 2] (list))", + }; + const int8_t types[] = {RAY_STR, RAY_GUID, RAY_LIST}; + ray_t* setup = ray_eval_str("(set i (til 262144))"); + TEST_ASSERT_TRUE(setup && !RAY_IS_ERR(setup)); ray_release(setup); + for (int kind = 0; kind < 3; kind++) { + char script[1024]; + snprintf(script, sizeof(script), + "(set t (table [i k v] (list i (as 'I32 (%% i 65536)) (at %s (as 'I64 (/ i 65536))))))", + vocabularies[kind]); + setup = ray_eval_str(script); + TEST_ASSERT_TRUE(setup && !RAY_IS_ERR(setup)); + TEST_ASSERT_EQ_I(ray_table_get_col_idx(setup, 2)->type, types[kind]); + ray_t* source = ray_table_get_col_idx(setup, 2); ray_retain(source); + ray_release(setup); + ray_t* grouped = ray_group_indices_fn(source); + TEST_ASSERT_TRUE(grouped && !RAY_IS_ERR(grouped)); + ray_t* indices = ray_dict_vals(grouped); + TEST_ASSERT_EQ_I(indices->len, 3); + for (int g = 0; g < 3; g++) { + ray_t* rows = ray_list_get(indices, g); + TEST_ASSERT_EQ_I(rows->type, RAY_I64); + TEST_ASSERT_EQ_I(rows->len, g ? 65536 : 131072); + for (int64_t i = 0; i < rows->len; i++) { + int64_t expected = g == 0 ? (i < 65536 ? i : i + 65536) + : g == 1 ? i + 65536 : i + 196608; + TEST_ASSERT_EQ_I(((int64_t*)ray_data(rows))[i], expected); + } + } + ray_release(grouped); ray_release(source); + for (int shape = 0; shape < 4; shape++) { + int selected = shape & 1; + snprintf(script, sizeof(script), "(select {from:t by:k s:(count (distinct v)) %s %s})", + selected ? "where:(< i 196608)" : "", shape >= 2 ? "total:(sum i)" : ""); + ray_t* out = ray_eval_str(script); + TEST_ASSERT_FMT(out && !RAY_IS_ERR(out), "wide count distinct failed, type %d", types[kind]); + TEST_ASSERT_EQ_I(ray_table_nrows(out), 65536); + ray_t* counts = ray_table_get_col(out, ray_sym_intern("s", 1)); + TEST_ASSERT_NOT_NULL(counts); TEST_ASSERT_EQ_I(counts->type, RAY_I64); + for (int64_t g = 0; g < counts->len; g++) + TEST_ASSERT_EQ_I(((int64_t*)ray_data(counts))[g], selected ? 2 : 3); + ray_release(out); + } + ray_t* out = ray_eval_str("(select {from:t by:v s:(count (distinct k)) total:(sum i)})"); + TEST_ASSERT_TRUE(out && !RAY_IS_ERR(out)); + TEST_ASSERT_EQ_I(ray_table_nrows(out), 3); + ray_t* counts = ray_table_get_col(out, ray_sym_intern("s", 1)); + TEST_ASSERT_NOT_NULL(counts); + for (int64_t g = 0; g < counts->len; g++) + TEST_ASSERT_EQ_I(((int64_t*)ray_data(counts))[g], 65536); + ray_release(out); + out = ray_eval_str("(select {from:t by:v where:(< i 0)})"); + TEST_ASSERT_TRUE(out && !RAY_IS_ERR(out)); + TEST_ASSERT_EQ_I(ray_table_nrows(out), 0); + TEST_ASSERT_EQ_I(ray_table_ncols(out), 3); + TEST_ASSERT_EQ_I(ray_table_get_col(out, ray_sym_intern("v", 1))->type, types[kind]); + ray_release(out); + out = ray_eval_str("(select {from:t by:v s:(count (distinct k)) total:(sum i) m:(med i) where:(< i 0)})"); + TEST_ASSERT_TRUE(out && !RAY_IS_ERR(out)); + TEST_ASSERT_EQ_I(ray_table_nrows(out), 0); + TEST_ASSERT_EQ_I(ray_table_ncols(out), 4); + TEST_ASSERT_EQ_I(ray_table_get_col(out, ray_sym_intern("v", 1))->type, types[kind]); + TEST_ASSERT_EQ_I(ray_table_get_col(out, ray_sym_intern("s", 1))->type, RAY_I64); + TEST_ASSERT_EQ_I(ray_table_get_col(out, ray_sym_intern("total", 5))->type, RAY_I64); + TEST_ASSERT_EQ_I(ray_table_get_col(out, ray_sym_intern("m", 1))->type, RAY_F64); + ray_release(out); + + } + PASS(); +} + +static test_result_t test_indexed_parallel_layout(void) { + const int64_t n = 131075; + const int64_t group_counts[] = {4096, 17, 7, 17}; + for (int shape = 0; shape < 4; shape++) { + ray_pool_destroy(); + if (shape < 3) TEST_ASSERT_EQ_I(ray_pool_init_total(shape ? 3 : 4), RAY_OK); + const int64_t ng = group_counts[shape]; + int64_t symbols[4096]; + for (int64_t k = 0; k < ng; k++) { + char name[32]; int len = snprintf(name, sizeof(name), "nested-key-%lld", (long long)k); + symbols[k] = ray_sym_intern(name, len); + } + const int8_t types[] = {RAY_I32, RAY_I64, RAY_F64, RAY_GUID, RAY_STR, RAY_LIST}; + for (size_t ti = 0; ti < sizeof(types) / sizeof(types[0]); ti++) { + int8_t type = types[ti]; + ray_t* key = type == RAY_LIST ? ray_list_new(n) : ray_vec_new(type, n); + ray_t* val = ray_vec_new(RAY_I64, n); + TEST_ASSERT_NOT_NULL(key); TEST_ASSERT_NOT_NULL(val); + key->len = val->len = n; + key->attrs |= RAY_ATTR_HAS_NULLS; val->attrs |= RAY_ATTR_HAS_NULLS; + int64_t count[4096] = {0}, valid[4096] = {0}, sum[4096] = {0}; + int64_t first[4096], last[4096], first_row[4096]; + uint32_t* hist = ray_calloc_raw((size_t)ng * 101 * sizeof(uint32_t)); + TEST_ASSERT_NOT_NULL(hist); + for (int64_t k = 0; k < ng; k++) first[k] = last[k] = first_row[k] = -1; + for (int64_t r = 0; r < n; r++) { + int64_t k = r % 3 == 0 ? 0 : r % ng; + int64_t v = r % 101; + bool null = r % 97 == 0 || k == 17; + if (type == RAY_I32) ((int32_t*)ray_data(key))[r] = k ? k : NULL_I32; + else if (type == RAY_I64) ((int64_t*)ray_data(key))[r] = k ? k * INT64_C(1000000007) : NULL_I64; + else if (type == RAY_F64) ((double*)ray_data(key))[r] = k ? (double)k : NAN; + else if (type == RAY_GUID) { memset((char*)ray_data(key) + r * 16, 0, 16); memcpy((char*)ray_data(key) + r * 16, &k, sizeof(k)); } + else if (type == RAY_LIST) { + ray_t* item = NULL; + if (k) { + item = ray_sym_vec_new(r % 2 ? RAY_SYM_W32 : RAY_SYM_W64, 1); + TEST_ASSERT_NOT_NULL(item); item->len = 1; + ray_write_sym(ray_data(item), 0, symbols[k], RAY_SYM, item->attrs); + } + ((ray_t**)ray_data(key))[r] = item; + } + else { + char text[32]; int len = k ? snprintf(text, sizeof(text), "key%lld", (long long)k) : 0; + key = ray_str_vec_set(key, r, text, len); + TEST_ASSERT_NOT_NULL(key); TEST_ASSERT_FALSE(RAY_IS_ERR(key)); + } + ((int64_t*)ray_data(val))[r] = null ? NULL_I64 : v; + if (first_row[k] < 0) first_row[k] = r; + count[k]++; + if (!null) { + if (first[k] < 0) first[k] = v; + last[k] = v; valid[k]++; sum[k] += v; hist[k * 101 + v]++; + } + } + ray_t* tbl = ray_table_new(2); + tbl = ray_table_add_col(tbl, ray_sym_intern("k", 1), key); + tbl = ray_table_add_col(tbl, ray_sym_intern("v", 1), val); + ray_release(key); ray_release(val); + for (int repeat = 0; repeat < 2; repeat++) { + ray_graph_t* graph = ray_graph_new(tbl); + ray_op_t* keys[] = {ray_scan(graph, "k")}; + ray_op_t* v = ray_scan(graph, "v"); + ray_op_t* inputs[] = {v, v, v, v, v}; + uint16_t ops[] = {OP_SUM, OP_FIRST, OP_LAST, OP_MEDIAN, OP_COUNT}; + ray_op_t* group = ray_group(graph, keys, 1, ops, inputs, 5); + agg_route_reset(); + ray_t* out = ray_execute(graph, group); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + TEST_ASSERT_EQ_I(agg_route_stats().routes[AGG_ROUTE_V2_INDEXED], 1); + TEST_ASSERT_EQ_I(ray_table_nrows(out), ng); + ray_t* ko = ray_table_get_col_idx(out, 0); + TEST_ASSERT_EQ_I(ko->type, type); + int64_t previous = -1; + for (int64_t r = 0; r < ng; r++) { + int64_t k; + if (type == RAY_I32) { k = ((int32_t*)ray_data(ko))[r]; if (k == NULL_I32) k = 0; } + else if (type == RAY_I64) { k = ((int64_t*)ray_data(ko))[r]; k = k == NULL_I64 ? 0 : k / INT64_C(1000000007); } + else if (type == RAY_F64) { double f = ((double*)ray_data(ko))[r]; k = isnan(f) ? 0 : (int64_t)f; } + else if (type == RAY_GUID) memcpy(&k, (char*)ray_data(ko) + r * 16, sizeof(k)); + else if (type == RAY_LIST) { + ray_t* item = ray_list_get(ko, r); + k = 0; + if (item) { + int64_t id = ray_read_sym(ray_data(item), 0, RAY_SYM, item->attrs); + while (k < ng && symbols[k] != id) k++; + } + } + else { + size_t len = 0; const char* text = ray_str_vec_get(ko, r, &len); + k = 0; for (size_t i = 3; i < len; i++) k = k * 10 + text[i] - '0'; + } + TEST_ASSERT_TRUE(k >= 0 && k < ng); + TEST_ASSERT_TRUE(first_row[k] > previous); previous = first_row[k]; + TEST_ASSERT_EQ_I(((int64_t*)ray_data(ray_table_get_col_idx(out, 1)))[r], sum[k]); + TEST_ASSERT_EQ_I(((int64_t*)ray_data(ray_table_get_col_idx(out, 2)))[r], first[k] < 0 ? NULL_I64 : first[k]); + TEST_ASSERT_EQ_I(((int64_t*)ray_data(ray_table_get_col_idx(out, 3)))[r], last[k] < 0 ? NULL_I64 : last[k]); + TEST_ASSERT_EQ_I(((int64_t*)ray_data(ray_table_get_col_idx(out, 5)))[r], count[k]); + double median = ((double*)ray_data(ray_table_get_col_idx(out, 4)))[r]; + if (!valid[k]) TEST_ASSERT_TRUE(isnan(median)); + else { + int64_t cumulative = 0, a = -1, b = -1; + for (int value = 0; value <= 100; value++) { + cumulative += hist[k * 101 + value]; + if (a < 0 && cumulative > (valid[k] - 1) / 2) a = value; + if (b < 0 && cumulative > valid[k] / 2) b = value; + } + TEST_ASSERT_TRUE(median == (a + b) / 2.0); + } + } + ray_release(out); ray_graph_free(graph); + } + ray_free_raw(hist); ray_release(tbl); + } + } + PASS(); +} + +static int wide_test_code(ray_t* vector, int64_t row, const int64_t* symbols) { + if (ray_vec_is_null(vector, row)) return 0; + if (vector->type == RAY_F32) return (int)((float*)ray_data(vector))[row]; + if (vector->type == RAY_GUID) return ((uint8_t*)ray_data(vector))[row * 16]; + if (vector->type == RAY_SYM) { + int64_t code = ray_read_sym(ray_data(vector), row, vector->type, vector->attrs); + for (int i = 1; i <= 3; i++) if (code == symbols[i]) return i; + return -1; + } + size_t len = 0; const char* text = ray_str_vec_get(vector, row, &len); + return len ? text[len - 1] - 'a' + 1 : 0; +} +static test_result_t test_parallel_wide_consumers(void) { + ray_pool_destroy(); TEST_ASSERT_EQ_I(ray_pool_init_total(4), RAY_OK); + const int64_t n = 131072, ng = 4096; + const int8_t types[] = {RAY_F32, RAY_GUID, RAY_SYM, RAY_STR}; + int64_t symbols[4] = {0, ray_sym_intern("wide-a", 6), ray_sym_intern("wide-b", 6), ray_sym_intern("wide-c", 6)}; + int64_t* rows = ray_alloc_raw((size_t)n * sizeof(int64_t)); + int64_t* offsets = ray_alloc_raw((size_t)(ng + 1) * sizeof(int64_t)); + int64_t* counts = ray_alloc_raw((size_t)ng * sizeof(int64_t)); + TEST_ASSERT_NOT_NULL(rows); TEST_ASSERT_NOT_NULL(offsets); TEST_ASSERT_NOT_NULL(counts); + for (int64_t i = 0; i < n; i++) rows[i] = i; + for (int64_t g = 0; g <= ng; g++) { offsets[g] = g * 32; if (g < ng) counts[g] = 32; } + for (size_t ti = 0; ti < sizeof(types) / sizeof(types[0]); ti++) { + int8_t type = types[ti]; + ray_t* src = type == RAY_SYM ? ray_sym_vec_new(RAY_SYM_W64, n) : ray_vec_new(type, n); + TEST_ASSERT_NOT_NULL(src); src->len = n; + src->attrs |= RAY_ATTR_HAS_NULLS; + for (int64_t i = 0; i < n; i++) { + int code = i / 32 == 17 || i % 5 == 0 ? 0 : (int)(i % 3) + 1; + if (type == RAY_F32) ((float*)ray_data(src))[i] = code ? (float)code : NAN; + else if (type == RAY_GUID) { memset((char*)ray_data(src) + i * 16, 0, 16); ((uint8_t*)ray_data(src))[i * 16] = code; } + else if (type == RAY_SYM) ray_write_sym(ray_data(src), i, symbols[code], type, src->attrs); + else { + char text[] = "long pooled wide value a"; text[sizeof(text) - 2] += code ? code - 1 : 0; + src = ray_str_vec_set(src, i, text, code ? sizeof(text) - 1 : 0); + TEST_ASSERT_NOT_NULL(src); TEST_ASSERT_FALSE(RAY_IS_ERR(src)); + } + } + ray_t* mode = ray_mode_per_group_buf(src, rows, offsets, counts, ng); + TEST_ASSERT_NOT_NULL(mode); TEST_ASSERT_FALSE(RAY_IS_ERR(mode)); + for (int64_t g = 0; g < ng; g++) { + int hist[4] = {0}, first[4] = {32, 32, 32, 32}; + for (int j = 0; j < 32; j++) { + int64_t row = g * 32 + j; + int code = g == 17 || row % 5 == 0 ? 0 : (int)(row % 3) + 1; + hist[code]++; if (first[code] == 32) first[code] = j; + } + int best = 0; + for (int c = 1; c <= 3; c++) if (hist[c] > hist[best] || (hist[c] == hist[best] && first[c] < first[best])) best = c; + TEST_ASSERT_EQ_I(wide_test_code(mode, g, symbols), best); + } + ray_release(mode); + for (int desc = 0; desc < 2; desc++) { + ray_t* top = ray_topk_per_group_buf(src, 3, desc, rows, offsets, counts, ng); + TEST_ASSERT_NOT_NULL(top); TEST_ASSERT_FALSE(RAY_IS_ERR(top)); + TEST_ASSERT_EQ_I(top->len, ng); + for (int64_t g = 0; g < ng; g++) { + ray_t* cell = ray_list_get(top, g); + TEST_ASSERT_NOT_NULL(cell); TEST_ASSERT_EQ_I(cell->type, type); + TEST_ASSERT_EQ_I(cell->len, g == 17 ? 0 : 3); + for (int64_t i = 0; i < cell->len; i++) TEST_ASSERT_EQ_I(wide_test_code(cell, i, symbols), desc ? 3 : 1); + } + ray_release(top); + if (type == RAY_GUID || type == RAY_STR) { + ray_t* extreme = ray_wide_minmax_per_group_buf(src, desc ? OP_MAX : OP_MIN, rows, offsets, counts, ng); + TEST_ASSERT_NOT_NULL(extreme); TEST_ASSERT_FALSE(RAY_IS_ERR(extreme)); + for (int64_t g = 0; g < ng; g++) TEST_ASSERT_EQ_I(wide_test_code(extreme, g, symbols), g == 17 ? 0 : desc ? 3 : 1); + ray_release(extreme); + } + } + ray_release(src); + } + ray_free_raw(rows); ray_free_raw(offsets); ray_free_raw(counts); + PASS(); +} + +/* Histogram ranks independently check both sides of the parallel grain. */ +static test_result_t test_parallel_rank_size(int64_t n) { + const int64_t counts[] = {n / 2, n / 2 - 17, 17}; + const int64_t offsets[] = {0, n / 2, n - 17}; + const int8_t types[] = {RAY_I64, RAY_F32, RAY_TIME, RAY_BOOL}; + const double probabilities[] = {0.0, 0.25, 0.5, 0.9, 1.0}; + int64_t* rows = ray_alloc_raw((size_t)n * sizeof(int64_t)); + TEST_ASSERT_NOT_NULL(rows); + for (int64_t i = 0; i < n; i++) rows[i] = i; + for (size_t t = 0; t < sizeof(types) / sizeof(types[0]); t++) { + for (int shape = 0; shape < 2; shape++) { + ray_t* values = ray_vec_new(types[t], n); + TEST_ASSERT_NOT_NULL(values); values->len = n; + bool nullable = types[t] != RAY_BOOL; + if (nullable) values->attrs |= RAY_ATTR_HAS_NULLS; + int64_t histogram[3][257] = {{0}}, valid[3] = {0}; + for (int64_t g = 0; g < 3; g++) { + for (int64_t j = 0; j < counts[g]; j++) { + int64_t row = offsets[g] + j; + int code = shape ? 256 - (int)(j * 257 / counts[g]) : (int)(row * 37 % 257); + if (types[t] == RAY_BOOL) code %= 2; + bool missing = nullable && (row % 97 == 0 || (shape && g == 1)); + if (!missing) { histogram[g][code]++; valid[g]++; } + if (types[t] == RAY_I64) ((int64_t*)ray_data(values))[row] = missing ? NULL_I64 : code; + else if (types[t] == RAY_TIME) ((int32_t*)ray_data(values))[row] = missing ? NULL_I32 : code; + else if (types[t] == RAY_F32) ((float*)ray_data(values))[row] = missing ? NULL_F32 : code + 0.25f; + else ((uint8_t*)ray_data(values))[row] = code; + } + } + for (int op = -1; op < 5; op++) { + ray_t* out = op < 0 ? ray_median_per_group_buf(values, rows, offsets, counts, 3) + : ray_quantile_per_group_buf(values, rows, offsets, counts, 3, probabilities[op]); + TEST_ASSERT_FMT(out && !RAY_IS_ERR(out), "dominant rank failed"); + TEST_ASSERT_EQ_I(out->type, RAY_F64); + for (int64_t g = 0; g < 3; g++) { + if (!valid[g]) { TEST_ASSERT_TRUE(ray_vec_is_null(out, g)); continue; } + double position = (op < 0 ? 0.5 : probabilities[op]) * (double)(valid[g] - 1); + int64_t lo = (int64_t)position, hi = position > lo ? lo + 1 : lo; + int64_t seen = 0; + double lower = 0, upper = 0; + for (int code = 0; code <= 256; code++) { + double value = code + (types[t] == RAY_F32 ? 0.25 : 0.0); + if (seen <= lo && lo < seen + histogram[g][code]) lower = value; + if (seen <= hi && hi < seen + histogram[g][code]) upper = value; + seen += histogram[g][code]; + } + double expected = lower + (position - lo) * (upper - lower); + TEST_ASSERT_EQ_F(((double*)ray_data(out))[g], expected, 1e-12); + } + ray_release(out); + } + ray_release(values); + } + } + ray_free_raw(rows); + PASS(); +} + +static test_result_t test_parallel_rank_dominant(void) { + ray_pool_destroy(); TEST_ASSERT_EQ_I(ray_pool_init_total(4), RAY_OK); + const int64_t sizes[] = {131072, 400000, 1048576}; + for (size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) { + test_result_t result = test_parallel_rank_size(sizes[i]); + if (result.status != TEST_PASS) return result; + } + PASS(); +} + +/* Independent long-double moments and pair sums validate reassociation in + * both partitioned streaming and shared indexed consumers. */ +static test_result_t test_parallel_float_oracle(void) { + ray_pool_destroy(); TEST_ASSERT_EQ_I(ray_pool_init_total(4), RAY_OK); + const int64_t n = 262144, ng = 4096; + long double sx[4096] = {0}, xx[4096] = {0}, xy[4096] = {0}, weights[4096] = {0}; + int64_t nx[4096] = {0}, np[4096] = {0}; + ray_t* keys = ray_vec_new(RAY_I32, n); + ray_t* x = ray_vec_new(RAY_F64, n); + ray_t* y = ray_vec_new(RAY_F64, n); + TEST_ASSERT_NOT_NULL(keys); TEST_ASSERT_NOT_NULL(x); TEST_ASSERT_NOT_NULL(y); + keys->len = x->len = y->len = n; + x->attrs |= RAY_ATTR_HAS_NULLS; y->attrs |= RAY_ATTR_HAS_NULLS; + for (int64_t i = 0; i < n; i++) { + int64_t g = i % 3 == 0 ? 0 : i % ng; + double a = (i % 101 - 50) * 0.25, b = (i % 31 + 1) * 0.125; + bool na = i % 97 == 0 || g == 17, nb = i % 103 == 0; + ((int32_t*)ray_data(keys))[i] = g; + ((double*)ray_data(x))[i] = na ? NAN : a; + ((double*)ray_data(y))[i] = nb ? NAN : b; + if (!na) { nx[g]++; sx[g] += a; xx[g] += (long double)a * a; } + if (!na && !nb) { np[g]++; xy[g] += (long double)a * b; weights[g] += a; } + } + ray_t* t = ray_table_new(3); + t = ray_table_add_col(t, ray_sym_intern("k", 1), keys); + t = ray_table_add_col(t, ray_sym_intern("x", 1), x); + t = ray_table_add_col(t, ray_sym_intern("y", 1), y); + ray_env_set(ray_sym_intern("t", 1), t); + ray_release(t); ray_release(keys); ray_release(x); ray_release(y); + for (int indexed = 0; indexed < 2; indexed++) { + ray_t* out = ray_eval_str(indexed + ? "(select {from:t by:k s:(sum x) a:(avg x) v:(var_pop x) w:(wsum x y) wa:(wavg x y) m:(med x)})" + : "(select {from:t by:k s:(sum x) a:(avg x) v:(var_pop x) w:(wsum x y) wa:(wavg x y)})"); + TEST_ASSERT_FMT(out && !RAY_IS_ERR(out), "floating oracle query failed"); + TEST_ASSERT_EQ_I(ray_table_nrows(out), ng); + int32_t* groups = ray_data(ray_table_get_col_idx(out, 0)); + for (int64_t r = 0; r < ng; r++) { + int64_t g = groups[r]; + TEST_ASSERT_TRUE(g >= 0 && g < ng); + long double mean = nx[g] ? sx[g] / nx[g] : 0; + long double expected[] = {sx[g], mean, nx[g] ? xx[g] / nx[g] - mean * mean : 0, + xy[g], weights[g] ? xy[g] / weights[g] : 0}; + for (int a = 0; a < 5; a++) { + ray_t* col = ray_table_get_col_idx(out, a + 1); + TEST_ASSERT_EQ_I(col->type, RAY_F64); + if ((a == 1 || a == 2) && !nx[g]) { TEST_ASSERT_TRUE(ray_vec_is_null(col, r)); continue; } + if (a == 4 && (!np[g] || !weights[g])) { TEST_ASSERT_TRUE(ray_vec_is_null(col, r)); continue; } + double actual = ((double*)ray_data(col))[r]; + TEST_ASSERT_FMT(fabsl((long double)actual - expected[a]) <= 1e-9L + fabsl(expected[a]) * 1e-10L, + "float oracle indexed=%d group=%lld aggregate=%d got=%.17g expected=%.17Lg", + indexed, (long long)g, a, actual, expected[a]); + } + } + ray_release(out); + } + PASS(); +} + +static test_result_t test_parallel_selected_composite(void) { + ray_pool_destroy(); TEST_ASSERT_EQ_I(ray_pool_init_total(4), RAY_OK); + ray_t* setup = ray_eval_str( + "(set composite_i (til 262144)) " + "(set composite_t (table [a b v] (list (as 'I32 (* (% composite_i 4096) 4)) " + "(as 'I16 (% (div composite_i 4096) 2)) (- (% composite_i 101) 50)))) " + "(set composite_t (update {from:composite_t a:(as 'I32 0N) where:(== (% v 7) 0)}))"); + TEST_ASSERT_NOT_NULL(setup); TEST_ASSERT_FALSE(RAY_IS_ERR(setup)); ray_release(setup); + int64_t counts[8194] = {0}, sums[8194] = {0}, minima[8194]; + bool seen[8194] = {0}; + for (int k = 0; k < 8194; k++) minima[k] = INT64_MAX; + int groups = 0; + for (int64_t r = 0; r < 262144; r++) { + int64_t v = r % 101 - 50; + if (v <= 0) continue; + int key = (r / 4096 % 2) * 4097 + (v % 7 == 0 ? 4096 : r % 4096); + if (!counts[key]) groups++; + counts[key]++; sums[key] += v; + if (v < minima[key]) minima[key] = v; + } + agg_route_reset(); + ray_t* out = ray_eval_str("(select {from:composite_t by:[a b] s:(sum v) lo:(min v) n:(count v) where:(> v 0)})"); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + TEST_ASSERT_EQ_I(agg_route_stats().dense_strategy, AGG_DENSE_PARTITIONED); + TEST_ASSERT_EQ_I(ray_table_nrows(out), groups); + ray_t* a = ray_table_get_col_idx(out, 0); ray_t* b = ray_table_get_col_idx(out, 1); + TEST_ASSERT_EQ_I(a->type, RAY_I32); TEST_ASSERT_EQ_I(b->type, RAY_I16); + for (int64_t r = 0; r < groups; r++) { + int32_t x = ((int32_t*)ray_data(a))[r]; int16_t y = ((int16_t*)ray_data(b))[r]; + int key = y * 4097 + (x == NULL_I32 ? 4096 : x / 4); + TEST_ASSERT_TRUE(key >= 0 && key < 8194); TEST_ASSERT_FALSE(seen[key]); seen[key] = true; + TEST_ASSERT_EQ_I(((int64_t*)ray_data(ray_table_get_col_idx(out, 2)))[r], sums[key]); + TEST_ASSERT_EQ_I(((int64_t*)ray_data(ray_table_get_col_idx(out, 3)))[r], minima[key]); + TEST_ASSERT_EQ_I(((int64_t*)ray_data(ray_table_get_col_idx(out, 4)))[r], counts[key]); + } + ray_release(out); + out = ray_eval_str("(select {from:composite_t by:[a b] s:(sum v) where:(> v 100)})"); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); TEST_ASSERT_EQ_I(ray_table_nrows(out), 0); + ray_release(out); + PASS(); +} + +static test_result_t test_parallel_native_gather(void) { + const int8_t types[] = {RAY_BOOL, RAY_U8, RAY_I16, RAY_I32, RAY_I64, + RAY_F32, RAY_F64, RAY_DATE, RAY_TIME, RAY_TIMESTAMP, RAY_GUID, RAY_SYM}; + const uint8_t widths[] = {RAY_SYM_W8, RAY_SYM_W16, RAY_SYM_W32, RAY_SYM_W64}; + const int64_t n = 131072; + int64_t* rows = ray_alloc_raw((size_t)n * sizeof(int64_t)); + TEST_ASSERT_NOT_NULL(rows); + for (size_t t = 0; t < sizeof(types) / sizeof(types[0]); t++) { + int8_t type = types[t]; + bool nullable = type != RAY_BOOL && type != RAY_U8; + for (unsigned w = 0; w < (type == RAY_SYM ? 4u : 1u); w++) { + ray_t* source = type == RAY_SYM ? ray_sym_vec_new(widths[w], 4) : contract_fixture(type); + TEST_ASSERT_NOT_NULL(source); TEST_ASSERT_FALSE(RAY_IS_ERR(source)); + if (type == RAY_SYM) { + source->len = 4; + for (int64_t i = 0; i < 4; i++) ray_write_sym(ray_data(source), i, i + 1, type, source->attrs); + } + if (nullable) ray_vec_set_null(source, 2, true); + ray_t* view = ray_vec_slice(source, 1, 3); + TEST_ASSERT_NOT_NULL(view); TEST_ASSERT_FALSE(RAY_IS_ERR(view)); + /* Null metadata inherited from a sliced parent must survive. */ + view->attrs &= ~RAY_ATTR_HAS_NULLS; + for (int64_t i = 0; i < n; i++) rows[i] = nullable && i % 7 == 0 ? -1 : i * 17 % 3; + ray_t* out = ray_group_gather(view, rows, n); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + TEST_ASSERT_EQ_I(out->type, type); TEST_ASSERT_EQ_I(out->len, n); + size_t width = col_esz(view); + if (type == RAY_SYM) { + TEST_ASSERT_EQ_I(out->attrs & RAY_SYM_W_MASK, widths[w]); + TEST_ASSERT_TRUE(ray_sym_vec_domain(out) == ray_sym_vec_domain(source)); + } + for (int64_t i = 0; i < n; i++) { + bool missing = rows[i] < 0 || (nullable && rows[i] == 1); + TEST_ASSERT_FMT(ray_vec_is_null(out, i) == missing, + "gather %s width=%zu output=%lld source=%lld: null mismatch", + ray_type_name(type), width, (long long)i, (long long)rows[i]); + if (rows[i] >= 0) + TEST_ASSERT_TRUE(memcmp((char*)ray_data(out) + (size_t)i * width, + (char*)ray_data(source) + (size_t)(rows[i] + 1) * width, width) == 0); + } + ray_t* empty = ray_group_gather(view, NULL, 0); + TEST_ASSERT_NOT_NULL(empty); TEST_ASSERT_FALSE(RAY_IS_ERR(empty)); + TEST_ASSERT_EQ_I(empty->type, type); TEST_ASSERT_EQ_I(empty->len, 0); + ray_release(empty); ray_release(out); ray_release(view); ray_release(source); + } + } + ray_free_raw(rows); + PASS(); +} + +static bool contract_same_cell(ray_t* out, int64_t at, ray_t* src, int64_t row) { + if (row < 0) return src->type == RAY_BOOL || src->type == RAY_U8 + ? ((uint8_t*)ray_data(out))[at] == 0 : ray_vec_is_null(out, at); + if (src->type == RAY_STR) { + size_t na = 0, nb = 0; + const char* a = ray_str_vec_get(out, at, &na); + const char* b = ray_str_vec_get(src, row, &nb); + return na == nb && (!na || memcmp(a, b, na) == 0); + } + size_t width = col_esz(src); + return memcmp((char*)ray_data(out) + (size_t)at * width, + (char*)ray_data(src) + (size_t)row * width, width) == 0; +} +static test_result_t test_parallel_dominant_consumers(void) { + ray_pool_destroy(); TEST_ASSERT_EQ_I(ray_pool_init_total(3), RAY_OK); + const int64_t n = 524288, counts[] = {262144, 262144, 17, 0}; + const int64_t offsets[] = {0, 262144, n, n + 17}; + const int8_t types[] = {RAY_BOOL, RAY_U8, RAY_I16, RAY_I32, RAY_I64, + RAY_F32, RAY_F64, RAY_DATE, RAY_TIME, RAY_TIMESTAMP, RAY_GUID, RAY_SYM, RAY_STR}; + int64_t* rows = ray_alloc_raw((size_t)(n + 17) * sizeof(int64_t)); + TEST_ASSERT_NOT_NULL(rows); + /* Deliberately not source-row order: tie priority follows index order. */ + for (int64_t i = 0; i < n + 17; i++) rows[i] = i < n / 2 ? (i % 2 ? 1 : 3) : i < n ? 2 : 0; + for (size_t t = 0; t < sizeof(types) / sizeof(types[0]); t++) { + int8_t type = types[t]; + bool nullable = type != RAY_BOOL && type != RAY_U8; + ray_t* src = contract_fixture(type); + TEST_ASSERT_NOT_NULL(src); TEST_ASSERT_FALSE(RAY_IS_ERR(src)); + if (type == RAY_GUID) { + memset(ray_data(src), 0, 64); + for (int i = 0; i < 4; i++) ((uint8_t*)ray_data(src))[16 * i] = i + 1; + } + if (nullable) ray_vec_set_null(src, 2, true); + if (type == RAY_I64 || type == RAY_F64) { + const int64_t tiny_rows[] = {3, 2, 1, 0}, off = 0, count = 4; + for (int desc = 0; desc < 2; desc++) { + ray_t* tiny = ray_topk_per_group_buf(src, 10, desc, tiny_rows, &off, &count, 1); + TEST_ASSERT_NOT_NULL(tiny); TEST_ASSERT_FALSE(RAY_IS_ERR(tiny)); + ray_t* cell = ray_list_get(tiny, 0); + const int64_t expected_rows[] = {0, 1, 3}; + TEST_ASSERT_EQ_I(cell->len, 3); + for (int i = 0; i < 3; i++) + TEST_ASSERT_TRUE(contract_same_cell(cell, i, src, expected_rows[desc ? 2 - i : i])); + ray_release(tiny); + } + } + int64_t expected[] = {3, nullable && type != RAY_STR ? -1 : 2, 0, -1}; + ray_t* mode = ray_mode_per_group_buf(src, rows, offsets, counts, 4); + TEST_ASSERT_NOT_NULL(mode); TEST_ASSERT_FALSE(RAY_IS_ERR(mode)); + TEST_ASSERT_EQ_I(mode->type, type); TEST_ASSERT_EQ_I(mode->len, 4); + for (int g = 0; g < 4; g++) + TEST_ASSERT_FMT(contract_same_cell(mode, g, src, expected[g]), + "dominant mode %s group %d", ray_type_name(type), g); + ray_release(mode); + const int64_t ks[] = {3, 10000, 300000}; + for (int ki = 0; ki < 3; ki++) for (int desc = 0; desc < 2; desc++) { + ray_t* top = ray_topk_per_group_buf(src, ks[ki], desc, rows, offsets, counts, 4); + TEST_ASSERT_NOT_NULL(top); TEST_ASSERT_FALSE(RAY_IS_ERR(top)); + for (int g = 0; g < 4; g++) { + ray_t* cell = ray_list_get(top, g); + TEST_ASSERT_NOT_NULL(cell); TEST_ASSERT_EQ_I(cell->type, type); + int64_t length = g == 3 || (g == 1 && nullable) ? 0 : counts[g] < ks[ki] ? counts[g] : ks[ki]; + TEST_ASSERT_EQ_I(cell->len, length); + for (int64_t j = 0; j < length; j++) + TEST_ASSERT_FMT(contract_same_cell(cell, j, src, g == 0 ? ((desc == (j < counts[g] / 2)) ? 3 : 1) : g == 1 ? 2 : 0), + "dominant top/bot %s group %d k=%lld desc=%d row=%lld", ray_type_name(type), g, (long long)ks[ki], desc, (long long)j); + } + ray_release(top); + } + if (type == RAY_GUID || type == RAY_STR) { + const uint16_t ops[] = {OP_MIN, OP_MAX, OP_FIRST, OP_LAST}; + const int64_t first_group[] = {1, 3, 3, 1}; + for (int op = 0; op < 4; op++) { + ray_t* out = ray_wide_minmax_per_group_buf(src, ops[op], rows, offsets, counts, 4); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + for (int g = 0; g < 4; g++) + TEST_ASSERT_TRUE(contract_same_cell(out, g, src, g == 0 ? first_group[op] : g == 2 ? 0 : -1)); + ray_release(out); + } + } + ray_release(src); + } + /* Many values force local collisions and exact partition merging. All + * frequencies tie, so the first position must beat the smallest row id. */ + ray_t* values = ray_vec_new(RAY_I64, n); + TEST_ASSERT_NOT_NULL(values); values->len = n; + for (int64_t i = 0; i < n; i++) { + ((int64_t*)ray_data(values))[i] = i % 4096; + rows[i] = n - 1 - i; + } + const int64_t zero = 0; + ray_t* mode = ray_mode_per_group_buf(values, rows, &zero, &n, 1); + TEST_ASSERT_NOT_NULL(mode); TEST_ASSERT_FALSE(RAY_IS_ERR(mode)); + TEST_ASSERT_EQ_I(((int64_t*)ray_data(mode))[0], 4095); + ray_release(mode); + const int64_t uneven = n - 3, large_k = 100003; + int64_t histogram[4096] = {0}; + for (int64_t i = 0; i < uneven; i++) histogram[rows[i] % 4096]++; + for (int desc = 0; desc < 2; desc++) { + ray_t* result = ray_topk_per_group_buf(values, large_k, desc, rows, &zero, &uneven, 1); + TEST_ASSERT_NOT_NULL(result); TEST_ASSERT_FALSE(RAY_IS_ERR(result)); + ray_t* cell = ray_list_get(result, 0); + TEST_ASSERT_EQ_I(cell->len, large_k); + int64_t at = 0; + for (int c = 0; c < 4096 && at < large_k; c++) { + int value = desc ? 4095 - c : c; + for (int64_t j = 0; j < histogram[value] && at < large_k; j++) + TEST_ASSERT_EQ_I(((int64_t*)ray_data(cell))[at++], value); + } + TEST_ASSERT_EQ_I(at, large_k); + ray_release(result); + } + ray_release(values); ray_free_raw(rows); + PASS(); +} + +/* Exercise inference failures without relying on allocator fault timing. */ +static const char* empty_probe_error; +static ray_t* empty_probe(ray_t* input) { + (void)input; + return ray_error(empty_probe_error, "empty inference probe"); +} +static test_result_t test_empty_inference_errors(void) { + ray_t* fn = ray_fn_unary("empty_probe", RAY_FN_AGGR, empty_probe); + TEST_ASSERT_NOT_NULL(fn); + ray_env_set(ray_sym_intern("empty_probe", 11), fn); + ray_release(fn); + const char* codes[] = {"domain", "type", "oom", "cancel"}; + for (int i = 0; i < 4; i++) { + empty_probe_error = codes[i]; + ray_t* out = ray_eval_str("(select {from:(table [k v] (list [1 2] [3 4])) by:k where:(< k 0) a:(empty_probe v)})"); + TEST_ASSERT_NOT_NULL(out); + if (i < 2) { + TEST_ASSERT_FALSE(RAY_IS_ERR(out)); + TEST_ASSERT_EQ_I(ray_table_nrows(out), 0); + ray_t* col = ray_table_get_col(out, ray_sym_intern("a", 1)); + TEST_ASSERT_NOT_NULL(col); + TEST_ASSERT_EQ_I(col->type, RAY_LIST); + } else { + TEST_ASSERT_TRUE(RAY_IS_ERR(out)); + TEST_ASSERT_STR_EQ(ray_err_code(out), codes[i]); + } + ray_release(out); + } + PASS(); +} + +static test_result_t test_nth_bounds(void) { + double values[] = {3.0, 1.0, 2.0}; + TEST_ASSERT_TRUE(isnan(ray_nth_dbl_inplace(NULL, 0, 0))); + TEST_ASSERT_TRUE(isnan(ray_nth_dbl_inplace(values, 0, 0))); + TEST_ASSERT_TRUE(isnan(ray_nth_dbl_inplace(values, 3, -1))); + TEST_ASSERT_TRUE(isnan(ray_nth_dbl_inplace(values, 3, 3))); + TEST_ASSERT_TRUE(ray_nth_dbl_inplace(values, 3, 0) == 1.0); + TEST_ASSERT_TRUE(ray_nth_dbl_inplace(values, 3, 2) == 3.0); + PASS(); +} + +static test_result_t test_cancelled_group(void) { + ray_t* tbl = ray_eval_str("(table [k v] (list [0 0 1 1] (as 'TIME [1 2 3 4])))"); + TEST_ASSERT_NOT_NULL(tbl); TEST_ASSERT_FALSE(RAY_IS_ERR(tbl)); + ray_graph_t* graph = ray_graph_new(tbl); + ray_op_t* keys[] = {ray_scan(graph, "k")}; + ray_op_t* values[] = {ray_scan(graph, "v")}; + uint16_t kind = OP_MIN; + ray_op_t* group = ray_group(graph, keys, 1, &kind, values, 1); + ray_pool_t* pool = ray_pool_get(); + atomic_store_explicit(&pool->cancelled, 1, memory_order_relaxed); + ray_t* out = exec_group_v2(graph, group, tbl, 0); + atomic_store_explicit(&pool->cancelled, 0, memory_order_relaxed); + TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_TRUE(RAY_IS_ERR(out)); + TEST_ASSERT_STR_EQ(ray_err_code(out), "cancel"); + ray_error_free(out); ray_graph_free(graph); ray_release(tbl); + PASS(); +} + +const test_entry_t agg_contract_entries[] = { + { "agg_contract/empty_inference_errors", test_empty_inference_errors, contract_setup, contract_teardown }, + { "agg_contract/nth_bounds", test_nth_bounds, contract_setup, contract_teardown }, + { "agg_contract/native_binary_output", test_native_binary_output, contract_setup, contract_teardown }, + { "agg_contract/native_streaming_output", test_native_streaming_output, contract_setup, contract_teardown }, + { "agg_contract/unary_types_values", test_unary_contracts, contract_setup, contract_teardown }, + { "agg_contract/pairwise_numeric", test_pairwise_numeric_contracts, contract_setup, contract_teardown }, + { "agg_contract/registry_admission", test_registry_admission_contracts, contract_setup, contract_teardown }, + { "agg_contract/routes_bool_outputs", test_group_routes_and_bool_outputs, contract_setup, contract_teardown }, + { "agg_contract/derived_key_per_symbol_route", test_derived_key_per_symbol_route, contract_setup, contract_teardown }, + { "agg_contract/narrow_extrema_limits", test_narrow_extrema_limits, contract_setup, contract_teardown }, + { "agg_contract/dense_strategies", test_dense_strategies, contract_setup, contract_teardown }, + { "agg_contract/dense_symbol_output", test_dense_symbol_output, contract_setup, contract_teardown }, + { "agg_contract/dense_task_local", test_dense_task_local, contract_setup, contract_teardown }, + { "agg_contract/rank_widths_nulls_slices", test_rank_widths_nulls_and_slices, contract_setup, contract_teardown }, + { "agg_contract/nullable_differential", test_nullable_differential, contract_setup, contract_teardown }, + { "agg_contract/wide_key_routes", test_wide_key_routes, contract_setup, contract_teardown }, + { "agg_contract/fused_typed_comparisons", test_fused_typed_comparisons, contract_setup, contract_teardown }, + { "agg_contract/count_distinct_typed_routes", test_count_distinct_typed_routes, contract_setup, contract_teardown }, + { "agg_contract/indexed_parallel_layout", test_indexed_parallel_layout, contract_setup, contract_teardown }, + { "agg_contract/wide_count_distinct", test_wide_count_distinct, contract_setup, contract_teardown }, + { "agg_contract/parallel_wide_consumers", test_parallel_wide_consumers, contract_setup, contract_teardown }, + { "agg_contract/parallel_rank_dominant", test_parallel_rank_dominant, contract_setup, contract_teardown }, + { "agg_contract/parallel_float_oracle", test_parallel_float_oracle, contract_setup, contract_teardown }, + { "agg_contract/parallel_selected_composite", test_parallel_selected_composite, contract_setup, contract_teardown }, + { "agg_contract/parallel_native_gather", test_parallel_native_gather, contract_setup, contract_teardown }, + { "agg_contract/parallel_dominant_consumers", test_parallel_dominant_consumers, contract_setup, contract_teardown }, + { "agg_contract/cancelled_group", test_cancelled_group, contract_setup, contract_teardown }, + { NULL, NULL, NULL, NULL }, +}; diff --git a/test/test_agg_engine.c b/test/test_agg_engine.c index c66147cbd..631474673 100644 --- a/test/test_agg_engine.c +++ b/test/test_agg_engine.c @@ -119,7 +119,7 @@ static test_result_t test_gate_admits_two_keys(void) { } /* Case 3: single I64 key + OP_SUM over an I32 column (not registered) → defer. */ -static test_result_t test_gate_defers_sum_i32(void) { +static test_result_t test_gate_admits_sum_i32(void) { ray_heap_init(); (void)ray_sym_init(); @@ -136,7 +136,7 @@ static test_result_t test_gate_defers_sum_i32(void) { ray_op_t* grp = ray_group(g, keys, 1, ops, ins, 1); TEST_ASSERT_NOT_NULL(grp); - TEST_ASSERT_FALSE(agg_v2_can_handle(g, grp, tbl)); + TEST_ASSERT_TRUE(agg_v2_can_handle(g, grp, tbl)); ray_graph_free(g); ray_release(tbl); @@ -343,8 +343,9 @@ static test_result_t test_dense_plan_nullable_key(void) { dense_plan_t pl = {0}; bool ok = agg_dense_plan(keys, 1, vts, 1, 8, &pl); - TEST_ASSERT_FALSE(ok); - TEST_ASSERT_FALSE(pl.ok); + TEST_ASSERT_TRUE(ok); + TEST_ASSERT_TRUE(pl.ok); + TEST_ASSERT_EQ_I(pl.ranges[0], 5); ray_release(k); ray_sym_destroy(); @@ -2090,7 +2091,7 @@ const test_entry_t agg_engine_entries[] = { { "sel_2k_median", test_sel_2k_median, NULL, NULL }, { "gate_admits_i64_key_sum_i64", test_gate_admits_i64_key_sum_i64, NULL, NULL }, { "gate_admits_two_keys", test_gate_admits_two_keys, NULL, NULL }, - { "gate_defers_sum_i32", test_gate_defers_sum_i32, NULL, NULL }, + { "gate_admits_sum_i32", test_gate_admits_sum_i32, NULL, NULL }, { "gate_admits_count", test_gate_admits_count, NULL, NULL }, { "dense_plan_single_i64", test_dense_plan_single_i64, NULL, NULL }, { "dense_plan_two_keys", test_dense_plan_two_keys, NULL, NULL }, diff --git a/test/test_agg_registry.c b/test/test_agg_registry.c index d6c863911..da7885e40 100644 --- a/test/test_agg_registry.c +++ b/test/test_agg_registry.c @@ -154,14 +154,11 @@ static test_result_t test_nulls_match_reduction(void) { TEST_ASSERT_EQ_I(got->i64, want->i64); /* sentinels skipped identically */ ray_release(got); ray_release(want); } - /* count over a HAS_NULLS column = live rows only = 3. - * This is the redesign's COMMITTED behavior (live-rows-only). We assert the - * literal 3, NOT a comparison to the legacy count (which counts slots incl. - * nulls per design §2.10) — this is an intentional corrected-behavior pin. */ + /* COUNT counts all input rows, including nulls, on every route. */ const agg_vtable_t* vt = agg_resolve(OP_COUNT, RAY_I64); TEST_ASSERT_NOT_NULL(vt); ray_t* got = run_single_group(vt, col); - TEST_ASSERT_EQ_I(got->i64, 3); + TEST_ASSERT_EQ_I(got->i64, 5); ray_release(got); ray_release(col); ray_sym_destroy(); diff --git a/test/test_audit.c b/test/test_audit.c index 99a13eb1f..e6681f577 100644 --- a/test/test_audit.c +++ b/test/test_audit.c @@ -1528,7 +1528,56 @@ static test_result_t test_public_runtime_api_not_redeclared(void) { * Test array + suite registration * ----------------------------------------------------------------------- */ +/* ------------------------------------------------------------------ + * A connection's compression threshold must come from the connection. + * + * ray_ipc_link_threshold derives a policy from the PEER ADDRESS alone. + * That is the right default when a connection is established, and wrong + * everywhere after: .ipc.open's `compress` option is stored on the conn + * data, so re-deriving from the address on a send silently discards it. + * A loopback link asked to compress would quietly never compress. + * + * The send paths therefore take conn_threshold(sel), which prefers the + * stored value. ray_ipc_link_threshold belongs only where a connection + * is being set up (ipc_accept / ray_ipc_connect_opts) or where there is + * no conn data at all (conn_threshold's own fallback). Guard that: no conn_write_msg or conn_frame_msg call may + * pass it. + * + * This is a tripwire, not a proof: it matches source text with strstr, + * uses ';' as a statement terminator and a 3-line proximity window, so + * reformatting the calls it guards can silently defeat it. It catches + * the specific regression that already happened once; it does not + * enforce the invariant in general. + * ------------------------------------------------------------------ */ +static test_result_t test_audit_conn_threshold_not_rederived(void) { + FILE* f = fopen("src/core/ipc.c", "r"); + if (!f) FAILF("cannot open src/core/ipc.c"); + + /* Calls span lines, so track the most recent call opened. */ + const char* pending = NULL; + int pending_line = 0; + int bad_line = 0; + char line[1024]; + for (int line_no = 1; fgets(line, sizeof line, f); line_no++) { + if (strstr(line, "conn_write_msg(")) { pending = "conn_write_msg"; pending_line = line_no; } + else if (strstr(line, "conn_frame_msg(")) { pending = "conn_frame_msg"; pending_line = line_no; } + if (pending && strstr(line, "ray_ipc_link_threshold")) { + /* within a few lines of the call opening = an argument to it */ + if (line_no - pending_line <= 3) { bad_line = line_no; break; } + } + if (strchr(line, ';')) pending = NULL; + } + fclose(f); + + if (bad_line) + FAILF("src/core/ipc.c:%d passes ray_ipc_link_threshold to a frame/send " + "call; use conn_threshold(sel) so .ipc.open's `compress` survives", + bad_line); + PASS(); +} + const test_entry_t audit_entries[] = { + { "audit/conn_threshold_not_rederived", test_audit_conn_threshold_not_rederived, NULL, NULL }, { "audit/smoke", test_audit_smoke, NULL, NULL }, { "audit/sel_group_sum", test_sel_group_sum, NULL, NULL }, { "audit/sel_group_count", test_sel_group_count, NULL, NULL }, diff --git a/test/test_expr_null.c b/test/test_expr_null.c index 168d54356..89f7fa674 100644 --- a/test/test_expr_null.c +++ b/test/test_expr_null.c @@ -29,6 +29,8 @@ #include "ops/internal.h" #include "mem/heap.h" #include "table/sym.h" +#include "ops/idxop.h" +#include "ops/fused_pred.h" #include #include #include @@ -1389,6 +1391,237 @@ static test_result_t test_diff_f64_andor_chokes(void) { return res; } + +/* ---- Issue #533: null-bearing SYM columns fuse when every consumer is a + * comparison against a non-null constant or another SYM column ------------- + * + * A null SYM is id 0, below every real intern id, and the fallback treats a + * null SYM as "smaller than everything" in all six comparisons (null == null, + * null != x, null < x are true; null == x, x < null are false). Raw id + * compares in the fused lane give the same table, so the per-query null scan + * is unnecessary for those shapes. Consumers that read the lane as a value + * (ISNULL, CAST, arithmetic) still need the proof and must bail. */ + +/* SYM column at the given width: "a","b",NULL,"a","c",NULL,"b","a","c" */ +static ray_t* make_sym_col_with_nulls(uint8_t width) { + const char* names[] = {"a","b","","a","c","","b","a","c"}; + ray_t* v = ray_sym_vec_new(width, 9); + for (int i = 0; i < 9; i++) { + int64_t id = names[i][0] ? ray_sym_intern(names[i], 1) : 0; + uint8_t b8 = (uint8_t)id; uint16_t b16 = (uint16_t)id; + uint32_t b32 = (uint32_t)id; int64_t b64 = id; + const void* src = width == RAY_SYM_W8 ? (const void*)&b8 : + width == RAY_SYM_W16 ? (const void*)&b16 : + width == RAY_SYM_W32 ? (const void*)&b32 : (const void*)&b64; + v = ray_vec_append(v, src); + } + return v; +} + +/* second SYM column, W64: "a",NULL,NULL,"c","c","b",NULL,"a","b" */ +static ray_t* make_sym_col2_with_nulls(void) { + const char* names[] = {"a","","","c","c","b","","a","b"}; + ray_t* v = ray_sym_vec_new(RAY_SYM_W64, 9); + for (int i = 0; i < 9; i++) { + int64_t id = names[i][0] ? ray_sym_intern(names[i], 1) : 0; + v = ray_vec_append(v, &id); + } + return v; +} + +static ray_t* make_sym_null_table(void) { + ray_t* s = make_sym_col_with_nulls(RAY_SYM_W8); + ray_t* s2 = make_sym_col2_with_nulls(); + ray_t* tbl = ray_table_new(2); + tbl = ray_table_add_col(tbl, ray_sym_intern("s", 1), s); + tbl = ray_table_add_col(tbl, ray_sym_intern("s2", 2), s2); + ray_release(s); ray_release(s2); + return tbl; +} + +static ray_op_t* b_sym_eq_const(ray_graph_t* g) { return ray_eq(g, ray_scan(g, "s"), ray_const_str(g, "b", 1)); } +static ray_op_t* b_sym_ne_const(ray_graph_t* g) { return ray_ne(g, ray_scan(g, "s"), ray_const_str(g, "b", 1)); } +static ray_op_t* b_sym_lt_const(ray_graph_t* g) { return ray_lt(g, ray_scan(g, "s"), ray_const_str(g, "b", 1)); } +static ray_op_t* b_sym_le_const(ray_graph_t* g) { return ray_le(g, ray_scan(g, "s"), ray_const_str(g, "b", 1)); } +static ray_op_t* b_sym_gt_const(ray_graph_t* g) { return ray_gt(g, ray_scan(g, "s"), ray_const_str(g, "b", 1)); } +static ray_op_t* b_sym_ge_const(ray_graph_t* g) { return ray_ge(g, ray_scan(g, "s"), ray_const_str(g, "b", 1)); } +static ray_op_t* b_const_eq_sym(ray_graph_t* g) { return ray_eq(g, ray_const_str(g, "b", 1), ray_scan(g, "s")); } +static ray_op_t* b_sym_and_or(ray_graph_t* g) { + return ray_or(g, ray_and(g, ray_ne(g, ray_scan(g, "s"), ray_const_str(g, "a", 1)), + ray_lt(g, ray_scan(g, "s"), ray_const_str(g, "c", 1))), + ray_eq(g, ray_scan(g, "s"), ray_const_str(g, "c", 1))); +} +static ray_op_t* b_sym_eq_col(ray_graph_t* g) { return ray_eq(g, ray_scan(g, "s"), ray_scan(g, "s2")); } +static ray_op_t* b_sym_ne_col(ray_graph_t* g) { return ray_ne(g, ray_scan(g, "s"), ray_scan(g, "s2")); } +static ray_op_t* b_sym_isnull(ray_graph_t* g) { return ray_isnull(g, ray_scan(g, "s")); } +static ray_op_t* b_sym_eq_and_isnull(ray_graph_t* g) { + return ray_or(g, ray_eq(g, ray_scan(g, "s"), ray_const_str(g, "a", 1)), + ray_isnull(g, ray_scan(g, "s"))); +} + +static test_result_t test_diff_sym_nulls_cmp_const(void) { + ray_heap_init(); (void)ray_sym_init(); + ray_t* tbl = make_sym_null_table(); + static const expr_builder_t builders[] = { + b_sym_eq_const, b_sym_ne_const, b_sym_lt_const, b_sym_le_const, + b_sym_gt_const, b_sym_ge_const, b_const_eq_sym, b_sym_and_or, + }; + test_result_t r = { TEST_PASS, NULL }; + for (size_t i = 0; i < sizeof(builders) / sizeof(builders[0]) && r.status == TEST_PASS; i++) + r = diff_run(tbl, builders[i], true); + ray_release(tbl); ray_sym_destroy(); ray_heap_destroy(); + return r; +} + +static test_result_t test_diff_sym_nulls_cmp_col(void) { + ray_heap_init(); (void)ray_sym_init(); + ray_t* tbl = make_sym_null_table(); + test_result_t r = diff_run(tbl, b_sym_eq_col, true); + if (r.status == TEST_PASS) r = diff_run(tbl, b_sym_ne_col, true); + ray_release(tbl); ray_sym_destroy(); ray_heap_destroy(); + return r; +} + +/* A consumer that reads the SYM lane as a value keeps the null proof: with + * nulls present the compile must bail EXPR_BAIL_NULLS, even when the same + * column also feeds a null-safe comparison elsewhere in the tree. */ +static test_result_t test_sym_nulls_value_consumer_bails(void) { + ray_heap_init(); (void)ray_sym_init(); + ray_t* tbl = make_sym_null_table(); + test_result_t r = { TEST_PASS, NULL }; + static const expr_builder_t builders[] = { b_sym_isnull, b_sym_eq_and_isnull }; + for (size_t i = 0; i < 2 && r.status == TEST_PASS; i++) { + uint64_t nulls_before = ray_expr_bail_counts[EXPR_BAIL_NULLS]; + r = diff_run(tbl, builders[i], false); + if (r.status == TEST_PASS && ray_expr_bail_counts[EXPR_BAIL_NULLS] == nulls_before) + r = (test_result_t){ TEST_FAIL, "value consumer of null SYM did not bail NULLS" }; + } + ray_release(tbl); ray_sym_destroy(); ray_heap_destroy(); + return r; +} + +static test_result_t test_diff_sym_nulls_parted(void) { + ray_heap_init(); (void)ray_sym_init(); + ray_t* s0 = make_sym_col_with_nulls(RAY_SYM_W8); + ray_t* s1 = make_sym_col_with_nulls(RAY_SYM_W16); + ray_t* s2 = make_sym_col_with_nulls(RAY_SYM_W64); + ray_t* col = ray_alloc(3 * sizeof(ray_t*)); + col->type = (int8_t)(RAY_PARTED_BASE + RAY_SYM); + col->len = 3; + col->attrs = 0; + memset(col->aux, 0, sizeof(col->aux)); + ray_t** ptrs = (ray_t**)ray_data(col); + ptrs[0] = s0; ptrs[1] = s1; ptrs[2] = s2; + ray_t* tbl = ray_table_new(1); + tbl = ray_table_add_col(tbl, ray_sym_intern("s", 1), col); + ray_release(col); + test_result_t r = diff_run(tbl, b_sym_eq_const, true); + if (r.status == TEST_PASS) r = diff_run(tbl, b_sym_ne_const, true); + ray_release(tbl); ray_sym_destroy(); ray_heap_destroy(); + return r; +} + + +/* ---- Chunk-zone skip must keep null rows for NE / LT / LE --------------- + * + * The null-aware truth table ranks a null below every value: null != c, + * null < c and null <= c are TRUE. Zone extrema exclude nulls, so an + * all-fail decision taken from extrema alone drops those rows whenever the + * chunk also holds a null. Layout: chunk_log2 = 10 so each 1024-row morsel + * is exactly one chunk. Chunk 0 is all 5 with a null at row 7, chunk 1 is + * all 9 with a null at row 1030, chunk 2 is all 5, chunk 3 is 1..1024. */ +#define ZN_ROWS 4096 +static int64_t zn_vals[ZN_ROWS]; +static bool zn_null[ZN_ROWS]; + +static ray_t* make_zone_null_table(void) { + ray_t* v = ray_vec_new(RAY_I64, ZN_ROWS); + for (int64_t i = 0; i < ZN_ROWS; i++) { + int64_t x = i < 1024 ? 5 : i < 2048 ? 9 : i < 3072 ? 5 : (i - 3072 + 1); + zn_vals[i] = x; zn_null[i] = false; + v = ray_vec_append(v, &x); + } + ray_vec_set_null(v, 7, true); zn_null[7] = true; + ray_vec_set_null(v, 1030, true); zn_null[1030] = true; + ray_t* r = ray_index_attach_chunk_zone(&v, 10); + if (RAY_IS_ERR(r)) { ray_release(v); return r; } + ray_t* tbl = ray_table_new(1); + tbl = ray_table_add_col(tbl, ray_sym_intern("x", 1), v); + ray_release(v); + return tbl; +} + +static int zn_expect(int64_t i, uint16_t op) { + const int64_t c = 5; + if (zn_null[i]) return op == OP_NE || op == OP_LT || op == OP_LE; + int64_t v = zn_vals[i]; + switch (op) { + case OP_EQ: return v == c; case OP_NE: return v != c; + case OP_LT: return v < c; case OP_LE: return v <= c; + case OP_GT: return v > c; default: return v >= c; + } +} + +static uint16_t zn_op; +static ray_op_t* b_zone_cmp(ray_graph_t* g) { + ray_op_t* x = ray_scan(g, "x"); ray_op_t* c = ray_const_i64(g, 5); + switch (zn_op) { + case OP_EQ: return ray_eq(g, x, c); case OP_NE: return ray_ne(g, x, c); + case OP_LT: return ray_lt(g, x, c); case OP_LE: return ray_le(g, x, c); + case OP_GT: return ray_gt(g, x, c); default: return ray_ge(g, x, c); + } +} + +static test_result_t test_zone_skip_keeps_nulls_expr(void) { + ray_heap_init(); (void)ray_sym_init(); + ray_t* tbl = make_zone_null_table(); + TEST_ASSERT(tbl && !RAY_IS_ERR(tbl), "zone table"); + static const uint16_t ops[] = { OP_EQ, OP_NE, OP_LT, OP_LE, OP_GT, OP_GE }; + for (size_t k = 0; k < 6; k++) { + zn_op = ops[k]; + ray_graph_t* g = ray_graph_new(tbl); + ray_t* r = ray_execute(g, b_zone_cmp(g)); + TEST_ASSERT(r && !RAY_IS_ERR(r) && r->type == RAY_BOOL && r->len == ZN_ROWS, "bool result"); + const uint8_t* d = (const uint8_t*)ray_data(r); + for (int64_t i = 0; i < ZN_ROWS; i++) + TEST_ASSERT_FMT((int)d[i] == zn_expect(i, ops[k]), + "op %u row %lld: got %d want %d", + (unsigned)ops[k], (long long)i, (int)d[i], zn_expect(i, ops[k])); + ray_release(r); ray_graph_free(g); + } + ray_release(tbl); ray_sym_destroy(); ray_heap_destroy(); + PASS(); +} + +/* fused_pred.c has the same chunk-zone arms. It admits nullable numeric + * columns with null-as-minimum ordering and today routes them through the + * typed per-row compare ahead of the zone block, so the guarded arms are + * not reached with nulls yet. Drive fp_eval_pred one chunk at a time, + * exactly as the morsel loop does, and check every row, so the contract + * holds whichever path a future change sends nullable columns down. */ +static test_result_t test_zone_skip_keeps_nulls_fused_pred(void) { + ray_heap_init(); (void)ray_sym_init(); + ray_t* tbl = make_zone_null_table(); + TEST_ASSERT(tbl && !RAY_IS_ERR(tbl), "zone table"); + static const uint16_t ops[] = { OP_EQ, OP_NE, OP_LT, OP_LE, OP_GT, OP_GE }; + static uint8_t bits[ZN_ROWS]; + for (size_t k = 0; k < 6; k++) { + zn_op = ops[k]; + ray_graph_t* g = ray_graph_new(tbl); + fp_pred_t fp; + TEST_ASSERT(fp_compile_pred(g, b_zone_cmp(g), tbl, &fp) == 0, "fp compiles"); + for (int64_t ms = 0; ms < ZN_ROWS; ms += 1024) + fp_eval_pred(&fp, ms, ms + 1024, bits + ms); + for (int64_t i = 0; i < ZN_ROWS; i++) + TEST_ASSERT_FMT((int)bits[i] == zn_expect(i, ops[k]), + "fp op %u row %lld: got %d want %d", + (unsigned)ops[k], (long long)i, (int)bits[i], zn_expect(i, ops[k])); + fp_pred_cleanup(&fp); ray_graph_free(g); + } + ray_release(tbl); ray_sym_destroy(); ray_heap_destroy(); + PASS(); +} + const test_entry_t expr_null_entries[] = { { "expr_null/bail_counter", test_expr_bail_counter_nulls, NULL, NULL }, { "expr_null/nullfree_invariance", test_nullfree_stream_unchanged, NULL, NULL }, @@ -1443,5 +1676,13 @@ const test_entry_t expr_null_entries[] = { { "expr_null/diff_parted_nullable", test_diff_parted_nullable, NULL, NULL }, /* F64 AND/OR choke: nullable F64 operands must bail EXPR_BAIL_NULL_SHAPE */ { "expr_null/diff_f64_andor_chokes", test_diff_f64_andor_chokes, NULL, NULL }, + /* Issue #533: null-bearing SYM comparisons fuse without the null scan */ + { "expr_null/diff_sym_nulls_cmp_const", test_diff_sym_nulls_cmp_const, NULL, NULL }, + { "expr_null/diff_sym_nulls_cmp_col", test_diff_sym_nulls_cmp_col, NULL, NULL }, + { "expr_null/sym_nulls_value_consumer_bails", test_sym_nulls_value_consumer_bails, NULL, NULL }, + { "expr_null/diff_sym_nulls_parted", test_diff_sym_nulls_parted, NULL, NULL }, + /* chunk-zone all-fail arms must not drop null rows for NE/LT/LE */ + { "expr_null/zone_skip_keeps_nulls_expr", test_zone_skip_keeps_nulls_expr, NULL, NULL }, + { "expr_null/zone_skip_keeps_nulls_fused_pred", test_zone_skip_keeps_nulls_fused_pred, NULL, NULL }, { NULL, NULL, NULL, NULL }, }; diff --git a/test/test_fused_topk.c b/test/test_fused_topk.c index 11a29d7ba..557011df2 100644 --- a/test/test_fused_topk.c +++ b/test/test_fused_topk.c @@ -512,7 +512,7 @@ static test_result_t test_topk_gathers_f64_out_col_type(void) { PASS(); } -static test_result_t test_topk_gate_unsupported_sort_key_type(void) { +static test_result_t test_topk_float_sort_key_type(void) { /* F64 sort key → rejected at the sort-key gate. */ int64_t N = 50; ray_t* fc = ray_vec_new(RAY_F64, N); fc->len = N; @@ -534,7 +534,12 @@ static test_result_t test_topk_gate_unsupported_sort_key_type(void) { ray_t* res = ray_fused_topk_select(tbl, where_expr, sort_keys, sort_descs, 1, 5, out_syms, NULL, 1); - TEST_ASSERT_NULL(res); + TEST_ASSERT_NOT_NULL(res); + TEST_ASSERT_FALSE(RAY_IS_ERR(res)); + TEST_ASSERT_EQ_I(ray_table_nrows(res), 5); + ray_t* out = ray_table_get_col_idx(res, 0); + for (int64_t i = 0; i < 5; i++) TEST_ASSERT_EQ_I(((int64_t*)ray_data(out))[i], i); + ray_release(res); ray_release(where_expr); ray_release(tbl); PASS(); @@ -1603,7 +1608,7 @@ const test_entry_t fused_topk_entries[] = { { "fused_topk/gate_zero_sort_keys", test_topk_gate_zero_sort_keys, topk_setup, topk_teardown }, { "fused_topk/gate_k_ge_nrows", test_topk_gate_k_ge_nrows, topk_setup, topk_teardown }, { "fused_topk/gather_f64_out_col", test_topk_gathers_f64_out_col_type, topk_setup, topk_teardown }, - { "fused_topk/gate_unsupported_sort_key", test_topk_gate_unsupported_sort_key_type, topk_setup, topk_teardown }, + { "fused_topk/float_sort_key", test_topk_float_sort_key_type, topk_setup, topk_teardown }, { "fused_topk/gate_n_out_zero", test_topk_gate_n_out_zero, topk_setup, topk_teardown }, { "fused_topk/gate_too_many_sort_keys", test_topk_gate_too_many_sort_keys, topk_setup, topk_teardown }, { "fused_topk/gate_negative_k", test_topk_gate_negative_k, topk_setup, topk_teardown }, diff --git a/test/test_heap.c b/test/test_heap.c index afb6cb839..2054f7b15 100644 --- a/test/test_heap.c +++ b/test/test_heap.c @@ -41,6 +41,7 @@ #include "test.h" #include #include "mem/heap.h" +#include "core/pool.h" /* Restore the shipped policy after a test drives it. */ #define RAY_HEAP_DECAY_MS_TEST_DEFAULT 10000 @@ -317,9 +318,10 @@ static test_result_t test_scratch_arena_direct_backing(void) { + ray_block_data_bytes(a.backing[0]); TEST_ASSERT_TRUE(a.end <= cap_end); - /* Consequently the next push cannot be carved out of the leftover of - * a block that has none: it must take a backing of its own. */ - size_t follow = 4u << 20; + /* A reused direct block may be larger than the request. Exceed its + * actual remaining capacity so the next push needs a new backing, + * independently of allocations made by earlier tests. */ + size_t follow = (size_t)(cap_end - a.ptr) + (4u << 20); unsigned char* q = (unsigned char*)ray_scratch_arena_push(&a, follow); TEST_ASSERT_NOT_NULL(q); TEST_ASSERT_EQ_I(a.n_backing, 2); @@ -2463,10 +2465,105 @@ static test_result_t test_order_overflow_guards(void) { PASS(); } -/* Anon watermark: once our anonymous (RAM) footprint would cross the watermark, - * further large allocations spill to a disk-backed file instead of anonymous - * RAM (which the kernel could accept then OOM-kill). Drive it with a low - * watermark so the crossing is deterministic regardless of machine RAM. */ +/* Direct-cache reuse must follow the byte budget, not a fixed entry count or + * admission order. Check retained-byte accounting as well as payload reuse. */ +static test_result_t test_direct_cache_many_entries(void) { + enum { N = 24 }; + const size_t size = 40u * 1024 * 1024; + ray_t* blocks[N] = {0}; + ray_heap_direct_cache_drain(); + int64_t base = ray_heap_anon_committed(); + int64_t previous = ray_heap_anon_watermark(); + ray_heap_set_anon_watermark((int64_t)size * 16 * 32); + int64_t bytes = 0; + bool ok = true; + for (int i = 0; i < N; i++) { + blocks[i] = ray_alloc(size); + if (!blocks[i]) { ok = false; break; } + bytes += (int64_t)ray_direct_map_size(blocks[i]); + *(uint64_t*)ray_data(blocks[i]) = UINT64_C(0x123456789abcdef0); + } + for (int i = 0; i < N; i++) { if (blocks[i]) ray_free(blocks[i]); blocks[i] = NULL; } + bool retained = ray_heap_anon_committed() == base + bytes; + for (int i = 0; i < N && ok; i++) { + blocks[i] = ray_alloc(size); + if (!blocks[i] || *(uint64_t*)ray_data(blocks[i]) != UINT64_C(0x123456789abcdef0)) ok = false; + } + bool reused = ray_heap_anon_committed() == base + bytes; + for (int i = 0; i < N; i++) if (blocks[i]) ray_free(blocks[i]); + ray_heap_direct_cache_drain(); + bool drained = ray_heap_anon_committed() == base; + ray_heap_set_anon_watermark(previous); + TEST_ASSERT_TRUE(ok && retained && reused && drained); + PASS(); +} + +static test_result_t test_direct_cache_replaces_old_blocks(void) { + const size_t small = 40u * 1024 * 1024, large = 55u * 1024 * 1024; + ray_heap_direct_cache_drain(); + int64_t base = ray_heap_anon_committed(); + int64_t previous = ray_heap_anon_watermark(); + ray_heap_set_anon_watermark(INT64_C(96) * 1024 * 1024 * 16); + ray_t* a = ray_alloc(small); + ray_t* b = ray_alloc(small); + ray_t* c = ray_alloc(large); + bool ok = a && b && c; + int64_t expected = base; + if (ok) { + expected += (int64_t)ray_direct_map_size(b) + (int64_t)ray_direct_map_size(c); + *(uint64_t*)ray_data(b) = 42; + *(uint64_t*)ray_data(c) = 99; + } + if (a) ray_free(a); + if (b) ray_free(b); + if (c) ray_free(c); + bool replaced = ray_heap_anon_committed() == expected; + a = ray_alloc(small); b = ray_alloc(large); + bool reused = a && b && *(uint64_t*)ray_data(a) == 42 && *(uint64_t*)ray_data(b) == 99; + if (a) ray_free(a); + if (b) ray_free(b); + ray_heap_direct_cache_drain(); + bool drained = ray_heap_anon_committed() == base; + ray_heap_set_anon_watermark(previous); + TEST_ASSERT_TRUE(ok && replaced && reused && drained); + PASS(); +} + +static void direct_cache_churn(void* raw, uint32_t wid, int64_t start, int64_t end) { + (void)wid; + _Atomic(int)* errors = raw; + for (int64_t task = start; task < end; task++) { + for (int round = 0; round < 8; round++) { + const size_t sa = 40u * 1024 * 1024, sb = 55u * 1024 * 1024; + ray_t* a = ray_alloc(sa); + uint64_t tag = (uint64_t)(task * 16 + round + 1); + if (a) { ((uint64_t*)ray_data(a))[0] = tag; ((uint8_t*)ray_data(a))[sa - 1] = (uint8_t)tag; } + ray_t* b = ray_alloc(sb); + if (b) { ((uint64_t*)ray_data(b))[0] = tag; ((uint8_t*)ray_data(b))[sb - 1] = (uint8_t)tag; } + if (task == 0 && round == 3) ray_heap_direct_cache_drain(); + if (!a || !b || ((uint64_t*)ray_data(a))[0] != tag || ((uint8_t*)ray_data(a))[sa - 1] != (uint8_t)tag) + atomic_fetch_add_explicit(errors, 1, memory_order_relaxed); + if (a) ray_free(a); + if (b && (((uint64_t*)ray_data(b))[0] != tag || ((uint8_t*)ray_data(b))[sb - 1] != (uint8_t)tag)) + atomic_fetch_add_explicit(errors, 1, memory_order_relaxed); + if (b) ray_free(b); + } + } +} + +static test_result_t test_direct_cache_concurrent_replacement(void) { + ray_heap_direct_cache_drain(); + int64_t previous = ray_heap_anon_watermark(); + ray_heap_set_anon_watermark(INT64_C(96) * 1024 * 1024 * 16); + _Atomic(int) errors = 0; + ray_pool_dispatch_n(ray_pool_get(), direct_cache_churn, &errors, 8); + ray_heap_direct_cache_drain(); + ray_heap_set_anon_watermark(previous); + TEST_ASSERT_EQ_I(atomic_load_explicit(&errors, memory_order_relaxed), 0); + PASS(); +} + +/* Drive the anon-to-file crossing with a low watermark, independent of RAM. */ static test_result_t test_anon_watermark_spill(void) { size_t sz = 40 * 1024 * 1024 - 128; /* order 26 → direct path */ /* Start from an empty reuse cache: leftover cached blocks from earlier @@ -2886,6 +2983,9 @@ const test_entry_t heap_entries[] = { { "heap/free_no_heap", test_free_no_heap, heap_setup, heap_teardown }, { "heap/pool_of_oversized_walk", test_pool_of_oversized_walk, heap_setup, heap_teardown }, { "heap/order_overflow_guards", test_order_overflow_guards, heap_setup, heap_teardown }, + { "heap/direct_cache_many_entries", test_direct_cache_many_entries, heap_setup, heap_teardown }, + { "heap/direct_cache_replaces_old", test_direct_cache_replaces_old_blocks, heap_setup, heap_teardown }, + { "heap/direct_cache_concurrent", test_direct_cache_concurrent_replacement, heap_setup, heap_teardown }, { "heap/anon_watermark_spill", test_anon_watermark_spill, heap_setup, heap_teardown }, { "heap/slab_byte_budget", test_slab_byte_budget, heap_setup, heap_teardown }, { "heap/slab_gc_drains_wide", test_slab_gc_drains_wide, heap_setup, heap_teardown }, diff --git a/test/test_ipc.c b/test/test_ipc.c index 6312e6dca..7ddcd0798 100644 --- a/test/test_ipc.c +++ b/test/test_ipc.c @@ -25,11 +25,10 @@ * test_ipc.c — focused coverage for src/core/ipc.c. * * Existing coverage (test_store.c) already covers: - * - ray_ipc_server_init/destroy lifecycle * - sync/async round-trips (no-auth and with-auth) * - auth rejection + no-creds rejection * - restricted mode - * - handshake version mismatch via legacy server API + * - handshake version mismatch * - ray_ipc_compress / ray_ipc_decompress basics * * This file covers the gaps: @@ -54,6 +53,12 @@ #define _GNU_SOURCE #include "test.h" +#include "ipc_harness.h" +#include +#include +#include +#include +#include #include #include "core/ipc.h" #include "core/sock.h" @@ -115,18 +120,6 @@ static uint16_t get_listen_port(ray_sock_t fd) { return ntohs(addr.sin_port); } -typedef struct { - ray_ipc_server_t *srv; - ray_vm_t *vm; -} ipc_thread_ctx_t; - -static void server_thread_fn(void* arg) { - ipc_thread_ctx_t* ctx = (ipc_thread_ctx_t*)arg; - __VM = ctx->vm; - while (ctx->srv->running) - ray_ipc_poll(ctx->srv, 10); -} - /* Poll-based server thread */ typedef struct { ray_poll_t *poll; @@ -169,20 +162,69 @@ static void sleep_ms(long ms) { /* A listener confined to loopback binds and accepts a loopback connect; * an unparseable host is a loud io failure, never a silent INADDR_ANY. */ +/* ---- Shared poll-based server harness ---------------------------------- + * The one way to stand up an IPC server in tests. Mirrors test_mcast.c's + * start_server. Kept here for the wire-level tests below; the shared + * version lives in ipc_harness.h. */ +typedef struct { + ray_poll_t* poll; + ray_vm_t* vm; +} wire_srv_ctx_t; + +static wire_srv_ctx_t g_wire_ctx; + +static test_result_t wire_start_server(ray_poll_t** poll_out, uint16_t* port_out, + ray_vm_t** vm_out, ray_thread_t* tid_out) { + ray_poll_t* poll = ray_poll_create(); + TEST_ASSERT_NOT_NULL(poll); + int64_t listener_id = ray_ipc_listen(poll, 0); + TEST_ASSERT((listener_id) >= (0), "listener_id >= 0"); + ray_selector_t* sel = ray_poll_get(poll, listener_id); + TEST_ASSERT_NOT_NULL(sel); + uint16_t port = get_listen_port((ray_sock_t)sel->fd); + TEST_ASSERT((port) > (0), "port > 0"); + + ray_vm_t* vm = make_server_vm(); + TEST_ASSERT_NOT_NULL(vm); + g_wire_ctx.poll = poll; + g_wire_ctx.vm = vm; + + static poll_thread_ctx_t pctx; + pctx.poll = poll; pctx.vm = vm; pctx.running = 1; + ray_thread_create(tid_out, (void(*)(void*))poll_server_thread_fn, &pctx); + sleep_ms(20); + + *poll_out = poll; *port_out = port; *vm_out = vm; + PASS(); +} + +static void wire_stop_server(ray_poll_t* poll, uint16_t port, + ray_vm_t* vm, ray_thread_t tid) { + poll_stop(poll, port); + ray_thread_join(tid); + ray_poll_destroy(poll); + ray_sys_free(vm); +} + static test_result_t test_ipc_listen_bind_addr(void) { - ray_ipc_server_t srv; - TEST_ASSERT_EQ_I(ray_ipc_server_init_at(&srv, "127.0.0.1", 0), RAY_OK); - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT(port > 0, "port > 0"); - ray_ipc_server_destroy(&srv); + ray_poll_t* poll = ray_poll_create(); + TEST_ASSERT_NOT_NULL(poll); + + /* An explicit loopback bind gets a real ephemeral port. */ + int64_t id = ray_ipc_listen_at(poll, "127.0.0.1", 0); + TEST_ASSERT((id) >= (0), "loopback bind"); + ray_selector_t* sel = ray_poll_get(poll, id); + TEST_ASSERT_NOT_NULL(sel); + TEST_ASSERT_TRUE(ray_test_listen_port((ray_sock_t)sel->fd) > 0); /* NULL host keeps the historical all-interfaces bind. */ - TEST_ASSERT_EQ_I(ray_ipc_server_init_at(&srv, NULL, 0), RAY_OK); - ray_ipc_server_destroy(&srv); + TEST_ASSERT((ray_ipc_listen_at(poll, NULL, 0)) >= (0), "NULL host binds"); + + /* An unparseable host is a loud failure, never a silent INADDR_ANY. */ + TEST_ASSERT((ray_ipc_listen_at(poll, "not-an-address", 0)) < (0), "bad host refused"); + TEST_ASSERT((ray_ipc_listen_at(poll, "999.9.9.9", 0)) < (0), "bad octets refused"); - ray_ipc_server_t bad; - TEST_ASSERT_EQ_I(ray_ipc_server_init_at(&bad, "not-an-address", 0), RAY_ERR_IO); - TEST_ASSERT_EQ_I(ray_ipc_server_init_at(&bad, "999.9.9.9", 0), RAY_ERR_IO); + ray_poll_destroy(poll); PASS(); } @@ -192,19 +234,9 @@ static test_result_t test_ipc_listen_bind_addr(void) { * plus the verbose eval_payload wrapper (lines 341-402). */ static test_result_t test_ipc_send_verbose(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -235,10 +267,7 @@ static test_result_t test_ipc_send_verbose(void) { ray_release(resp); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } @@ -249,19 +278,9 @@ static test_result_t test_ipc_send_verbose(void) { * Covers lines 368-375: captured output non-empty path in eval_payload. */ static test_result_t test_ipc_send_verbose_captures_output(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -287,10 +306,7 @@ static test_result_t test_ipc_send_verbose_captures_output(void) { ray_release(resp); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } @@ -300,26 +316,16 @@ static test_result_t test_ipc_send_verbose_captures_output(void) { * has a branch for non-STR messages that calls ray_eval(msg) directly * (lines 315-317). To exercise it we need to send a serialized non-STR * object. We do this by building a serialized i64 directly and injecting - * it into the server using the legacy blocking API. + * it into the server using the blocking client API. * * The simplest approach: connect raw, do handshake, build header with * msgtype=SYNC, payload = serialized integer, send it. The server will * eval the integer (returns itself as a value) and send us a response. */ static test_result_t test_ipc_eval_non_string_msg(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; /* Connect raw socket, do manual handshake */ ray_sock_t s = ray_sock_connect("127.0.0.1", port, 2000); @@ -399,27 +405,14 @@ static test_result_t test_ipc_eval_non_string_msg(void) { if (result != RAY_NULL_OBJ) ray_release(result); ray_sock_close(s); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } static test_result_t test_ipc_send_list_select_msg(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -447,27 +440,14 @@ static test_result_t test_ipc_send_list_select_msg(void) { ray_release(result); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } static test_result_t test_ipc_send_compiled_lambda_msg(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -496,27 +476,14 @@ static test_result_t test_ipc_send_compiled_lambda_msg(void) { ray_release(result); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } static test_result_t test_ipc_send_list_dict_arg_literal(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -619,10 +586,7 @@ static test_result_t test_ipc_send_list_dict_arg_literal(void) { ray_release(result2); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } @@ -644,20 +608,9 @@ static test_result_t test_ipc_connect_fail_no_server(void) { * format (line 1082-1083). */ static test_result_t test_ipc_connect_auth_no_user(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - strcpy(srv.auth_secret, "mypass"); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START_OPTS(srv, "mypass", false); + uint16_t port = srv.port; /* Connect with NULL user but valid password */ int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, "mypass", 0); @@ -672,10 +625,7 @@ static test_result_t test_ipc_connect_auth_no_user(void) { ray_release(r); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } @@ -978,19 +928,9 @@ static test_result_t test_ipc_poll_handshake_version_mismatch(void) { * characters and highly repetitive so it actually compresses. */ static test_result_t test_ipc_send_large_compressible(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -1025,10 +965,7 @@ static test_result_t test_ipc_send_large_compressible(void) { ray_release(result); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } @@ -1052,19 +989,9 @@ static test_result_t test_ipc_journal_path(void) { PASS(); } - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -1079,10 +1006,7 @@ static test_result_t test_ipc_journal_path(void) { ray_release(result); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); ray_journal_close(); system(cmd); /* cleanup */ @@ -1322,19 +1246,9 @@ static test_result_t test_ipc_poll_bad_header(void) { * (lines 1173-1188). */ static test_result_t test_ipc_send_large_result(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -1356,10 +1270,7 @@ static test_result_t test_ipc_send_large_result(void) { if (result != RAY_NULL_OBJ) ray_release(result); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } @@ -1379,19 +1290,9 @@ static test_result_t test_ipc_send_large_result(void) { * also compresses, covering the other direction again). */ static test_result_t test_ipc_send_large_msg_client_compress(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -1425,10 +1326,7 @@ static test_result_t test_ipc_send_large_msg_client_compress(void) { if (result != RAY_NULL_OBJ) ray_release(result); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } @@ -1440,19 +1338,9 @@ static test_result_t test_ipc_send_large_msg_client_compress(void) { * path in ray_ipc_send_verbose (lines 1250-1265). */ static test_result_t test_ipc_send_verbose_large_result(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -1477,146 +1365,7 @@ static test_result_t test_ipc_send_verbose_large_result(void) { ray_release(resp); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); - PASS(); -} - -/* ---- test_ipc_server_destroy_active_conns ------------------------------- */ -/* - * Destroy the server while a client connection is still active (client did - * not call ray_ipc_close before ray_ipc_server_destroy). - * Covers lines 804-810: the n_conns > 0 cleanup loop in - * ray_ipc_server_destroy. - */ -static test_result_t test_ipc_server_destroy_active_conns(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); - - /* Connect two clients */ - int64_t h1 = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); - TEST_ASSERT((h1) >= (0), "h1 >= 0"); - int64_t h2 = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); - TEST_ASSERT((h2) >= (0), "h2 >= 0"); - - /* Do one round-trip to ensure the server has accepted the connections */ - ray_t* msg = ray_str("(+ 1 1)", 7); - ray_t* r = ray_ipc_send(h1, msg); - ray_release(msg); - if (r && !RAY_IS_ERR(r)) ray_release(r); - - /* Stop the server thread first */ - srv.running = false; - ray_thread_join(tid); - - /* Leave h2 open (don't call ray_ipc_close(h2)). - * srv->n_conns may still have the h2 conn registered. - * ray_ipc_server_destroy must clean it up gracefully. */ - ray_ipc_close(h1); - /* Don't close h2 — let destroy handle it */ - - /* This must not crash even when n_conns > 0 */ - ray_ipc_server_destroy(&srv); - - /* Clean up the client-side handle after server is destroyed */ - ray_ipc_close(h2); - ray_sys_free(srv_vm); - PASS(); -} - -/* ---- test_ipc_server_conn_swap ------------------------------------------ */ -/* - * Cover line 647 in conn_close: `srv->conns[idx] = srv->conns[srv->n_conns - 1]` - * This swap only executes when closing a non-last connection (idx + 1 < n_conns). - * - * Setup: two raw-socket clients do a successful handshake so the server has - * n_conns == 2 (conns[0]=c1, conns[1]=c2). c1 then sends a bad header - * (wrong prefix) which triggers conn_on_header → conn_close(&conns[0]). - * Since idx=0 and n_conns=2, the swap executes: conns[0] = conns[1]. - */ -static test_result_t test_ipc_server_conn_swap(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); - - /* Connect two raw sockets and do handshakes so n_conns == 2 */ - ray_sock_t s1 = ray_sock_connect("127.0.0.1", port, 2000); - TEST_ASSERT_TRUE(s1 != RAY_INVALID_SOCK); - ray_sock_t s2 = ray_sock_connect("127.0.0.1", port, 2000); - TEST_ASSERT_TRUE(s2 != RAY_INVALID_SOCK); - - uint8_t hs[2] = { RAY_SERDE_WIRE_VERSION, 0x00 }; - ray_sock_send(s1, hs, 2); - ray_sock_send(s2, hs, 2); - - /* Read handshake responses */ - uint8_t r1[2], r2[2]; - size_t got = 0; - while (got < 2) { - int64_t n = ray_sock_recv(s1, r1 + got, 2 - got); - if (n <= 0) break; - got += (size_t)n; - } - got = 0; - while (got < 2) { - int64_t n = ray_sock_recv(s2, r2 + got, 2 - got); - if (n <= 0) break; - got += (size_t)n; - } - - /* Give server time to process both accepts */ - sleep_ms(20); - - /* s1 sends a bad header (wrong prefix) → conn_close(&conns[0]) → swap */ - ray_ipc_header_t bad_hdr; - memset(&bad_hdr, 0, sizeof(bad_hdr)); - bad_hdr.prefix = 0xBADBAD00; - bad_hdr.version = RAY_SERDE_WIRE_VERSION; - bad_hdr.size = 8; - ray_sock_send(s1, &bad_hdr, sizeof(bad_hdr)); - - /* Give the server time to process the bad header and close s1 */ - sleep_ms(30); - ray_sock_close(s1); - - /* s2 should still work; do a proper round-trip on it */ - int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); - if (h >= 0) { - ray_t* msg = ray_str("(+ 1 1)", 7); - ray_t* r = ray_ipc_send(h, msg); - ray_release(msg); - if (r && !RAY_IS_ERR(r)) ray_release(r); - ray_ipc_close(h); - } - - ray_sock_close(s2); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } @@ -1641,22 +1390,9 @@ static test_result_t test_ipc_journal_restricted(void) { PASS(); /* journal not supported; skip */ } - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - /* Enable restricted mode on the server */ - srv.restricted = true; - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START_OPTS(srv, NULL, true); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -1675,10 +1411,7 @@ static test_result_t test_ipc_journal_restricted(void) { } ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); ray_journal_close(); system(cmd); @@ -1693,19 +1426,9 @@ static test_result_t test_ipc_journal_restricted(void) { * fixes the wire would carry a RAY_LAZY type the server can't deserialise. */ static test_result_t test_ipc_send_lazy_msg(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -1754,16 +1477,13 @@ static test_result_t test_ipc_send_lazy_msg(void) { ray_release(vec); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); PASS(); } /* ---- test_ipc_hooks_lifecycle ------------------------------------------- */ /* - * End-to-end exercise of the `.ipc.on.*` connection hooks on the legacy + * End-to-end exercise of the `.ipc.on.*` connection hooks on the * server path. Three hooks are installed via `set`; a single round-trip * (open → SYNC eval → close) drives them all. We track side effects * through plain user-bound globals that hooks mutate, then read those @@ -1802,19 +1522,9 @@ static test_result_t test_ipc_hooks_lifecycle(void) { TEST_ASSERT_FALSE(RAY_IS_ERR(r)); if (r != RAY_NULL_OBJ) ray_release(r); - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -1839,17 +1549,16 @@ static test_result_t test_ipc_hooks_lifecycle(void) { /* Stop the server before reading hook side effects — guarantees * the close hook has fired (otherwise we'd race the poll loop). */ - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); /* Read counters back through the global env. on.open + on.sync * fired exactly once; on.close fired twice — once on the client for * its own outbound handle (ray_ipc_close, #503) and once on the * server for the inbound one. `_hook_sync_handle` records - * `.ipc.handle` as seen INSIDE the sync hook — must equal the - * legacy server's conn-array index (0 for the only active conn). */ + * `.ipc.handle` as seen INSIDE the sync hook. A handle is the + * connection's selector id, so assert it is a real handle rather + * than a fixed number — the exact id depends on selector allocation + * (the listener takes one first). */ int64_t sym_open = ray_sym_intern("_hook_open", strlen("_hook_open")); int64_t sym_close = ray_sym_intern("_hook_close", strlen("_hook_close")); int64_t sym_h = ray_sym_intern("_hook_sync_handle", strlen("_hook_sync_handle")); @@ -1863,7 +1572,7 @@ static test_result_t test_ipc_hooks_lifecycle(void) { TEST_ASSERT_EQ_I(v_open->i64, 1); TEST_ASSERT_EQ_I(v_close->i64, 2); TEST_ASSERT_EQ_I(v_msg->i64, 1); - TEST_ASSERT_EQ_I(v_h->i64, 0); + TEST_ASSERT((v_h->i64) >= (0), "sync hook saw a real handle, not -1"); /* `.ipc.handle` outside any hook reads back -1. */ ray_t* handle_outside = ray_eval_str("(.ipc.handle)"); @@ -1947,19 +1656,9 @@ static test_result_t test_ipc_post_delivery(void) { TEST_ASSERT_FALSE(RAY_IS_ERR(r)); if (r != RAY_NULL_OBJ) ray_release(r); - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = make_server_vm(); - TEST_ASSERT_NOT_NULL(srv_vm); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -1987,10 +1686,7 @@ static test_result_t test_ipc_post_delivery(void) { if (resp && !RAY_IS_ERR(resp)) ray_release(resp); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); + ray_test_server_stop(&srv); /* Now that the server thread is gone, it is safe to assert. */ TEST_ASSERT_FALSE(pr_is_err); @@ -2141,6 +1837,799 @@ static test_result_t test_ipc_server_push(void) { /* ---- Registry ------------------------------------------------------------ */ +/* ---- Link locality -> compression policy (#541 layer 1) ---------------- + * Compression is a sender-side, per-frame decision (the receiver honours + * RAY_IPC_FLAG_COMPRESSED), so a link may skip it unilaterally. On + * loopback and UNIX-domain links there is no bandwidth to buy with the + * CPU, so those default to never compressing. */ + +static test_result_t test_ipc_addr_local_ipv4_loopback(void) { + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + TEST_ASSERT_EQ_I(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1); + TEST_ASSERT_TRUE(ray_sock_addr_is_local(&sa, sizeof(sa))); + PASS(); +} + +/* The whole 127/8 block is loopback, not just 127.0.0.1. */ +static test_result_t test_ipc_addr_local_ipv4_loopback_block(void) { + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + TEST_ASSERT_EQ_I(inet_pton(AF_INET, "127.5.6.7", &sa.sin_addr), 1); + TEST_ASSERT_TRUE(ray_sock_addr_is_local(&sa, sizeof(sa))); + PASS(); +} + +static test_result_t test_ipc_addr_local_ipv4_remote(void) { + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + /* A private-LAN peer is NOT local: same-host detection must not be + * confused with "cheap link" — that is layer 2's explicit option. */ + TEST_ASSERT_EQ_I(inet_pton(AF_INET, "10.0.0.5", &sa.sin_addr), 1); + TEST_ASSERT_FALSE(ray_sock_addr_is_local(&sa, sizeof(sa))); + TEST_ASSERT_EQ_I(inet_pton(AF_INET, "8.8.8.8", &sa.sin_addr), 1); + TEST_ASSERT_FALSE(ray_sock_addr_is_local(&sa, sizeof(sa))); + PASS(); +} + +static test_result_t test_ipc_addr_local_ipv6_loopback(void) { + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + TEST_ASSERT_EQ_I(inet_pton(AF_INET6, "::1", &sa.sin6_addr), 1); + TEST_ASSERT_TRUE(ray_sock_addr_is_local(&sa, sizeof(sa))); + PASS(); +} + +/* A v4-mapped loopback peer (::ffff:127.0.0.1) arrives on a dual-stack + * listener and is just as local. */ +static test_result_t test_ipc_addr_local_ipv6_mapped_loopback(void) { + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + TEST_ASSERT_EQ_I(inet_pton(AF_INET6, "::ffff:127.0.0.1", &sa.sin6_addr), 1); + TEST_ASSERT_TRUE(ray_sock_addr_is_local(&sa, sizeof(sa))); + PASS(); +} + +static test_result_t test_ipc_addr_local_ipv6_remote(void) { + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + TEST_ASSERT_EQ_I(inet_pton(AF_INET6, "2001:db8::1", &sa.sin6_addr), 1); + TEST_ASSERT_FALSE(ray_sock_addr_is_local(&sa, sizeof(sa))); + /* A v4-mapped non-loopback peer must stay non-local. */ + TEST_ASSERT_EQ_I(inet_pton(AF_INET6, "::ffff:10.0.0.5", &sa.sin6_addr), 1); + TEST_ASSERT_FALSE(ray_sock_addr_is_local(&sa, sizeof(sa))); + PASS(); +} + +static test_result_t test_ipc_addr_local_af_unix(void) { + struct sockaddr_un sa; + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_UNIX; + TEST_ASSERT_TRUE(ray_sock_addr_is_local(&sa, sizeof(sa))); + PASS(); +} + +static test_result_t test_ipc_addr_local_rejects_garbage(void) { + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + /* Too short to hold the address it claims: treat as non-local rather + * than reading past the end. */ + TEST_ASSERT_FALSE(ray_sock_addr_is_local(&sa, 2)); + TEST_ASSERT_FALSE(ray_sock_addr_is_local(NULL, sizeof(sa))); + PASS(); +} + +/* A real AF_UNIX pair resolves as local through getpeername. */ +static test_result_t test_ipc_peer_is_local_socketpair(void) { + int sv[2]; + TEST_ASSERT_EQ_I(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0); + TEST_ASSERT_TRUE(ray_sock_peer_is_local((ray_sock_t)sv[0])); + TEST_ASSERT_TRUE(ray_sock_peer_is_local((ray_sock_t)sv[1])); + close(sv[0]); + close(sv[1]); + PASS(); +} + +/* An unconnected socket has no peer: getpeername fails, and an unknown + * peer must fall back to the compressing default, not to "local". */ +static test_result_t test_ipc_peer_is_local_unconnected(void) { + int fd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(fd >= 0); + TEST_ASSERT_FALSE(ray_sock_peer_is_local((ray_sock_t)fd)); + close(fd); + PASS(); +} + +static test_result_t test_ipc_peer_is_local_invalid_fd(void) { + TEST_ASSERT_FALSE(ray_sock_peer_is_local(RAY_INVALID_SOCK)); + PASS(); +} + +/* The policy the send paths consult: local links never compress, others + * keep the compiled-in default. */ +static test_result_t test_ipc_link_threshold_local_vs_remote(void) { + int sv[2]; + TEST_ASSERT_EQ_I(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), 0); + TEST_ASSERT_EQ_U(ray_ipc_link_threshold((ray_sock_t)sv[0]), + RAY_IPC_COMPRESS_NEVER); + close(sv[0]); + close(sv[1]); + + int fd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(fd >= 0); + TEST_ASSERT_EQ_U(ray_ipc_link_threshold((ray_sock_t)fd), + (size_t)RAY_IPC_COMPRESS_THRESHOLD); + close(fd); + PASS(); +} + +/* RAY_IPC_COMPRESS_NEVER must actually suppress compression, not merely + * raise the bar: a payload far above the default threshold stays raw. */ +static test_result_t test_ipc_compress_never_suppresses(void) { + size_t n = (size_t)RAY_IPC_COMPRESS_THRESHOLD * 4; + uint8_t* src = (uint8_t*)ray_alloc_raw(n); + uint8_t* dst = (uint8_t*)ray_alloc_raw(n); + TEST_ASSERT_NOT_NULL(src); + TEST_ASSERT_NOT_NULL(dst); + memset(src, 0x5a, n); /* highly compressible under delta+RLE */ + + /* Above the default threshold it compresses... */ + TEST_ASSERT_TRUE(ray_ipc_compress_at(src, n, dst, n, + (size_t)RAY_IPC_COMPRESS_THRESHOLD) > 0); + /* ...and at NEVER it does not, however compressible the input is. */ + TEST_ASSERT_EQ_U(ray_ipc_compress_at(src, n, dst, n, + RAY_IPC_COMPRESS_NEVER), 0); + ray_free_raw(src); + ray_free_raw(dst); + PASS(); +} + +/* ---- .ipc.open options (#541 layer 2) ---------------------------------- + * The second argument stays an integer timeout, or becomes a dict with + * optional `timeout` and `compress`. `compress` is a threshold in bytes, + * not a flag: 0N never compresses, 0 always does, n compresses payloads + * larger than n. Absent means auto — the link-locality default. */ + +static ray_t* mk_opts(const char** keys, ray_t** vals, int64_t n) { + ray_t* k = ray_vec_new(RAY_SYM, n); + k->len = n; + int64_t* kd = (int64_t*)ray_data(k); + for (int64_t i = 0; i < n; i++) kd[i] = ray_sym_intern(keys[i], strlen(keys[i])); + ray_t* v = ray_list_new(n); + for (int64_t i = 0; i < n; i++) ray_list_append(v, vals[i]); + return ray_dict_new(k, v); +} + +/* An integer second argument keeps meaning "timeout", unchanged. */ +static test_result_t test_ipc_open_opts_int_timeout(void) { + ray_t* a = ray_i64(250); + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(a, &timeout, &thr); + TEST_ASSERT_NULL(err); + TEST_ASSERT_EQ_I(timeout, 250); + TEST_ASSERT_EQ_U(thr, RAY_IPC_COMPRESS_AUTO); + ray_release(a); + PASS(); +} + +static test_result_t test_ipc_open_opts_dict_timeout(void) { + const char* keys[] = { "timeout" }; + ray_t* vals[] = { ray_i64(400) }; + ray_t* d = mk_opts(keys, vals, 1); + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_NULL(err); + TEST_ASSERT_EQ_I(timeout, 400); + TEST_ASSERT_EQ_U(thr, RAY_IPC_COMPRESS_AUTO); + ray_release(vals[0]); + ray_release(d); + PASS(); +} + +/* 0N is "never compress", however large the payload. */ +static test_result_t test_ipc_open_opts_compress_null_never(void) { + const char* keys[] = { "compress" }; + ray_t* vals[] = { ray_i64(NULL_I64) }; + ray_t* d = mk_opts(keys, vals, 1); + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_NULL(err); + TEST_ASSERT_EQ_U(thr, RAY_IPC_COMPRESS_NEVER); + TEST_ASSERT_EQ_I(timeout, 0); /* unset -> default budget */ + ray_release(vals[0]); + ray_release(d); + PASS(); +} + +static test_result_t test_ipc_open_opts_compress_threshold(void) { + const char* keys[] = { "compress", "timeout" }; + ray_t* vals[] = { ray_i64(5000), ray_i64(75) }; + ray_t* d = mk_opts(keys, vals, 2); + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_NULL(err); + TEST_ASSERT_EQ_U(thr, 5000); + TEST_ASSERT_EQ_I(timeout, 75); + ray_release(vals[0]); ray_release(vals[1]); + ray_release(d); + PASS(); +} + +/* 0 is a legitimate threshold: compress everything. */ +static test_result_t test_ipc_open_opts_compress_zero_always(void) { + const char* keys[] = { "compress" }; + ray_t* vals[] = { ray_i64(0) }; + ray_t* d = mk_opts(keys, vals, 1); + int timeout = -1; + size_t thr = 99; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_NULL(err); + TEST_ASSERT_EQ_U(thr, 0); + ray_release(vals[0]); + ray_release(d); + PASS(); +} + +/* An unknown key is a typo, not a silent no-op. */ +static test_result_t test_ipc_open_opts_unknown_key(void) { + const char* keys[] = { "compres" }; + ray_t* vals[] = { ray_i64(1) }; + ray_t* d = mk_opts(keys, vals, 1); + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_TRUE(RAY_IS_ERR(err)); + ray_error_free(err); + ray_release(vals[0]); + ray_release(d); + PASS(); +} + +static test_result_t test_ipc_open_opts_bad_value_type(void) { + const char* keys[] = { "compress" }; + ray_t* vals[] = { ray_str("lots", 4) }; + ray_t* d = mk_opts(keys, vals, 1); + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_TRUE(RAY_IS_ERR(err)); + ray_error_free(err); + ray_release(vals[0]); + ray_release(d); + PASS(); +} + +static test_result_t test_ipc_open_opts_negative_rejected(void) { + const char* keys[] = { "compress" }; + ray_t* vals[] = { ray_i64(-5) }; + ray_t* d = mk_opts(keys, vals, 1); + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_TRUE(RAY_IS_ERR(err)); + ray_error_free(err); + ray_release(vals[0]); + ray_release(d); + + const char* k2[] = { "timeout" }; + ray_t* v2[] = { ray_i64(-1) }; + ray_t* d2 = mk_opts(k2, v2, 1); + err = ray_ipc_parse_open_opts(d2, &timeout, &thr); + TEST_ASSERT_TRUE(RAY_IS_ERR(err)); + ray_error_free(err); + ray_release(v2[0]); + ray_release(d2); + PASS(); +} + +/* 0N timeout means "use the default budget", matching an absent key. */ +static test_result_t test_ipc_open_opts_timeout_null_is_default(void) { + const char* keys[] = { "timeout" }; + ray_t* vals[] = { ray_i64(NULL_I64) }; + ray_t* d = mk_opts(keys, vals, 1); + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_NULL(err); + TEST_ASSERT_EQ_I(timeout, 0); + ray_release(vals[0]); + ray_release(d); + PASS(); +} + +/* An empty dict is valid: everything defaults. */ +static test_result_t test_ipc_open_opts_empty_dict(void) { + ray_t* k = ray_vec_new(RAY_SYM, 0); k->len = 0; + ray_t* v = ray_list_new(0); + ray_t* d = ray_dict_new(k, v); + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_NULL(err); + TEST_ASSERT_EQ_I(timeout, 0); + TEST_ASSERT_EQ_U(thr, RAY_IPC_COMPRESS_AUTO); + ray_release(d); + PASS(); +} + +/* A dict whose keys are not symbols must be refused, not silently + * ignored. ray_dict_find_idx returns -1 on a key-type mismatch rather + * than erroring, so skipping validation here would reinstate exactly the + * silent default the unknown-key check exists to prevent — and would + * accept a typo alongside it. */ +static test_result_t test_ipc_open_opts_string_keys_rejected(void) { + ray_t* k = ray_vec_new(RAY_STR, 1); + k->len = 1; + ray_t** kd = (ray_t**)ray_data(k); + kd[0] = ray_str("compress", 8); + ray_t* v = ray_list_new(1); + ray_t* val = ray_i64(0); + ray_list_append(v, val); + ray_release(val); + ray_t* d = ray_dict_new(k, v); + + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_TRUE(RAY_IS_ERR(err)); + ray_error_free(err); + ray_release(d); + PASS(); +} + +/* Keys built programmatically as a list of sym atoms still validate. */ +static test_result_t test_ipc_open_opts_list_sym_keys(void) { + ray_t* k = ray_list_new(1); + ray_t* ks = ray_sym(ray_sym_intern("compress", 8)); + ray_list_append(k, ks); + ray_release(ks); + ray_t* v = ray_list_new(1); + ray_t* val = ray_i64(4096); + ray_list_append(v, val); + ray_release(val); + ray_t* d = ray_dict_new(k, v); + + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_NULL(err); + TEST_ASSERT_EQ_U(thr, 4096); + ray_release(d); + PASS(); +} + +/* ...and an unknown name in that form is still caught. */ +static test_result_t test_ipc_open_opts_list_sym_keys_unknown(void) { + ray_t* k = ray_list_new(1); + ray_t* ks = ray_sym(ray_sym_intern("compres", 7)); + ray_list_append(k, ks); + ray_release(ks); + ray_t* v = ray_list_new(1); + ray_t* val = ray_i64(1); + ray_list_append(v, val); + ray_release(val); + ray_t* d = ray_dict_new(k, v); + + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(d, &timeout, &thr); + TEST_ASSERT_TRUE(RAY_IS_ERR(err)); + ray_error_free(err); + ray_release(d); + PASS(); +} + +static test_result_t test_ipc_open_opts_wrong_arg_type(void) { + ray_t* a = ray_str("nope", 4); + int timeout = -1; + size_t thr = 0; + ray_t* err = ray_ipc_parse_open_opts(a, &timeout, &thr); + TEST_ASSERT_TRUE(RAY_IS_ERR(err)); + ray_error_free(err); + ray_release(a); + PASS(); +} + +/* A compressed frame must survive a real socket round trip. + * + * Since #541 a loopback link never compresses, so every existing + * localhost test now exercises only the raw path — including + * ipc_diff.rfl's "compression boundary" section, whose comment claimed + * that coverage. Force it back on for this connection and send a + * payload well past the threshold, so ray_ipc_compress on the way out + * and deser_frame's decompression on the way in are both exercised and + * the value is checked for equality, not just for arriving. */ +static test_result_t test_ipc_compressed_roundtrip(void) { + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + + /* threshold 0 = compress everything, overriding the loopback default */ + int64_t h = ray_ipc_connect_opts("127.0.0.1", srv.port, NULL, NULL, 0, 0); + TEST_ASSERT((h) >= (0), "connected with compression forced"); + TEST_ASSERT_EQ_U(ray_ipc_handle_threshold(h), 0); + + /* A long, highly compressible source string: >2 KB serialized, and + * delta+RLE actually shrinks it, so the COMPRESSED branch is taken + * rather than falling back to raw on a poor ratio. */ + size_t pad_len = 4096; + size_t cap = pad_len + 64; + char* expr = (char*)ray_alloc_raw(cap); + TEST_ASSERT_NOT_NULL(expr); + int n = snprintf(expr, cap, "(count \""); + memset(expr + n, 'A', pad_len); + n += (int)pad_len; + n += snprintf(expr + n, cap - (size_t)n, "\")"); + + ray_t* msg = ray_str(expr, (size_t)n); + TEST_ASSERT_TRUE(ray_serde_size(msg) > 2000); + ray_t* r = ray_ipc_send(h, msg); + ray_release(msg); + ray_free_raw(expr); + + TEST_ASSERT_NOT_NULL(r); + TEST_ASSERT_FALSE(RAY_IS_ERR(r)); + TEST_ASSERT_EQ_I(r->type, -RAY_I64); + TEST_ASSERT_EQ_I(r->i64, (int64_t)pad_len); /* exact value, not just arrival */ + ray_release(r); + + ray_ipc_close(h); + ray_test_server_stop(&srv); + PASS(); +} + +/* ---- Wire-level characterization --------------------------------------- + * These drive a raw socket against a poll-based server and assert on the + * BYTES of the frames that come back — header fields and the compression + * flag — rather than on the deserialized result. + * + * They exist because the suite had no such coverage: a bug that framed + * every loopback response uncompressed when an explicit threshold asked + * for compression passed all 3800+ tests and was only caught by counting + * bytes on a socket outside the suite. Anything that restructures the + * IPC server must keep these green. */ + +/* Read exactly n bytes, or fail. */ +static int wire_recv_exact(ray_sock_t s, void* buf, size_t n) { + size_t got = 0; + while (got < n) { + int64_t r = ray_sock_recv(s, (uint8_t*)buf + got, n - got); + if (r <= 0) return -1; + got += (size_t)r; + } + return 0; +} + +/* Connect a raw socket and complete the 2-byte handshake. */ +static ray_sock_t wire_connect(uint16_t port) { + ray_sock_t s = ray_sock_connect("127.0.0.1", port, 2000); + if (s == RAY_INVALID_SOCK) return RAY_INVALID_SOCK; + uint8_t hs[2] = { RAY_SERDE_WIRE_VERSION, 0x00 }; + if (ray_sock_send(s, hs, 2) < 0) { ray_sock_close(s); return RAY_INVALID_SOCK; } + uint8_t resp[2]; + if (wire_recv_exact(s, resp, 2) < 0 || resp[0] != RAY_SERDE_WIRE_VERSION) { + ray_sock_close(s); + return RAY_INVALID_SOCK; + } + return s; +} + +/* Frame and send one message with an explicit msgtype, uncompressed. */ +static int wire_send(ray_sock_t s, uint8_t msgtype, ray_t* obj) { + int64_t n = ray_serde_size(obj); + if (n <= 0) return -1; + uint8_t* payload = (uint8_t*)ray_alloc_raw((size_t)n); + if (!payload) return -1; + ray_ser_raw(payload, obj); + ray_ipc_header_t hdr = { + .prefix = RAY_SERDE_PREFIX, + .version = RAY_SERDE_WIRE_VERSION, + .flags = 0, + .endian = RAY_SERDE_ENDIAN, + .msgtype = msgtype, + .size = n, + }; + int rc = (ray_sock_send(s, &hdr, sizeof hdr) < 0 || + ray_sock_send(s, payload, (size_t)n) < 0) ? -1 : 0; + ray_free_raw(payload); + return rc; +} + +/* Every field of a RESP header, byte for byte. */ +static test_result_t test_ipc_wire_resp_header_fields(void) { + ray_poll_t* poll; ray_vm_t* vm; uint16_t port; ray_thread_t tid; + test_result_t sr = wire_start_server(&poll, &port, &vm, &tid); + if (sr.status != TEST_PASS) return sr; + + ray_sock_t s = wire_connect(port); + TEST_ASSERT_TRUE(s != RAY_INVALID_SOCK); + + ray_t* q = ray_str("(+ 20 22)", 9); + TEST_ASSERT_EQ_I(wire_send(s, RAY_IPC_MSG_SYNC, q), 0); + ray_release(q); + + ray_ipc_header_t hdr; + TEST_ASSERT_EQ_I(wire_recv_exact(s, &hdr, sizeof hdr), 0); + TEST_ASSERT_EQ_U(hdr.prefix, RAY_SERDE_PREFIX); + TEST_ASSERT_EQ_I(hdr.version, RAY_SERDE_WIRE_VERSION); + TEST_ASSERT_EQ_I(hdr.endian, RAY_SERDE_ENDIAN); + TEST_ASSERT_EQ_I(hdr.msgtype, RAY_IPC_MSG_RESP); + TEST_ASSERT_EQ_I(hdr.flags, 0); /* tiny payload: never compressed */ + TEST_ASSERT_TRUE(hdr.size > 0 && hdr.size < 256); + + uint8_t* body = (uint8_t*)ray_alloc_raw((size_t)hdr.size); + TEST_ASSERT_NOT_NULL(body); + TEST_ASSERT_EQ_I(wire_recv_exact(s, body, (size_t)hdr.size), 0); + int64_t blen = hdr.size; + ray_t* val = ray_de_raw(body, &blen); + TEST_ASSERT_NOT_NULL(val); + TEST_ASSERT_FALSE(RAY_IS_ERR(val)); + TEST_ASSERT_EQ_I(val->type, -RAY_I64); + TEST_ASSERT_EQ_I(val->i64, 42); + ray_release(val); + ray_free_raw(body); + + ray_sock_close(s); + wire_stop_server(poll, port, vm, tid); + PASS(); +} + +/* Layer 1 (#541) at the wire: a loopback peer gets raw frames however + * large and however compressible the result is. */ +static test_result_t test_ipc_wire_loopback_never_compressed(void) { + ray_poll_t* poll; ray_vm_t* vm; uint16_t port; ray_thread_t tid; + test_result_t sr = wire_start_server(&poll, &port, &vm, &tid); + if (sr.status != TEST_PASS) return sr; + + ray_sock_t s = wire_connect(port); + TEST_ASSERT_TRUE(s != RAY_INVALID_SOCK); + + /* 40k of a highly compressible run — far above the 2000 default. */ + ray_t* q = ray_str("(take [7] 5000)", 15); + TEST_ASSERT_EQ_I(wire_send(s, RAY_IPC_MSG_SYNC, q), 0); + ray_release(q); + + ray_ipc_header_t hdr; + TEST_ASSERT_EQ_I(wire_recv_exact(s, &hdr, sizeof hdr), 0); + TEST_ASSERT_EQ_I(hdr.msgtype, RAY_IPC_MSG_RESP); + TEST_ASSERT_TRUE(hdr.size > 2000); + /* The whole point: the compression bit stays clear on a local link. */ + TEST_ASSERT_EQ_I(hdr.flags & RAY_IPC_FLAG_COMPRESSED, 0); + + uint8_t* body = (uint8_t*)ray_alloc_raw((size_t)hdr.size); + TEST_ASSERT_NOT_NULL(body); + TEST_ASSERT_EQ_I(wire_recv_exact(s, body, (size_t)hdr.size), 0); + int64_t blen = hdr.size; + ray_t* val = ray_de_raw(body, &blen); + TEST_ASSERT_NOT_NULL(val); + TEST_ASSERT_FALSE(RAY_IS_ERR(val)); + TEST_ASSERT_EQ_I(val->len, 5000); + ray_release(val); + ray_free_raw(body); + + ray_sock_close(s); + wire_stop_server(poll, port, vm, tid); + PASS(); +} + +/* An ASYNC frame is evaluated and produces no reply at all. */ +static test_result_t test_ipc_wire_async_no_reply(void) { + ray_poll_t* poll; ray_vm_t* vm; uint16_t port; ray_thread_t tid; + test_result_t sr = wire_start_server(&poll, &port, &vm, &tid); + if (sr.status != TEST_PASS) return sr; + + ray_sock_t s = wire_connect(port); + TEST_ASSERT_TRUE(s != RAY_INVALID_SOCK); + + ray_t* q = ray_str("(set _wire_async 11)", 20); + TEST_ASSERT_EQ_I(wire_send(s, RAY_IPC_MSG_ASYNC, q), 0); + ray_release(q); + + /* Nothing comes back... */ + TEST_ASSERT_EQ_I(ray_sock_wait_readable(s, 150), 0); + + /* ...but it did run: read the binding back over a SYNC frame. */ + ray_t* q2 = ray_str("(+ _wire_async 0)", 17); + TEST_ASSERT_EQ_I(wire_send(s, RAY_IPC_MSG_SYNC, q2), 0); + ray_release(q2); + + ray_ipc_header_t hdr; + TEST_ASSERT_EQ_I(wire_recv_exact(s, &hdr, sizeof hdr), 0); + TEST_ASSERT_EQ_I(hdr.msgtype, RAY_IPC_MSG_RESP); + uint8_t* body = (uint8_t*)ray_alloc_raw((size_t)hdr.size); + TEST_ASSERT_NOT_NULL(body); + TEST_ASSERT_EQ_I(wire_recv_exact(s, body, (size_t)hdr.size), 0); + int64_t blen = hdr.size; + ray_t* val = ray_de_raw(body, &blen); + TEST_ASSERT_EQ_I(val->i64, 11); + ray_release(val); + ray_free_raw(body); + + ray_sock_close(s); + wire_stop_server(poll, port, vm, tid); + PASS(); +} + +/* A peer speaking another wire version is dropped without a reply. */ +static test_result_t test_ipc_wire_version_mismatch_drops(void) { + ray_poll_t* poll; ray_vm_t* vm; uint16_t port; ray_thread_t tid; + test_result_t sr = wire_start_server(&poll, &port, &vm, &tid); + if (sr.status != TEST_PASS) return sr; + + ray_sock_t s = ray_sock_connect("127.0.0.1", port, 2000); + TEST_ASSERT_TRUE(s != RAY_INVALID_SOCK); + uint8_t hs[2] = { (uint8_t)(RAY_SERDE_WIRE_VERSION + 1), 0x00 }; + ray_sock_send(s, hs, 2); + + /* The server deregisters instead of answering: readable with 0 bytes + * (EOF) rather than a 2-byte handshake reply. */ + uint8_t resp[2]; + TEST_ASSERT_EQ_I(wire_recv_exact(s, resp, 2), -1); + + ray_sock_close(s); + wire_stop_server(poll, port, vm, tid); + PASS(); +} + +/* CHARACTERIZATION OF A WART, not an endorsement: ipc_read_header does + * not validate msgtype, and ipc_read_payload treats anything that is not + * RESP as async — so an unknown msgtype is EVALUATED and silently + * answered with nothing. Pinned here so a refactor cannot change it by + * accident; when msgtype validation lands this test should be updated + * deliberately to expect a dropped connection. */ +static test_result_t test_ipc_wire_unknown_msgtype_is_evaluated(void) { + ray_poll_t* poll; ray_vm_t* vm; uint16_t port; ray_thread_t tid; + test_result_t sr = wire_start_server(&poll, &port, &vm, &tid); + if (sr.status != TEST_PASS) return sr; + + ray_sock_t s = wire_connect(port); + TEST_ASSERT_TRUE(s != RAY_INVALID_SOCK); + + ray_t* q = ray_str("(set _wire_svc 99)", 18); + TEST_ASSERT_EQ_I(wire_send(s, 3 /* no such msgtype */, q), 0); + ray_release(q); + + TEST_ASSERT_EQ_I(ray_sock_wait_readable(s, 150), 0); /* no reply */ + + ray_t* q2 = ray_str("(+ _wire_svc 0)", 15); + TEST_ASSERT_EQ_I(wire_send(s, RAY_IPC_MSG_SYNC, q2), 0); + ray_release(q2); + + ray_ipc_header_t hdr; + TEST_ASSERT_EQ_I(wire_recv_exact(s, &hdr, sizeof hdr), 0); + uint8_t* body = (uint8_t*)ray_alloc_raw((size_t)hdr.size); + TEST_ASSERT_NOT_NULL(body); + TEST_ASSERT_EQ_I(wire_recv_exact(s, body, (size_t)hdr.size), 0); + int64_t blen = hdr.size; + ray_t* val = ray_de_raw(body, &blen); + /* It ran. */ + TEST_ASSERT_EQ_I(val->i64, 99); + ray_release(val); + ray_free_raw(body); + + ray_sock_close(s); + wire_stop_server(poll, port, vm, tid); + PASS(); +} + +/* Framing a message must honour the threshold it is given: the SAME payload + * frames compressed at the default and raw at NEVER. This is what lets a + * multicast topic build one framing per distinct subscriber policy (#551) + * instead of one framing at the compiled-in default for everybody. */ +static test_result_t test_ipc_frame_async_at_threshold(void) { + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + + /* Ascending i64 run: every delta is 1, so delta+RLE shrinks it hard. */ + ray_t* big = ray_eval_str("(til 5000)"); + TEST_ASSERT_NOT_NULL(big); + TEST_ASSERT_FALSE(RAY_IS_ERR(big)); + TEST_ASSERT_TRUE(ray_serde_size(big) > 2000); + + ray_poll_frame_t* f_def = NULL; + TEST_ASSERT_EQ_I(ray_ipc_frame_async_at(big, (size_t)RAY_IPC_COMPRESS_THRESHOLD, &f_def), RAY_OK); + TEST_ASSERT_NOT_NULL(f_def); + ray_ipc_header_t hd; + memcpy(&hd, f_def->data, sizeof hd); + TEST_ASSERT_TRUE((hd.flags & RAY_IPC_FLAG_COMPRESSED) != 0); + + ray_poll_frame_t* f_raw = NULL; + TEST_ASSERT_EQ_I(ray_ipc_frame_async_at(big, RAY_IPC_COMPRESS_NEVER, &f_raw), RAY_OK); + TEST_ASSERT_NOT_NULL(f_raw); + ray_ipc_header_t hr; + memcpy(&hr, f_raw->data, sizeof hr); + TEST_ASSERT_EQ_I(hr.flags & RAY_IPC_FLAG_COMPRESSED, 0); + + /* The raw framing is the bigger one — that is the trade #541 measured. */ + TEST_ASSERT_TRUE(f_raw->size > f_def->size); + + ray_poll_frame_release(f_def); + ray_poll_frame_release(f_raw); + ray_release(big); + ray_test_server_stop(&srv); + PASS(); +} + +/* A multicast publication to loopback subscribers must not be compressed. + * + * #549 made compression per-link, but the fan-out frame is shared by every + * subscriber of a topic (#487), so publish still framed at the compiled-in + * threshold: a tickerplant with only local subscribers compressed on every + * publication and every subscriber decompressed (#551). Subscribing a raw + * socket lets us read the published frame's header directly. */ +static test_result_t test_ipc_mcast_local_not_compressed(void) { + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + + ray_sock_t s = wire_connect(srv.port); + TEST_ASSERT_TRUE(s != RAY_INVALID_SOCK); + + /* .mc.sub binds to .ipc.handle — the connection the eval runs on. */ + const char* ssrc = "(.mc.sub 'depth null)"; + ray_t* sub = ray_str(ssrc, strlen(ssrc)); + TEST_ASSERT_EQ_I(wire_send(s, RAY_IPC_MSG_SYNC, sub), 0); + ray_release(sub); + ray_ipc_header_t rh; + TEST_ASSERT_EQ_I(wire_recv_exact(s, &rh, sizeof rh), 0); + TEST_ASSERT_EQ_I(rh.msgtype, RAY_IPC_MSG_RESP); + uint8_t* rb = (uint8_t*)ray_alloc_raw((size_t)rh.size); + TEST_ASSERT_NOT_NULL(rb); + TEST_ASSERT_EQ_I(wire_recv_exact(s, rb, (size_t)rh.size), 0); + ray_free_raw(rb); + + /* Publish well past the threshold and highly compressible: an ascending + * i64 run, so every delta is 1. */ + int64_t h = ray_ipc_connect("127.0.0.1", srv.port, NULL, NULL, 0); + TEST_ASSERT((h) >= (0), "control handle"); + const char* psrc = "(.mc.pub 'depth (til 5000))"; + ray_t* pub = ray_str(psrc, strlen(psrc)); + ray_t* pr = ray_ipc_send(h, pub); + ray_release(pub); + /* .mc.pub returns the topic's SEQUENCE number, not a subscriber count, + * so this only says the publish itself did not error. What actually + * rules out a vacuous pass is that the read below must produce a real + * frame: if the publication never fanned out, ray_sock_wait_readable + * times out and the test fails there. */ + TEST_ASSERT_NOT_NULL(pr); + TEST_ASSERT_FALSE(RAY_IS_ERR(pr)); + TEST_ASSERT_EQ_I(pr->type, -RAY_I64); + ray_release(pr); + + /* The publication arrives as an ASYNC frame on the subscriber. */ + TEST_ASSERT_EQ_I(ray_sock_wait_readable(s, 5000), 1); + ray_ipc_header_t ph; + TEST_ASSERT_EQ_I(wire_recv_exact(s, &ph, sizeof ph), 0); + TEST_ASSERT_EQ_I(ph.msgtype, RAY_IPC_MSG_ASYNC); + TEST_ASSERT_TRUE(ph.size > 2000); + /* The point of #551: a loopback subscriber gets raw bytes. */ + TEST_ASSERT_EQ_I(ph.flags & RAY_IPC_FLAG_COMPRESSED, 0); + + uint8_t* pb = (uint8_t*)ray_alloc_raw((size_t)ph.size); + TEST_ASSERT_NOT_NULL(pb); + TEST_ASSERT_EQ_I(wire_recv_exact(s, pb, (size_t)ph.size), 0); + int64_t plen = ph.size; + ray_t* body = ray_de_raw(pb, &plen); + TEST_ASSERT_NOT_NULL(body); + TEST_ASSERT_FALSE(RAY_IS_ERR(body)); + ray_release(body); + ray_free_raw(pb); + + ray_ipc_close(h); + ray_sock_close(s); + ray_test_server_stop(&srv); + PASS(); +} + const test_entry_t ipc_entries[] = { { "ipc/listen_bind_addr", test_ipc_listen_bind_addr, ipc_setup, ipc_teardown }, { "ipc/send_verbose", test_ipc_send_verbose, ipc_setup, ipc_teardown }, @@ -2170,8 +2659,6 @@ const test_entry_t ipc_entries[] = { { "ipc/send_large_result", test_ipc_send_large_result, ipc_setup, ipc_teardown }, { "ipc/send_large_msg_client_compress", test_ipc_send_large_msg_client_compress, ipc_setup, ipc_teardown }, { "ipc/send_verbose_large_result", test_ipc_send_verbose_large_result, ipc_setup, ipc_teardown }, - { "ipc/server_destroy_active_conns", test_ipc_server_destroy_active_conns, ipc_setup, ipc_teardown }, - { "ipc/server_conn_swap", test_ipc_server_conn_swap, ipc_setup, ipc_teardown }, { "ipc/journal_restricted", test_ipc_journal_restricted, ipc_setup, ipc_teardown }, { "ipc/send_lazy_msg", test_ipc_send_lazy_msg, ipc_setup, ipc_teardown }, { "ipc/hooks_lifecycle", test_ipc_hooks_lifecycle, ipc_setup, ipc_teardown }, @@ -2180,5 +2667,47 @@ const test_entry_t ipc_entries[] = { { "ipc/post_invalid_handle", test_ipc_post_invalid_handle, ipc_setup, ipc_teardown }, { "ipc/post_non_serializable", test_ipc_post_non_serializable, ipc_setup, ipc_teardown }, { "ipc/server_push", test_ipc_server_push, ipc_setup, ipc_teardown }, + /* link locality -> compression policy (#541 layer 1) */ + { "ipc/addr_local/ipv4_loopback", test_ipc_addr_local_ipv4_loopback, ipc_setup, ipc_teardown }, + { "ipc/addr_local/ipv4_loopback_block", test_ipc_addr_local_ipv4_loopback_block, ipc_setup, ipc_teardown }, + { "ipc/addr_local/ipv4_remote", test_ipc_addr_local_ipv4_remote, ipc_setup, ipc_teardown }, + { "ipc/addr_local/ipv6_loopback", test_ipc_addr_local_ipv6_loopback, ipc_setup, ipc_teardown }, + { "ipc/addr_local/ipv6_mapped_loopback",test_ipc_addr_local_ipv6_mapped_loopback, ipc_setup, ipc_teardown }, + { "ipc/addr_local/ipv6_remote", test_ipc_addr_local_ipv6_remote, ipc_setup, ipc_teardown }, + { "ipc/addr_local/af_unix", test_ipc_addr_local_af_unix, ipc_setup, ipc_teardown }, + { "ipc/addr_local/rejects_garbage", test_ipc_addr_local_rejects_garbage, ipc_setup, ipc_teardown }, + { "ipc/peer_is_local/socketpair", test_ipc_peer_is_local_socketpair, ipc_setup, ipc_teardown }, + { "ipc/peer_is_local/unconnected", test_ipc_peer_is_local_unconnected, ipc_setup, ipc_teardown }, + { "ipc/peer_is_local/invalid_fd", test_ipc_peer_is_local_invalid_fd, ipc_setup, ipc_teardown }, + { "ipc/link_threshold/local_vs_remote", test_ipc_link_threshold_local_vs_remote, ipc_setup, ipc_teardown }, + { "ipc/compress_never_suppresses", test_ipc_compress_never_suppresses, ipc_setup, ipc_teardown }, + + /* .ipc.open options dict (#541 layer 2) */ + { "ipc/open_opts/int_timeout", test_ipc_open_opts_int_timeout, ipc_setup, ipc_teardown }, + { "ipc/open_opts/dict_timeout", test_ipc_open_opts_dict_timeout, ipc_setup, ipc_teardown }, + { "ipc/open_opts/compress_null", test_ipc_open_opts_compress_null_never, ipc_setup, ipc_teardown }, + { "ipc/open_opts/compress_threshold",test_ipc_open_opts_compress_threshold, ipc_setup, ipc_teardown }, + { "ipc/open_opts/compress_zero", test_ipc_open_opts_compress_zero_always,ipc_setup, ipc_teardown }, + { "ipc/open_opts/unknown_key", test_ipc_open_opts_unknown_key, ipc_setup, ipc_teardown }, + { "ipc/open_opts/bad_value_type", test_ipc_open_opts_bad_value_type, ipc_setup, ipc_teardown }, + { "ipc/open_opts/negative_rejected", test_ipc_open_opts_negative_rejected, ipc_setup, ipc_teardown }, + { "ipc/open_opts/timeout_null", test_ipc_open_opts_timeout_null_is_default, ipc_setup, ipc_teardown }, + { "ipc/open_opts/empty_dict", test_ipc_open_opts_empty_dict, ipc_setup, ipc_teardown }, + { "ipc/open_opts/wrong_arg_type", test_ipc_open_opts_wrong_arg_type, ipc_setup, ipc_teardown }, + { "ipc/open_opts/string_keys_rejected", test_ipc_open_opts_string_keys_rejected, ipc_setup, ipc_teardown }, + { "ipc/open_opts/list_sym_keys", test_ipc_open_opts_list_sym_keys, ipc_setup, ipc_teardown }, + { "ipc/open_opts/list_sym_keys_unknown",test_ipc_open_opts_list_sym_keys_unknown,ipc_setup, ipc_teardown }, + + { "ipc/mcast_local_not_compressed", test_ipc_mcast_local_not_compressed, ipc_setup, ipc_teardown }, + { "ipc/frame_async_at_threshold", test_ipc_frame_async_at_threshold, ipc_setup, ipc_teardown }, + { "ipc/compressed_roundtrip", test_ipc_compressed_roundtrip, ipc_setup, ipc_teardown }, + + /* wire-level characterization (refactor guard) */ + { "ipc/wire/resp_header_fields", test_ipc_wire_resp_header_fields, ipc_setup, ipc_teardown }, + { "ipc/wire/loopback_never_compressed",test_ipc_wire_loopback_never_compressed, ipc_setup, ipc_teardown }, + { "ipc/wire/async_no_reply", test_ipc_wire_async_no_reply, ipc_setup, ipc_teardown }, + { "ipc/wire/version_mismatch_drops", test_ipc_wire_version_mismatch_drops, ipc_setup, ipc_teardown }, + { "ipc/wire/unknown_msgtype_evaluated",test_ipc_wire_unknown_msgtype_is_evaluated, ipc_setup, ipc_teardown }, + { NULL, NULL, NULL, NULL }, }; diff --git a/test/test_lang.c b/test/test_lang.c index 221036ae9..3ca768565 100644 --- a/test/test_lang.c +++ b/test/test_lang.c @@ -46,6 +46,7 @@ #include "lang/eval.h" #include "lang/nfo.h" #include "lang/format.h" +#include "core/runtime.h" /* ray_error_msg */ #include "ops/internal.h" #include "ops/ops.h" #include "ops/temporal.h" @@ -3917,15 +3918,21 @@ static bool lang_parted_insert_onecol(const char* root, const char* part, } static test_result_t test_eval_insert_parted_key_types_impl(const char* root) { - char iroot[900], sroot[900], src[1400], next_path[1200]; + char iroot[900], sroot[900], droot[900], oroot[900], src[1400], + next_path[1200]; int ni = snprintf(iroot, sizeof(iroot), "%s/i64", root); int ns = snprintf(sroot, sizeof(sroot), "%s/sym", root); + int nd = snprintf(droot, sizeof(droot), "%s/dup", root); + int no = snprintf(oroot, sizeof(oroot), "%s/ovf", root); TEST_ASSERT(ni > 0 && (size_t)ni < sizeof(iroot), "format i64 root"); TEST_ASSERT(ns > 0 && (size_t)ns < sizeof(sroot), "format sym root"); + TEST_ASSERT(nd > 0 && (size_t)nd < sizeof(droot), "format dup root"); + TEST_ASSERT(no > 0 && (size_t)no < sizeof(oroot), "format ovf root"); - /* collect_part_dirs is byte-lexical, so these become [10,2]. The - * insert validator must reject that malformed numeric MAPCOMMON order - * instead of treating 2 as a growable tail after 10. */ + /* collect_part_dirs orders an all-integer set by VALUE (by name these + * would be [10,2]), so the I64 MAPCOMMON keys load as [2,10]: 10 is the + * growable tail, a later key appends after it, and an earlier one is + * historical. */ TEST_ASSERT(lang_parted_insert_onecol(iroot, "10", 10), "save integer partition 10"); TEST_ASSERT(lang_parted_insert_onecol(iroot, "2", 2), @@ -3937,9 +3944,48 @@ static test_result_t test_eval_insert_parted_key_types_impl(const char* root) { TEST_ASSERT_NOT_NULL(setup); TEST_ASSERT_FALSE(RAY_IS_ERR(setup)); ray_release(setup); - ASSERT_ER_CODE("(insert pi 11 (list 11))", "corrupt"); + ASSERT_EQ("(at (select {from: pi where: (> id 0)}) 'part)", "[2 10]"); + ASSERT_EQ("(count (insert pi 11 (list 11)))", "3"); + ASSERT_EQ("(count (insert pi 10 (list 12)))", "3"); + ASSERT_ER_CODE("(insert pi 5 (list 5))", "domain"); ASSERT_EQ("(count pi)", "2"); + /* Two spellings of one value ("02" and "2") both parse to key 2. They + * sort by name after the value (so the order is filesystem-independent) + * and load as equal I64 keys, which the insert validator must reject as + * corrupt rather than treat as a growable tail. */ + TEST_ASSERT(lang_parted_insert_onecol(droot, "2", 2), + "save integer partition 2"); + TEST_ASSERT(lang_parted_insert_onecol(droot, "02", 20), + "save integer partition 02"); + n = snprintf(src, sizeof(src), + "(set pd (.db.parted.get \"%s\" 'trades))", droot); + TEST_ASSERT(n > 0 && (size_t)n < sizeof(src), "format dup parted get"); + setup = ray_eval_str(src); + TEST_ASSERT_NOT_NULL(setup); + TEST_ASSERT_FALSE(RAY_IS_ERR(setup)); + ray_release(setup); + ASSERT_EQ("(at (select {from: pd where: (> id 0)}) 'part)", "[2 2]"); + ASSERT_EQ("(at (select {from: pd where: (> id 0)}) 'id)", "[20 2]"); + ASSERT_ER_CODE("(insert pd 3 (list 3))", "corrupt"); + + /* A digit-only name past int64 makes the set unreadable: the error names + * the directory instead of silently retyping every key to SYM. */ + TEST_ASSERT(lang_parted_insert_onecol(oroot, "1", 1), + "save integer partition 1"); + TEST_ASSERT(lang_parted_insert_onecol(oroot, "99999999999999999999", 2), + "save overflowing integer partition"); + n = snprintf(src, sizeof(src), "(.db.parted.get \"%s\" 'trades)", oroot); + TEST_ASSERT(n > 0 && (size_t)n < sizeof(src), "format overflow parted get"); + ray_t* ovf = ray_eval_str(src); + TEST_ASSERT_NOT_NULL(ovf); + TEST_ASSERT_TRUE(RAY_IS_ERR(ovf)); + TEST_ASSERT_STR_EQ(ray_err_code(ovf), "corrupt"); + const char* ovf_msg = ray_error_msg(); + TEST_ASSERT_NOT_NULL(ovf_msg); + TEST_ASSERT_TRUE(strstr(ovf_msg, "99999999999999999999") != NULL); + ray_error_free(ovf); + /* Opaque directory names use a SYM MAPCOMMON key. Equal-key growth and * a lexically later key follow the same immutable-tail contract. */ TEST_ASSERT(lang_parted_insert_onecol(sroot, "1.2", 1), diff --git a/test/test_mcast.c b/test/test_mcast.c index 4475bafda..8fa810535 100644 --- a/test/test_mcast.c +++ b/test/test_mcast.c @@ -937,6 +937,44 @@ static test_result_t test_mcast_txlimit_overflow_disconnects(void) { * closes) and the client poll (outbound closes), so it records the * direction it saw rather than bare handle ids, which the two polls * number independently. */ +/* An explicit `compress` from .ipc.open must survive onto the connection + * and be used by the send paths. Regression: conn_write_msg re-derived + * the threshold from the peer address per frame, so a loopback link + * silently ignored the override and never compressed. */ +static test_result_t test_ipc_connect_compress_override(void) { + ray_poll_t* poll; + ray_vm_t* vm; + uint16_t port; + ray_thread_t tid; + test_result_t sr = start_server(&poll, &port, &vm, &tid); + if (sr.status != TEST_PASS) return sr; + + /* Auto: a loopback peer never compresses. */ + int64_t h_auto = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); + TEST_ASSERT((h_auto) >= (0), "auto connected"); + TEST_ASSERT_EQ_U(ray_ipc_handle_threshold(h_auto), RAY_IPC_COMPRESS_NEVER); + + /* Explicit threshold wins over the locality default... */ + int64_t h_on = ray_ipc_connect_opts("127.0.0.1", port, NULL, NULL, 0, 2000); + TEST_ASSERT((h_on) >= (0), "override connected"); + TEST_ASSERT_EQ_U(ray_ipc_handle_threshold(h_on), 2000); + + /* ...in both directions: NEVER stays NEVER on a link that would + * otherwise have compressed. */ + int64_t h_off = ray_ipc_connect_opts("127.0.0.1", port, NULL, NULL, 0, + RAY_IPC_COMPRESS_NEVER); + TEST_ASSERT((h_off) >= (0), "never connected"); + TEST_ASSERT_EQ_U(ray_ipc_handle_threshold(h_off), RAY_IPC_COMPRESS_NEVER); + + pump_client(); + ray_ipc_close(h_auto); + ray_ipc_close(h_on); + ray_ipc_close(h_off); + pump_client(); + stop_server(poll, port, vm, tid); + PASS(); +} + static test_result_t test_ipc_outbound_close_hook(void) { ray_t* r = ray_eval_str( "(set _oc_in 0)" @@ -1251,5 +1289,6 @@ const test_entry_t mcast_entries[] = { { "mcast/shared_frame_across_subs", test_mcast_shared_frame_across_subscribers, mcast_setup, mcast_teardown }, { "mcast/txlimit_overflow_disconnects", test_mcast_txlimit_overflow_disconnects, mcast_setup, mcast_teardown }, { "ipc/outbound_close_hook", test_ipc_outbound_close_hook, mcast_setup, mcast_teardown }, + { "ipc/connect_compress_override", test_ipc_connect_compress_override, mcast_setup, mcast_teardown }, { NULL, NULL, NULL, NULL }, }; diff --git a/test/test_pool.c b/test/test_pool.c index 1b757d0a9..7e2f177de 100644 --- a/test/test_pool.c +++ b/test/test_pool.c @@ -35,6 +35,7 @@ #include "mem/heap.h" #include "ops/ops.h" #include +#include #include #include @@ -855,29 +856,42 @@ static test_result_t test_dispatch_workers_participate(void) { /* -------------------------------------------------------------------------- * Test: ray_pool_dispatch_n with n_tasks exceeding MAX_RING_CAP (1<<16). * - * Drives the growth-loop early-out (`new_cap < MAX_RING_CAP`) on line ~335 - * and the post-growth clamp (`if (n_tasks > pool->task_cap) n_tasks = ...`) - * on line ~347. With n_tasks = 70000 and MAX_RING_CAP = 65536, the ring - * grows to 65536 then clamps n_tasks down to 65536; only 65536 tasks fire. + * The ring grows to MAX_RING_CAP and stops; the tasks past it must still run, + * in further rounds, each with its absolute index. Until the rounds were + * added the pool clamped n_tasks to the ring and silently dropped the rest + * (a window with more partitions than the ring lost every partition past + * 65536). * -------------------------------------------------------------------------- */ +typedef struct { + _Atomic(int64_t) calls; + _Atomic(int64_t) start_sum; /* sum of every task's absolute index */ + _Atomic(int64_t) start_max; +} pool_index_ctx_t; + +static void pool_index_fn(void* ctx, uint32_t worker_id, int64_t start, int64_t end) { + (void)worker_id; + pool_index_ctx_t* c = (pool_index_ctx_t*)ctx; + if (end != start + 1) return; /* a range would break the [i, i+1) contract: leave calls short */ + atomic_fetch_add_explicit(&c->calls, 1, memory_order_relaxed); + atomic_fetch_add_explicit(&c->start_sum, start, memory_order_relaxed); + int64_t seen = atomic_load_explicit(&c->start_max, memory_order_relaxed); + while (start > seen && !atomic_compare_exchange_weak_explicit(&c->start_max, &seen, start, + memory_order_relaxed, memory_order_relaxed)) {} +} static test_result_t test_dispatch_n_max_ring_cap_clamp(void) { ray_heap_init(); - ray_pool_t pool; TEST_ASSERT_EQ_I(ray_pool_create(&pool, 1), RAY_OK); - - pool_count_ctx_t ctx = {0}; - /* MAX_RING_CAP is 1<<16 = 65536; ask for 70000 → growth caps at 65536, - * then n_tasks is clamped to task_cap. */ + pool_index_ctx_t ctx = {0}; uint32_t requested = 70000; - ray_pool_dispatch_n(&pool, pool_count_fn, &ctx, requested); - - /* task_cap should have grown to MAX_RING_CAP exactly */ + ray_pool_dispatch_n(&pool, pool_index_fn, &ctx, requested); + /* the ring grows to MAX_RING_CAP exactly ... */ TEST_ASSERT_EQ_U(pool.task_cap, 65536u); - /* Calls should equal the clamped count, not the requested one. */ - TEST_ASSERT_EQ_I(atomic_load(&ctx.calls), 65536); - + /* ... and every requested task still runs, once, with its own index */ + TEST_ASSERT_EQ_I(atomic_load(&ctx.calls), (int64_t)requested); + TEST_ASSERT_EQ_I(atomic_load(&ctx.start_max), (int64_t)requested - 1); + TEST_ASSERT_EQ_I(atomic_load(&ctx.start_sum), (int64_t)requested * (requested - 1) / 2); ray_pool_free(&pool); ray_heap_destroy(); PASS(); @@ -1313,7 +1327,27 @@ static test_result_t test_epoll_hup_no_errfn(void) { * Suite definition * -------------------------------------------------------------------------- */ +#if defined(__linux__) || defined(__APPLE__) +static test_result_t test_auto_all_logical_cpus(void) { + const char* current = getenv("RAYFORCE_CORES"); + char* saved = current ? strdup(current) : NULL; + TEST_ASSERT_TRUE(!current || saved); + unsetenv("RAYFORCE_CORES"); + ray_pool_t local; + ray_err_t rc = ray_pool_create(&local, 0); + uint32_t total = rc == RAY_OK ? ray_pool_total_workers(&local) : 0; + if (rc == RAY_OK) ray_pool_free(&local); + if (saved) { setenv("RAYFORCE_CORES", saved, 1); free(saved); } + TEST_ASSERT_EQ_I(rc, RAY_OK); + TEST_ASSERT_EQ_I(total, ray_thread_count()); + PASS(); +} +#endif + const test_entry_t pool_entries[] = { +#if defined(__linux__) || defined(__APPLE__) + { "pool/auto_all_logical_cpus", test_auto_all_logical_cpus, NULL, NULL }, +#endif { "pool/parallel_sum", test_parallel_sum, NULL, NULL }, { "pool/parallel_add", test_parallel_add, NULL, NULL }, { "pool/parallel_group_sum", test_parallel_group_sum, NULL, NULL }, diff --git a/test/test_repl.c b/test/test_repl.c index 9866d73c2..a341cc4fa 100644 --- a/test/test_repl.c +++ b/test/test_repl.c @@ -41,6 +41,7 @@ #define _DEFAULT_SOURCE 1 #include "test.h" +#include "ipc_harness.h" #include #include "app/repl.h" #include "core/profile.h" @@ -906,64 +907,26 @@ static test_result_t test_repl_run_piped_error_with_trace(void) { /* ─── Remote-REPL session ───────────────────────────────────────── */ -/* Helper: read OS-assigned port from a listen socket. */ -static uint16_t get_listen_port(ray_sock_t fd) { - struct sockaddr_in addr; - socklen_t len = sizeof(addr); - if (getsockname(fd, (struct sockaddr*)&addr, &len) < 0) return 0; - return ntohs(addr.sin_port); -} - -/* Server poll thread — same pattern as test_store.c. */ +/* Spin up an in-process IPC server on the shared poll-based harness and + * return its bound port. Returns 0 on success, -1 on failure; caller + * releases via repl_stop_server. */ typedef struct { - ray_ipc_server_t* srv; - ray_vm_t* vm; -} repl_ipc_ctx_t; - -static void repl_server_thread_fn(void* arg) { - repl_ipc_ctx_t* ctx = (repl_ipc_ctx_t*)arg; - __VM = ctx->vm; - while (ctx->srv->running) - ray_ipc_poll(ctx->srv, 10); -} - -/* Spin up an in-process IPC server, return its bound port via *port_out. - * Returns 0 on success, -1 on failure. Caller releases via - * repl_stop_server. */ -typedef struct { - ray_ipc_server_t srv; - ray_vm_t* srv_vm; - repl_ipc_ctx_t ctx; - ray_thread_t tid; - uint16_t port; - bool alive; + ray_test_server_t srv; + uint16_t port; /* mirrors srv.port for the call sites below */ + bool alive; } repl_server_t; static int repl_start_server(repl_server_t* s) { memset(s, 0, sizeof(*s)); - if (ray_ipc_server_init(&s->srv, 0) != RAY_OK) return -1; - s->port = get_listen_port(s->srv.listen_fd); - if (s->port == 0) { ray_ipc_server_destroy(&s->srv); return -1; } - s->srv_vm = (ray_vm_t*)ray_sys_alloc(sizeof(ray_vm_t)); - if (!s->srv_vm) { ray_ipc_server_destroy(&s->srv); return -1; } - ray_vm_init(s->srv_vm, 1); - s->ctx.srv = &s->srv; - s->ctx.vm = s->srv_vm; - if (ray_thread_create(&s->tid, repl_server_thread_fn, &s->ctx) != RAY_OK) { - ray_sys_free(s->srv_vm); - ray_ipc_server_destroy(&s->srv); - return -1; - } + if (ray_test_server_start(&s->srv) != 0) return -1; + s->port = s->srv.port; s->alive = true; return 0; } static void repl_stop_server(repl_server_t* s) { if (!s->alive) return; - s->srv.running = false; - ray_thread_join(s->tid); - ray_ipc_server_destroy(&s->srv); - ray_sys_free(s->srv_vm); + ray_test_server_stop(&s->srv); s->alive = false; } diff --git a/test/test_runtime.c b/test/test_runtime.c index 3c07b9914..8e6094fad 100644 --- a/test/test_runtime.c +++ b/test/test_runtime.c @@ -194,6 +194,46 @@ static test_result_t test_create_with_sym_load_preserves_user_ids(void) { PASS(); } +/* A tree-walked lambda call binds `self` under the interned sym ID of + * "self". That ID must belong to the runtime doing the call: the destroy + * path tears the sym table down, and a fresh runtime that interns names in + * a different order gives "self" a different ID. An ID cached from an + * earlier runtime binds the lambda under a name that is no longer "self" — + * shadowing whatever variable owns it — and `self` in the body no longer + * names the call's own lambda (without the fix, the second runtime below + * fails with a stack-limit error instead of returning 42). + * + * The inner fn is built inside a lambda body, so it captures a closure and + * is never compiled: every call map makes to it takes the tree walker. Two + * runtimes pad the sym table by different amounts before "self" is first + * interned, so "self" gets two different IDs; one process-wide cached ID + * cannot match both, whichever runtime (or an earlier test) filled it. */ +static test_result_t test_lambda_self_sym_per_runtime(void) { + int64_t self_ids[2]; + for (int pass = 0; pass < 2; pass++) { + ray_runtime_t* rt = ray_runtime_create(0, NULL); + TEST_ASSERT_NOT_NULL(rt); + for (int i = 0; i < pass * 7; i++) { + char name[32]; + int n = snprintf(name, sizeof(name), "self_sym_pad_%d", i); + ray_sym_intern(name, (size_t)n); + } + ray_t* r = ray_eval_str( + "((fn [n] (first (map (fn [k] (if (== k 0) n (self (- k 1)))) [3]))) 42)"); + TEST_ASSERT_NOT_NULL(r); + TEST_ASSERT_FALSE(RAY_IS_ERR(r)); + TEST_ASSERT_EQ_I((int)r->type, -RAY_I64); + TEST_ASSERT_EQ_I((int)r->i64, 42); + ray_release(r); + self_ids[pass] = ray_sym_intern("self", 4); + ray_runtime_destroy(rt); + } + /* Guard the premise: with equal IDs a stale cache would go unnoticed. */ + TEST_ASSERT(self_ids[0] != self_ids[1], + "padding must give \"self\" a different ID in each runtime"); + PASS(); +} + /* Sym file whose stat st_size exceeds half of physical RAM must trigger the * pre-flight OOM guard and surface RAY_ERR_OOM through out_sym_err. * We use ftruncate to create a SPARSE file (no backing bytes), sized from @@ -1232,6 +1272,7 @@ const test_entry_t runtime_entries[] = { { "runtime/create_with_sym_plain_variant_absent", test_create_with_sym_plain_variant_absent, NULL, NULL }, { "runtime/create_with_sym_corrupt_file", test_create_with_sym_corrupt_file, NULL, NULL }, { "runtime/create_with_sym_load_preserves_user_ids", test_create_with_sym_load_preserves_user_ids, NULL, NULL }, + { "runtime/lambda_self_sym_per_runtime", test_lambda_self_sym_per_runtime, NULL, NULL }, { "runtime/create_with_sym_oversized_file", test_create_with_sym_oversized_file, NULL, NULL }, { "runtime/oom_sentinel_is_well_formed", test_oom_sentinel_is_well_formed, NULL, NULL }, { "runtime/sock_close_invalid", test_sock_close_invalid, NULL, NULL }, diff --git a/test/test_store.c b/test/test_store.c index e42a7c547..f4ca10561 100644 --- a/test/test_store.c +++ b/test/test_store.c @@ -26,6 +26,7 @@ #endif #include "test.h" +#include "ipc_harness.h" #include #include #include @@ -3655,88 +3656,18 @@ static test_result_t test_ipc_compress_zeros(void) { PASS(); } -/* ---- IPC server lifecycle ----------------------------------------------- */ - -static test_result_t test_ipc_server_lifecycle(void) { - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); /* ephemeral port */ - TEST_ASSERT_EQ_I(err, RAY_OK); - TEST_ASSERT_TRUE(srv.running); - TEST_ASSERT((srv.listen_fd) != (RAY_INVALID_SOCK), "srv.listen_fd != RAY_INVALID_SOCK"); - - /* Verify we can retrieve the OS-assigned port */ - struct sockaddr_in addr; - socklen_t alen = sizeof(addr); - int rc = getsockname(srv.listen_fd, (struct sockaddr*)&addr, &alen); - TEST_ASSERT_EQ_I(rc, 0); - uint16_t port = ntohs(addr.sin_port); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_ipc_server_destroy(&srv); - TEST_ASSERT_FALSE(srv.running); - PASS(); -} - /* ---- IPC sync round-trip ------------------------------------------------ */ -/* Helper: get ephemeral port from listen socket */ -static uint16_t get_listen_port(ray_sock_t fd) { - struct sockaddr_in addr; - socklen_t len = sizeof(addr); - if (getsockname(fd, (struct sockaddr*)&addr, &len) < 0) return 0; - return ntohs(addr.sin_port); -} - -/* Server poll thread context — carries a VM for eval */ -typedef struct { - ray_ipc_server_t *srv; - ray_vm_t *vm; -} ipc_thread_ctx_t; - -static void server_thread_fn(void* arg) { - ipc_thread_ctx_t* ctx = (ipc_thread_ctx_t*)arg; - /* Set up TLS VM so ray_eval_str works in this thread */ - __VM = ctx->vm; - while (ctx->srv->running) - ray_ipc_poll(ctx->srv, 10); -} - -/* Unified IPC handles are poll selector ids resolved in the runtime - * poll, so every client-side ray_ipc_connect below needs one published — - * mirroring what main.c does at startup. */ -static ray_poll_t* ipc_client_poll(void) { - ray_poll_t* p = ray_poll_create(); - if (p) ray_runtime_set_poll(p); - return p; -} -static void ipc_client_poll_done(void) { - ray_poll_t* p = (ray_poll_t*)ray_runtime_get_poll(); - if (p) { ray_runtime_set_poll(NULL); ray_poll_destroy(p); } -} - static test_result_t test_ipc_sync_roundtrip(void) { /* Full runtime needed for ray_eval_str in server thread */ ray_runtime_t* rt = ray_runtime_create(0, NULL); - ipc_client_poll(); + ray_test_client_poll(); TEST_ASSERT_NOT_NULL(rt); - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - /* Create a VM for the server thread */ - ray_vm_t* srv_vm = (ray_vm_t*)ray_sys_alloc(sizeof(ray_vm_t)); - TEST_ASSERT_NOT_NULL(srv_vm); - ray_vm_init(srv_vm, 1); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - - /* Start server poll thread */ - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); /* Client: connect */ int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); @@ -3758,11 +3689,8 @@ static test_result_t test_ipc_sync_roundtrip(void) { ray_ipc_close(h); /* Stop server */ - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); - ipc_client_poll_done(); + ray_test_server_stop(&srv); + ray_test_client_poll_done(); ray_runtime_destroy(rt); PASS(); @@ -3773,22 +3701,12 @@ static test_result_t test_ipc_sync_roundtrip(void) { static test_result_t test_ipc_async_send(void) { /* Full runtime needed for eval on server side */ ray_runtime_t* rt = ray_runtime_create(0, NULL); - ipc_client_poll(); + ray_test_client_poll(); TEST_ASSERT_NOT_NULL(rt); - ray_ipc_server_t srv; - ray_ipc_server_init(&srv, 0); - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = (ray_vm_t*)ray_sys_alloc(sizeof(ray_vm_t)); - TEST_ASSERT_NOT_NULL(srv_vm); - ray_vm_init(srv_vm, 1); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -3803,11 +3721,8 @@ static test_result_t test_ipc_async_send(void) { { struct timespec ts = { .tv_sec = 0, .tv_nsec = 50000000 }; nanosleep(&ts, NULL); } ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); - ipc_client_poll_done(); + ray_test_server_stop(&srv); + ray_test_client_poll_done(); ray_runtime_destroy(rt); PASS(); @@ -3817,23 +3732,13 @@ static test_result_t test_ipc_async_send(void) { static test_result_t test_ipc_auth_success(void) { ray_runtime_t* rt = ray_runtime_create(0, NULL); - ipc_client_poll(); + ray_test_client_poll(); TEST_ASSERT_NOT_NULL(rt); - ray_ipc_server_t srv; - ray_ipc_server_init(&srv, 0); - strcpy(srv.auth_secret, "secret123"); + ray_test_server_t srv; + RAY_TEST_SERVER_START_OPTS(srv, "secret123", false); + uint16_t port = srv.port; - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = (ray_vm_t*)ray_sys_alloc(sizeof(ray_vm_t)); - TEST_ASSERT_NOT_NULL(srv_vm); - ray_vm_init(srv_vm, 1); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); int64_t h = ray_ipc_connect("127.0.0.1", port, "admin", "secret123", 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -3848,11 +3753,8 @@ static test_result_t test_ipc_auth_success(void) { ray_release(result); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); - ipc_client_poll_done(); + ray_test_server_stop(&srv); + ray_test_client_poll_done(); ray_runtime_destroy(rt); PASS(); @@ -3862,32 +3764,19 @@ static test_result_t test_ipc_auth_success(void) { static test_result_t test_ipc_auth_reject(void) { ray_runtime_t* rt = ray_runtime_create(0, NULL); - ipc_client_poll(); + ray_test_client_poll(); TEST_ASSERT_NOT_NULL(rt); - ray_ipc_server_t srv; - ray_ipc_server_init(&srv, 0); - strcpy(srv.auth_secret, "secret123"); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); + ray_test_server_t srv; + RAY_TEST_SERVER_START_OPTS(srv, "secret123", false); + uint16_t port = srv.port; - ray_vm_t* srv_vm = (ray_vm_t*)ray_sys_alloc(sizeof(ray_vm_t)); - TEST_ASSERT_NOT_NULL(srv_vm); - ray_vm_init(srv_vm, 1); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); int64_t h = ray_ipc_connect("127.0.0.1", port, "admin", "wrong", 0); TEST_ASSERT_EQ_I(h, -3); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); - ipc_client_poll_done(); + ray_test_server_stop(&srv); + ray_test_client_poll_done(); ray_runtime_destroy(rt); PASS(); @@ -3897,32 +3786,19 @@ static test_result_t test_ipc_auth_reject(void) { static test_result_t test_ipc_auth_no_creds(void) { ray_runtime_t* rt = ray_runtime_create(0, NULL); - ipc_client_poll(); + ray_test_client_poll(); TEST_ASSERT_NOT_NULL(rt); - ray_ipc_server_t srv; - ray_ipc_server_init(&srv, 0); - strcpy(srv.auth_secret, "secret123"); - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); + ray_test_server_t srv; + RAY_TEST_SERVER_START_OPTS(srv, "secret123", false); + uint16_t port = srv.port; - ray_vm_t* srv_vm = (ray_vm_t*)ray_sys_alloc(sizeof(ray_vm_t)); - TEST_ASSERT_NOT_NULL(srv_vm); - ray_vm_init(srv_vm, 1); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); int64_t h = ray_ipc_connect("127.0.0.1", port, NULL, NULL, 0); TEST_ASSERT_EQ_I(h, -2); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); - ipc_client_poll_done(); + ray_test_server_stop(&srv); + ray_test_client_poll_done(); ray_runtime_destroy(rt); PASS(); @@ -3932,24 +3808,13 @@ static test_result_t test_ipc_auth_no_creds(void) { static test_result_t test_ipc_restricted(void) { ray_runtime_t* rt = ray_runtime_create(0, NULL); - ipc_client_poll(); + ray_test_client_poll(); TEST_ASSERT_NOT_NULL(rt); - ray_ipc_server_t srv; - ray_ipc_server_init(&srv, 0); - strcpy(srv.auth_secret, "secret123"); - srv.restricted = true; - - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); + ray_test_server_t srv; + RAY_TEST_SERVER_START_OPTS(srv, "secret123", true); + uint16_t port = srv.port; - ray_vm_t* srv_vm = (ray_vm_t*)ray_sys_alloc(sizeof(ray_vm_t)); - TEST_ASSERT_NOT_NULL(srv_vm); - ray_vm_init(srv_vm, 1); - - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); int64_t h = ray_ipc_connect("127.0.0.1", port, "admin", "secret123", 0); TEST_ASSERT((h) >= (0), "h >= 0"); @@ -4012,11 +3877,8 @@ static test_result_t test_ipc_restricted(void) { ray_release(r6); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); - ipc_client_poll_done(); + ray_test_server_stop(&srv); + ray_test_client_poll_done(); ray_runtime_destroy(rt); PASS(); @@ -4030,22 +3892,13 @@ static test_result_t test_ipc_handshake_version_mismatch(void) { * to any framed payload. This is the defense-in-depth layer that * protects an old peer from ever seeing a new-format message. */ ray_runtime_t* rt = ray_runtime_create(0, NULL); - ipc_client_poll(); + ray_test_client_poll(); TEST_ASSERT_NOT_NULL(rt); - ray_ipc_server_t srv; - ray_err_t err = ray_ipc_server_init(&srv, 0); - TEST_ASSERT_EQ_I(err, RAY_OK); - uint16_t port = get_listen_port(srv.listen_fd); - TEST_ASSERT((port) > (0), "port > 0"); - - ray_vm_t* srv_vm = (ray_vm_t*)ray_sys_alloc(sizeof(ray_vm_t)); - TEST_ASSERT_NOT_NULL(srv_vm); - ray_vm_init(srv_vm, 1); + ray_test_server_t srv; + RAY_TEST_SERVER_START(srv); + uint16_t port = srv.port; - ipc_thread_ctx_t ctx = { .srv = &srv, .vm = srv_vm }; - ray_thread_t tid; - ray_thread_create(&tid, server_thread_fn, &ctx); /* Connect raw socket and send a version-byte that doesn't match. */ ray_sock_t s = ray_sock_connect("127.0.0.1", port, 2000); @@ -4074,11 +3927,8 @@ static test_result_t test_ipc_handshake_version_mismatch(void) { TEST_ASSERT((h) >= (0), "h >= 0"); ray_ipc_close(h); - srv.running = false; - ray_thread_join(tid); - ray_ipc_server_destroy(&srv); - ray_sys_free(srv_vm); - ipc_client_poll_done(); + ray_test_server_stop(&srv); + ray_test_client_poll_done(); ray_runtime_destroy(rt); PASS(); } @@ -5615,7 +5465,6 @@ const test_entry_t store_entries[] = { { "store/ipc/compress_rt", test_ipc_compress_rt, NULL, NULL }, { "store/ipc/compress_threshold", test_ipc_compress_threshold, NULL, NULL }, { "store/ipc/compress_zeros", test_ipc_compress_zeros, NULL, NULL }, - { "store/ipc/server_lifecycle", test_ipc_server_lifecycle, NULL, NULL }, { "store/ipc/sync_roundtrip", test_ipc_sync_roundtrip, NULL, NULL }, { "store/ipc/async_send", test_ipc_async_send, NULL, NULL }, { "store/ipc/auth_success", test_ipc_auth_success, NULL, NULL }, diff --git a/test/test_sym.c b/test/test_sym.c index b840a9faa..49a7fcc75 100644 --- a/test/test_sym.c +++ b/test/test_sym.c @@ -30,6 +30,8 @@ #include "store/col.h" #include "lang/internal.h" #include "ops/hash.h" +#include "store/serde.h" +#include "vec/vec.h" #include "ops/glob.h" #include #include @@ -2623,6 +2625,167 @@ static test_result_t test_glob_match_class_edge(void) { /* ---- Suite definition -------------------------------------------------- */ +/* ---- ray_sym_intern_batch (#542 fix 1) ---------------------------------- */ + +/* A batch intern must be indistinguishable from the same sequence of + * ray_sym_intern calls: same ids, same dotted-segment caching, same + * treatment of "" (the SYM null, id 0). */ +static test_result_t test_sym_intern_batch_matches_intern(void) { + static const char* names[] = { "batch_a", "batch_b", "batch_a", ".ns.batch_c", "" }; + static const size_t lens[] = { 7, 7, 7, 11, 0 }; + const char* strs[5]; + uint32_t hashes[5]; + int64_t got[5]; + + for (int i = 0; i < 5; i++) { + strs[i] = names[i]; + hashes[i] = (uint32_t)ray_hash_bytes(names[i], lens[i]); + } + + TEST_ASSERT_EQ_I(ray_sym_intern_batch(hashes, strs, lens, 5, got), 0); + + /* Re-interning through the single-cell path must return the same ids. */ + for (int i = 0; i < 5; i++) + TEST_ASSERT_EQ_I(got[i], ray_sym_intern(names[i], lens[i])); + + TEST_ASSERT_EQ_I(got[0], got[2]); /* duplicate -> same id */ + TEST_ASSERT_EQ_I(got[4], 0); /* "" is the SYM null */ + PASS(); +} + +/* Dotted names interned in a batch must get their segments cached, exactly + * as ray_sym_intern does — env lookup depends on it. */ +static test_result_t test_sym_intern_batch_caches_segments(void) { + const char* strs[1] = { ".ns.deep" }; + size_t lens[1] = { 8 }; + uint32_t hashes[1] = { (uint32_t)ray_hash_bytes(".ns.deep", 8) }; + int64_t got[1]; + + TEST_ASSERT_EQ_I(ray_sym_intern_batch(hashes, strs, lens, 1, got), 0); + TEST_ASSERT_TRUE(ray_sym_is_dotted(got[0])); + + const int64_t* segs = NULL; + TEST_ASSERT_EQ_I(ray_sym_segs(got[0], &segs), 2); + TEST_ASSERT_NOT_NULL(segs); + TEST_ASSERT_EQ_I(segs[0], ray_sym_intern(".ns", 3)); + TEST_ASSERT_EQ_I(segs[1], ray_sym_intern("deep", 4)); + PASS(); +} + +static test_result_t test_sym_intern_batch_empty(void) { + int64_t out[1] = { -7 }; + TEST_ASSERT_EQ_I(ray_sym_intern_batch(NULL, NULL, NULL, 0, out), 0); + TEST_ASSERT_EQ_I(out[0], -7); /* untouched */ + PASS(); +} + +/* ---- SYM vector deserialization (#542 fix 1, decode side) --------------- */ + +/* Build a RAY_SYM_W64 vector from names, round-trip it through ser/de, and + * require the decoded ids to be identical to the originals. */ +static test_result_t sym_serde_roundtrip_names(const char** names, int64_t n) { + ray_t* v = ray_sym_vec_new(RAY_SYM_W64, n); + TEST_ASSERT_FALSE(RAY_IS_ERR(v)); + v->len = n; + int64_t* d = (int64_t*)ray_data(v); + for (int64_t i = 0; i < n; i++) + d[i] = ray_sym_intern(names[i], strlen(names[i])); + + ray_t* bytes = ray_ser(v); + TEST_ASSERT_FALSE(RAY_IS_ERR(bytes)); + ray_t* back = ray_de(bytes); + TEST_ASSERT_FALSE(RAY_IS_ERR(back)); + TEST_ASSERT_EQ_I(back->type, RAY_SYM); + TEST_ASSERT_EQ_I(back->len, n); + + const int64_t* b = (const int64_t*)ray_data(back); + for (int64_t i = 0; i < n; i++) { + TEST_ASSERT_EQ_I(b[i], d[i]); + /* Borrowed atom — valid for the sym table's lifetime, not released. */ + ray_t* s = ray_sym_str(b[i]); + TEST_ASSERT_NOT_NULL(s); + TEST_ASSERT_EQ_U(ray_str_len(s), strlen(names[i])); + if (strlen(names[i])) + TEST_ASSERT_MEM_EQ(strlen(names[i]), ray_str_ptr(s), names[i]); + } + + ray_release(back); + ray_release(bytes); + ray_release(v); + PASS(); +} + +/* Heavy duplication is the tickerplant case: a 'side' column of two + * distinct values repeated across the batch. */ +static test_result_t test_sym_serde_vec_duplicates(void) { + static const char* names[12] = { + "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A" + }; + return sym_serde_roundtrip_names(names, 12); +} + +/* Duplicates mixed with "" (SYM null) and dotted names. */ +static test_result_t test_sym_serde_vec_mixed(void) { + static const char* names[8] = { + "bfu", "", ".ns.deep", "bfu", "", "bfu", ".ns.deep", "zzz" + }; + return sym_serde_roundtrip_names(names, 8); +} + +/* All-distinct, past the dedupe table's initial capacity, so the decode's + * open-addressing table has to grow/probe heavily. */ +static test_result_t test_sym_serde_vec_high_cardinality(void) { + enum { N = 500 }; + static char bufs[N][24]; + const char* names[N]; + for (int i = 0; i < N; i++) { + snprintf(bufs[i], sizeof(bufs[i]), "BFU:BTCUSDT%d", i); + names[i] = bufs[i]; + } + return sym_serde_roundtrip_names(names, N); +} + +/* A single-element vector exercises the smallest dedupe table. */ +static test_result_t test_sym_serde_vec_single(void) { + static const char* names[1] = { "solo" }; + return sym_serde_roundtrip_names(names, 1); +} + +static test_result_t test_sym_serde_vec_empty(void) { + ray_t* v = ray_sym_vec_new(RAY_SYM_W64, 0); + TEST_ASSERT_FALSE(RAY_IS_ERR(v)); + v->len = 0; + ray_t* bytes = ray_ser(v); + TEST_ASSERT_FALSE(RAY_IS_ERR(bytes)); + ray_t* back = ray_de(bytes); + TEST_ASSERT_FALSE(RAY_IS_ERR(back)); + TEST_ASSERT_EQ_I(back->type, RAY_SYM); + TEST_ASSERT_EQ_I(back->len, 0); + ray_release(back); + ray_release(bytes); + ray_release(v); + PASS(); +} + +/* A SYM vector whose last cell has no NUL terminator: the decoder must + * report a domain error rather than run off the end of the buffer. Built + * by hand — ray_de rejects a truncated envelope before the vector body. */ +static test_result_t test_sym_serde_vec_unterminated(void) { + /* [type][attrs][int64 count=2]["aa\0" "bb" with no trailing NUL] */ + uint8_t wire[1 + 1 + 8 + 5]; + wire[0] = (uint8_t)RAY_SYM; + wire[1] = 0; + int64_t count = 2; + memcpy(wire + 2, &count, 8); + memcpy(wire + 10, "aa\0bb", 5); + + int64_t len = (int64_t)sizeof(wire); + ray_t* back = ray_de_raw(wire, &len); + TEST_ASSERT_TRUE(RAY_IS_ERR(back)); + ray_error_free(back); + PASS(); +} + const test_entry_t sym_entries[] = { { "sym/init_destroy", test_sym_init_destroy, sym_setup, sym_teardown }, { "sym/intern_basic", test_sym_intern_basic, sym_setup, sym_teardown }, @@ -2737,7 +2900,16 @@ const test_entry_t sym_entries[] = { { "sym/glob/match_ci_class_branches", test_glob_match_ci_class_branches, sym_setup, sym_teardown }, { "sym/glob/match_class_edge", test_glob_match_class_edge, sym_setup, sym_teardown }, + /* ray_sym_intern_batch + SYM vector decode (#542) */ + { "sym/intern_batch/matches_intern", test_sym_intern_batch_matches_intern, sym_setup, sym_teardown }, + { "sym/intern_batch/caches_segments", test_sym_intern_batch_caches_segments, sym_setup, sym_teardown }, + { "sym/intern_batch/empty", test_sym_intern_batch_empty, sym_setup, sym_teardown }, + { "sym/serde/vec_duplicates", test_sym_serde_vec_duplicates, sym_setup, sym_teardown }, + { "sym/serde/vec_mixed", test_sym_serde_vec_mixed, sym_setup, sym_teardown }, + { "sym/serde/vec_high_cardinality", test_sym_serde_vec_high_cardinality, sym_setup, sym_teardown }, + { "sym/serde/vec_single", test_sym_serde_vec_single, sym_setup, sym_teardown }, + { "sym/serde/vec_empty", test_sym_serde_vec_empty, sym_setup, sym_teardown }, + { "sym/serde/vec_unterminated", test_sym_serde_vec_unterminated, sym_setup, sym_teardown }, + { NULL, NULL, NULL, NULL }, }; - - diff --git a/test/test_text_null.c b/test/test_text_null.c index 53771e86a..9285bebda 100644 --- a/test/test_text_null.c +++ b/test/test_text_null.c @@ -184,6 +184,11 @@ static test_result_t test_sym_hash_routes(void) { PASS(); } +/* Fused admission for SYM columns ignores the HAS_NULLS flag either way: + * a comparison against a constant compiles whether or not id 0 is present + * (issue #533: raw-id compares already give the null-aware answers), while + * a consumer that reads the lane as a value is admitted only when the + * payload itself is null-free. */ static test_result_t test_sym_expr_admission(void) { for (int nullable = 0; nullable < 2; nullable++) { for (int flagged = 0; flagged < 2; flagged++) { @@ -195,9 +200,11 @@ static test_result_t test_sym_expr_admission(void) { tbl = ray_table_add_col(tbl, ray_sym_intern("k", 1), v); ray_release(v); ray_graph_t* g = ray_graph_new(tbl); - ray_op_t* pred = ray_eq(g, ray_scan(g, "k"), ray_const_str(g, "beta", 4)); + ray_op_t* cmp = ray_eq(g, ray_scan(g, "k"), ray_const_str(g, "beta", 4)); + ray_op_t* val = ray_isnull(g, ray_scan(g, "k")); ray_expr_t expr; - TEST_ASSERT_EQ_I(expr_compile(g, tbl, pred, &expr), !nullable); + TEST_ASSERT_EQ_I(expr_compile(g, tbl, cmp, &expr), 1); + TEST_ASSERT_EQ_I(expr_compile(g, tbl, val, &expr), !nullable); ray_graph_free(g); ray_release(tbl); } diff --git a/test/test_vec.c b/test/test_vec.c index e331a3b5c..458adf7c4 100644 --- a/test/test_vec.c +++ b/test/test_vec.c @@ -627,6 +627,72 @@ static test_result_t test_sym_vec_widths(void) { PASS(); } +/* ---- has_nulls: text types are proven null-free by payload scan -------- */ + +/* SYM/STR always "may have nulls", so ray_vec_has_nulls must inspect the + * payload. Pin the contract at every SYM width, on STR, and through a + * slice, with lengths that are not multiples of any vector width so the + * scan's tail handling is exercised: a single id-0 cell anywhere flips + * the answer. */ +static test_result_t test_vec_has_nulls_text(void) { + static const uint8_t widths[] = { RAY_SYM_W8, RAY_SYM_W16, RAY_SYM_W32, RAY_SYM_W64 }; + const int64_t n = 37; + for (size_t wi = 0; wi < sizeof(widths); wi++) { + ray_t* v = ray_sym_vec_new(widths[wi], n); + TEST_ASSERT_FALSE(RAY_IS_ERR(v)); + for (int64_t i = 0; i < n; i++) { + uint64_t id = (uint64_t)(i + 1); + uint8_t b8 = (uint8_t)id; uint16_t b16 = (uint16_t)id; + uint32_t b32 = (uint32_t)id; int64_t b64 = (int64_t)id; + const void* src = widths[wi] == RAY_SYM_W8 ? (const void*)&b8 : + widths[wi] == RAY_SYM_W16 ? (const void*)&b16 : + widths[wi] == RAY_SYM_W32 ? (const void*)&b32 : + (const void*)&b64; + v = ray_vec_append(v, src); + TEST_ASSERT_FALSE(RAY_IS_ERR(v)); + } + TEST_ASSERT_EQ_I(v->len, n); + TEST_ASSERT_TRUE(ray_vec_may_have_nulls(v)); + TEST_ASSERT_FALSE(ray_vec_has_nulls(v)); + + /* null in the unaligned tail */ + ray_write_sym(ray_data(v), n - 1, 0, RAY_SYM, v->attrs); + TEST_ASSERT_TRUE(ray_vec_has_nulls(v)); + ray_write_sym(ray_data(v), n - 1, (uint64_t)n, RAY_SYM, v->attrs); + TEST_ASSERT_FALSE(ray_vec_has_nulls(v)); + + /* null in the body; a slice that excludes it stays null-free */ + ray_write_sym(ray_data(v), 5, 0, RAY_SYM, v->attrs); + TEST_ASSERT_TRUE(ray_vec_has_nulls(v)); + ray_t* clean = ray_vec_slice(v, 6, n - 6); + TEST_ASSERT_FALSE(RAY_IS_ERR(clean)); + TEST_ASSERT_FALSE(ray_vec_has_nulls(clean)); + ray_t* dirty = ray_vec_slice(v, 3, 4); + TEST_ASSERT_FALSE(RAY_IS_ERR(dirty)); + TEST_ASSERT_TRUE(ray_vec_has_nulls(dirty)); + ray_release(clean); + ray_release(dirty); + ray_release(v); + } + + /* STR: canonical "" is the null */ + ray_t* s = ray_vec_new(RAY_STR, 3); + s = ray_str_vec_append(s, "a", 1); + s = ray_str_vec_append(s, "a long pooled string value", 26); + s = ray_str_vec_append(s, "bb", 2); + TEST_ASSERT_FALSE(RAY_IS_ERR(s)); + TEST_ASSERT_FALSE(ray_vec_has_nulls(s)); + ray_vec_set_null(s, 2, true); + TEST_ASSERT_TRUE(ray_vec_has_nulls(s)); + ray_release(s); + + /* empty text vec is trivially null-free */ + ray_t* e = ray_sym_vec_new(RAY_SYM_W16, 0); + TEST_ASSERT_FALSE(ray_vec_has_nulls(e)); + ray_release(e); + PASS(); +} + /* ---- slice_of_slice (parent_offset accumulation) ----------------------- */ static test_result_t test_vec_slice_of_slice(void) { @@ -2315,6 +2381,7 @@ const test_entry_t vec_entries[] = { { "vec/copy_nulls_paths", test_vec_copy_nulls_paths, vec_setup, vec_teardown }, { "vec/get_paths", test_vec_get_paths, vec_setup, vec_teardown }, { "vec/sym_vec_w64", test_sym_vec_w64, vec_setup, vec_teardown }, + { "vec/has_nulls_text", test_vec_has_nulls_text, vec_setup, vec_teardown }, { "vec/concat_sym_same_width", test_vec_concat_sym_same_width, vec_setup, vec_teardown }, { "vec/insert_many_atom", test_vec_insert_many_atom, vec_setup, vec_teardown }, { "vec/insert_many_guards", test_vec_insert_many_guards, vec_setup, vec_teardown },