Skip to content

Instantly share code, notes, and snippets.

@dwsmith1983
Created September 2, 2026 14:39
Show Gist options
  • Select an option

  • Save dwsmith1983/e46e22c1c594b4f5120515c773f2b3ef to your computer and use it in GitHub Desktop.

Select an option

Save dwsmith1983/e46e22c1c594b4f5120515c773f2b3ef to your computer and use it in GitHub Desktop.
apache/datafusion-comet PR 5612 regex PatternCache benchmarks: harness, results, sort-path repro, mechanism micro-bench

PR #5612 benchmark harness: per-expression regex PatternCache (datafusion-comet)

Note: gists have no directories, so files referenced below as harness/src/... and results/... appear here under their bare filenames.

Standalone benchmarks for apache/datafusion-comet PR #5612, comparing:

  • BASE: commit 8729f6e6a (main at the time, one regex compile per batch invocation)
  • HEAD: commit b4d915236 (branch perf/compile-user-regex-once, one-slot PatternCache per planned expression)

Machine and toolchain

  • Apple M5, 10 cores (4 performance + 6 efficiency), macOS (Darwin 25.6.0)
  • rustc 1.98.0 (88d9e12ae 2026-08-18), cargo 1.98.0
  • All builds --release (opt-level 3, thin LTO, codegen-units 1)
  • Dependency versions match the repo workspace pins: datafusion 54.1.0, arrow 58.4.0, regex 1.13.1 (resolved from the workspace's regex = "1.12.3"), regex-automata 0.4.16

How it builds

harness/Cargo.toml has a path dependency on the repo's native/spark-expr crate, so the identical harness source builds against whichever commit is checked out in the repo. The harness uses only symbols that exist in both commits: UDFs are constructed through create_comet_physical_fun (the serde entry point) and evaluated through ScalarUDF::invoke_with_args, with the regex pattern supplied as a scalar argument on every invoke, exactly as the engine does. Build each commit into its own target dir so the two builds never clobber each other:

cd harness
# with the repo checked out at HEAD (b4d915236):
cargo build --release --target-dir ../target-head
# with the repo checked out at BASE (git checkout 8729f6e6a):
cargo build --release --target-dir ../target-base

Adjust the absolute path in harness/Cargo.toml to your checkout location.

Binaries

  1. bench5612 (src/main.rs): the main matrix. Functions {regexp_extract, regexp_extract_all, split} x sharing {shared single UDF instance across threads, one instance per worker} x workers {1,2,4,8} x batch rows {512, 8192} x pattern regime {warm, alternating}. Also measures allocations per batch (counting global allocator), cold first-invoke cost, and FNV-1a hashes of all outputs for a BASE vs HEAD equality check. Run: bench5612 <label> <out_dir>.
  2. sort_repro (src/bin/sort_repro.rs): the sort-key sharing scenario. Builds the exact plan shape Comet produces (PhysicalSortExpr wrapping a scalar function expression, passed to SortExec with no fetch, single partition memory source of 128 x 8192-row batches, about 21 MB so the reservation is far past the 1 MiB sort_in_place_threshold_bytes default). DataFusion 54.1.0 ExternalSorter::in_mem_sort_stream then sorts every buffered batch in its own tokio task via spawn_buffered (datafusion-physical-plan-54.1.0/src/sorts/sort.rs lines 586-646), all tasks sharing the same UDF instance. A tracking shim around one comet UDF instance records total invokes and max concurrent in-flight invocations. Run: sort_repro <label> <function> <workers> <iters> on a multithread tokio runtime with <workers> worker threads.
  3. shared_clone (src/bin/shared_clone.rs): commit-independent micro benchmark that isolates the mechanism of the shared-instance slowdown using the regex crate directly. Modes price separately: fresh compile per invoke (BASE), mutex slot + clone from a shared source (HEAD shared), mutex slot + clone from a per-thread source (HEAD per worker), plain clones from shared vs private sources, hoisted clones, direct shared use, and clone-only loops. Run: shared_clone [invokes_per_thread].

Result files

  • results/{base,head}_cells.csv and {base2,head2}_cells.csv: two full replicates of the main matrix (throughput, mean/p50/p99 per-batch latency, allocations per batch)
  • results/*_cold.csv: cold first-invoke cost, 30 fresh instances each
  • results/*_verify.csv: output hashes; identical across all four runs (BASE and HEAD produce byte-identical results)
  • results/sort_head_w1.csv, sort_head_w8.csv, sort_base_w1.csv, sort_base_w8.csv: first sort_repro runs (regexp_extract_all only, before the function column was added)
  • results/sort_{base,head}_all.csv: sort_repro for both regexp_extract_all and regexp_extract at 1 and 8 workers
  • results/sort_extract_w1_rep2.csv, sort_extractall_w8_rep2.csv: interleaved BASE/HEAD confirmation runs
  • results/shared_clone_micro.csv: mechanism micro benchmark
  • analyze.py: merges the main-matrix CSVs into markdown tables and checks the output hashes

Key findings

  1. Warm patterns (the realistic case; Comet plans regexp patterns as literals, one expression instance per Spark task thread): HEAD removes a full regex compile per batch. Small batches benefit most (regexp_extract, 512 rows, 8 per-worker threads: 5.8 to 62.8 Mrows/s). Allocations per batch drop up to 92 percent.
  2. Alternating patterns on one instance (one-slot cache worst case, 1 worker): within 2 percent of BASE for all three functions.
  3. Concurrent same-instance evaluation does happen on the SortExec path (max in-flight 9 at 8 runtime workers, 2 at 1 worker because the merge evaluates on the driver thread concurrently with one spawned sort task).
  4. On that sort path, HEAD regresses vs BASE for regexp_extract_all as the sort key (about 1.45x wall at 1 worker, about 2.2x at 8 workers), while regexp_extract as the sort key shows parity to a small HEAD win.
  5. Mechanism (shared_clone_micro.csv): not the PatternCache mutex and not the per-invoke Regex clone. regex-automata 0.4.16 impl Clone for Regex (src/meta/regex.rs lines 1916-1926) shares the compiled program via Arc but creates a fresh private scratch pool, so scratch state is never shared. The contention is per row: Regex::captures_iter calls create_captures (meta/regex.rs line 657), which is Captures::all(self.group_info().clone()) (line 1571), an Arc::clone of the program-owned GroupInfo, plus one more Captures clone per match (CapturesMatches::next). When many threads run captures_iter over clones of ONE compiled program, they all hammer the refcount cache line of that single GroupInfoInner allocation. In the micro benchmark this caps every shared-program mode at about 6-15 Mrows/s regardless of thread count (2 threads drop below 1 thread total), while per-thread-program modes scale to 60-80 Mrows/s. regexp_extract is unaffected because it reuses one CaptureLocations across rows (captures_read, native/spark-expr/src/string_funcs/regexp_extract.rs lines 94-107); split is unaffected because find_iter creates no Captures. BASE never shares a program across threads (it compiles per invocation), which is why BASE shared and per-worker numbers are identical.

Fix verification

After the analysis above, regexp_extract_all was changed on the PR branch (commit f35bc97fa) to drive iteration with find_iter and resolve capture groups through captures_read_at into one CaptureLocations reused for the whole batch, in both extract_all_array and extract_one. That removes the per-row create_captures and the per-match Captures clone, and with them both the shared GroupInfo Arc refcount traffic and the per-match heap allocations.

Result files: results/sort_fix.csv (labels base3 and headfix, interleaved runs), results/{headfix,base3}_{cells,cold,verify}.csv.

Sort-key scenario (median wall seconds, regexp_extract_all as the SortExec key):

workers BASE HEAD before fix HEAD with fix
1 0.171 0.251 (1.45x slower) 0.166 (3 percent faster than BASE)
8 0.130 0.287 (2.2x slower) 0.120 (8 percent faster than BASE)

Main-matrix cells that regressed (regexp_extract_all, warm, 8192 rows, Mrows/s, BASE3 / HEAD before fix / HEAD with fix):

sharing w=1 w=2 w=4 w=8
shared 13.1 / 14.9 / 15.3 13.0 / 9.4 / 27.8 10.3 / 9.5 / 52.7 7.1 / 6.3 / 72.4
perworker 13.1 / 14.9 / 15.4 13.0 / 13.9 / 27.4 10.3 / 10.8 / 53.3 7.1 / 7.2 / 74.2

The fix does more than remove the contention: eliminating the per-match allocations lets the function scale nearly linearly to 8 workers in BOTH sharing modes (previously even the uncontended per-worker mode was allocator-bound at about 7 Mrows/s). Single thread: +17 percent vs BASE at 8192 rows (+3 percent vs unfixed HEAD); at 512 rows +89 percent vs BASE but about 12 percent below unfixed HEAD, the cost of rerunning the capture engine at each match start. Alternating-pattern worst case stays within 2 percent of BASE. Output hashes for all functions, batch sizes, and regimes remain byte-identical across BASE, both HEAD builds, and the fixed build (0 mismatches in 12 hash cells x 4 runs).

#!/usr/bin/env python3
"""Merge BASE/HEAD CSVs from the PR #5612 harness and emit markdown tables."""
import csv
import os
import sys
D = os.path.join(os.path.dirname(os.path.abspath(__file__)), "results")
def load(path):
with open(path) as f:
return list(csv.DictReader(f))
def key(r):
return (r["function"], r["sharing"], int(r["workers"]), int(r["batch_rows"]), r["regime"])
def main():
base = {key(r): r for r in load(f"{D}/base_cells.csv")}
head = {key(r): r for r in load(f"{D}/head_cells.csv")}
assert set(base) == set(head), "cell mismatch"
out = []
out.append("| function | sharing | workers | batch | regime | BASE Mrows/s | HEAD Mrows/s | thrpt Δ | BASE mean/p99 us | HEAD mean/p99 us | mean Δ |")
out.append("|---|---|---:|---:|---|---:|---:|---:|---:|---:|---:|")
for k in sorted(base):
b, h = base[k], head[k]
bt, ht = float(b["rows_per_s"]) / 1e6, float(h["rows_per_s"]) / 1e6
bm, hm = float(b["mean_us"]), float(h["mean_us"])
out.append(
f"| {k[0]} | {k[1]} | {k[2]} | {k[3]} | {k[4]} "
f"| {bt:.2f} | {ht:.2f} | {100*(ht-bt)/bt:+.1f}% "
f"| {bm:.0f} / {float(b['p99_us']):.0f} | {hm:.0f} / {float(h['p99_us']):.0f} "
f"| {100*(hm-bm)/bm:+.1f}% |"
)
print("\n".join(out))
print("\n### Allocations per batch (count / MB)\n")
print("| function | sharing | workers | batch | regime | BASE allocs | HEAD allocs | Δ | BASE MB | HEAD MB |")
print("|---|---|---:|---:|---|---:|---:|---:|---:|---:|")
for k in sorted(base):
b, h = base[k], head[k]
ba, ha = float(b["allocs_per_batch"]), float(h["allocs_per_batch"])
print(
f"| {k[0]} | {k[1]} | {k[2]} | {k[3]} | {k[4]} | {ba:.0f} | {ha:.0f} "
f"| {100*(ha-ba)/ba:+.1f}% | {float(b['bytes_per_batch'])/1e6:.3f} | {float(h['bytes_per_batch'])/1e6:.3f} |"
)
print("\n### Cold first-invoke (8192 rows, mean of 30 fresh instances)\n")
bc = {r["function"]: r for r in load(f"{D}/base_cold.csv")}
hc = {r["function"]: r for r in load(f"{D}/head_cold.csv")}
print("| function | BASE 1st us | HEAD 1st us | BASE 2nd us | HEAD 2nd us |")
print("|---|---:|---:|---:|---:|")
for fn in bc:
print(
f"| {fn} | {float(bc[fn]['first_us_mean']):.0f} | {float(hc[fn]['first_us_mean']):.0f} "
f"| {float(bc[fn]['second_us_mean']):.0f} | {float(hc[fn]['second_us_mean']):.0f} |"
)
bv = {(r["function"], r["batch_rows"], r["regime"]): r["hash"] for r in load(f"{D}/base_verify.csv")}
hv = {(r["function"], r["batch_rows"], r["regime"]): r["hash"] for r in load(f"{D}/head_verify.csv")}
bad = [k for k in bv if bv[k] != hv.get(k)]
print(f"\nCorrectness: {len(bv)} output-hash cells compared; mismatches: {len(bad)}")
if bad:
print("MISMATCHES:", bad)
sys.exit(1)
if __name__ == "__main__":
main()
label function sharing workers batch_rows regime batches rows_total wall_s rows_per_s mean_us p50_us p99_us allocs_per_batch bytes_per_batch
base2 regexp_extract shared 1 512 warm 8192 4194304 0.5901 7107346 71.96 71.71 81.54 988 738598
base2 regexp_extract shared 1 512 alternating 8192 4194304 0.4065 10317503 49.53 65.88 78.50 654 389052
base2 regexp_extract shared 2 512 warm 8192 4194304 0.4073 10298309 99.30 98.71 110.58 988 738598
base2 regexp_extract shared 2 512 alternating 8192 4194304 0.3346 12534580 58.64 65.71 111.67 654 389052
base2 regexp_extract shared 4 512 warm 8192 4194304 0.4186 10020086 203.56 204.29 238.29 988 738598
base2 regexp_extract shared 4 512 alternating 8192 4194304 0.2719 15427164 99.89 93.88 207.25 654 389052
base2 regexp_extract shared 8 512 warm 8192 4194304 0.7260 5777496 704.86 549.54 1106.92 988 738597
base2 regexp_extract shared 8 512 alternating 8192 4194304 0.3837 10932433 303.37 207.17 1046.25 654 389092
base2 regexp_extract perworker 1 512 warm 8192 4194304 0.5904 7103841 72.00 71.67 81.33 988 738598
base2 regexp_extract perworker 1 512 alternating 8192 4194304 0.4062 10325742 49.49 65.75 78.75 654 389052
base2 regexp_extract perworker 2 512 warm 8192 4194304 0.4075 10292251 99.37 98.42 115.25 988 738598
base2 regexp_extract perworker 2 512 alternating 8192 4194304 0.3330 12594874 58.46 65.54 110.54 654 389052
base2 regexp_extract perworker 4 512 warm 8192 4194304 0.4188 10015792 203.95 203.67 245.50 988 738598
base2 regexp_extract perworker 4 512 alternating 8192 4194304 0.2724 15397370 99.92 93.33 211.67 654 389052
base2 regexp_extract perworker 8 512 warm 8192 4194304 0.7257 5779946 702.27 548.33 1106.46 988 738637
base2 regexp_extract perworker 8 512 alternating 8192 4194304 0.3836 10934525 304.06 207.79 1044.88 654 389092
base2 regexp_extract shared 1 8192 warm 512 4194304 0.2007 20896059 391.85 385.25 436.12 988 834115
base2 regexp_extract shared 1 8192 alternating 512 4194304 0.1729 24261127 337.46 373.33 423.42 654 484570
base2 regexp_extract shared 2 8192 warm 512 4194304 0.1070 39185980 414.61 415.46 475.21 988 834115
base2 regexp_extract shared 2 8192 alternating 512 4194304 0.1048 40035355 349.73 371.58 475.33 654 484570
base2 regexp_extract shared 4 8192 warm 512 4194304 0.0551 76183947 426.17 422.25 512.96 988 834115
base2 regexp_extract shared 4 8192 alternating 512 4194304 0.0550 76327148 361.58 380.58 502.46 654 484570
base2 regexp_extract shared 8 8192 warm 512 4194304 0.0474 88580632 680.13 582.42 1355.29 988 834748
base2 regexp_extract shared 8 8192 alternating 512 4194304 0.0376 111685790 488.62 439.38 1062.08 654 485203
base2 regexp_extract perworker 1 8192 warm 512 4194304 0.1999 20984045 390.21 385.50 427.83 988 834115
base2 regexp_extract perworker 1 8192 alternating 512 4194304 0.1749 23978059 341.44 370.38 429.08 654 484570
base2 regexp_extract perworker 2 8192 warm 512 4194304 0.1067 39293404 414.17 415.21 478.83 988 834115
base2 regexp_extract perworker 2 8192 alternating 512 4194304 0.1049 39983769 350.61 371.62 475.58 654 484570
base2 regexp_extract perworker 4 8192 warm 512 4194304 0.0563 74529409 430.98 424.79 524.67 988 834115
base2 regexp_extract perworker 4 8192 alternating 512 4194304 0.0545 76971358 361.69 383.04 495.58 654 484570
base2 regexp_extract perworker 8 8192 warm 512 4194304 0.0467 89905476 652.07 518.50 1356.33 988 834746
base2 regexp_extract perworker 8 8192 alternating 512 4194304 0.0437 96036820 486.63 421.38 1344.54 654 485195
base2 regexp_extract_all shared 1 512 warm 8192 4194304 0.7057 5943566 86.00 85.12 100.25 1768 776944
base2 regexp_extract_all shared 1 512 alternating 8192 4194304 0.5097 8228637 62.05 80.71 93.08 1351 423442
base2 regexp_extract_all shared 2 512 warm 8192 4194304 0.6632 6324633 161.35 161.17 182.12 1768 776944
base2 regexp_extract_all shared 2 512 alternating 8192 4194304 0.5009 8374264 97.60 82.96 175.88 1351 423442
base2 regexp_extract_all shared 4 512 warm 8192 4194304 0.8229 5097104 400.95 400.33 465.42 1768 776983
base2 regexp_extract_all shared 4 512 alternating 8192 4194304 0.5879 7134104 245.74 205.00 423.29 1351 423442
base2 regexp_extract_all shared 8 512 warm 8192 4194304 1.3155 3188254 1277.15 992.29 1938.92 1768 776983
base2 regexp_extract_all shared 8 512 alternating 8192 4194304 0.8970 4675888 775.73 532.96 1934.79 1351 423481
base2 regexp_extract_all perworker 1 512 warm 8192 4194304 0.7076 5927904 86.23 85.83 98.08 1768 776944
base2 regexp_extract_all perworker 1 512 alternating 8192 4194304 0.5102 8220922 62.11 81.50 93.88 1351 423442
base2 regexp_extract_all perworker 2 512 warm 8192 4194304 0.6578 6376343 160.28 159.96 180.08 1768 776944
base2 regexp_extract_all perworker 2 512 alternating 8192 4194304 0.5013 8366101 97.97 83.12 175.50 1351 423442
base2 regexp_extract_all perworker 4 512 warm 8192 4194304 0.8251 5083362 402.30 400.38 511.04 1768 776944
base2 regexp_extract_all perworker 4 512 alternating 8192 4194304 0.5860 7156939 245.42 205.00 420.21 1351 423442
base2 regexp_extract_all perworker 8 512 warm 8192 4194304 1.3159 3187450 1279.01 991.83 1945.96 1768 776983
base2 regexp_extract_all perworker 8 512 alternating 8192 4194304 0.8917 4703747 772.47 530.50 1932.46 1351 423481
base2 regexp_extract_all shared 1 8192 warm 512 4194304 0.3119 13449295 608.87 608.33 646.00 13268 1447844
base2 regexp_extract_all shared 1 8192 alternating 512 4194304 0.2701 15527826 527.33 579.12 637.12 11720 1023618
base2 regexp_extract_all shared 2 8192 warm 512 4194304 0.3201 13104291 1248.14 1249.88 1309.83 13268 1447844
base2 regexp_extract_all shared 2 8192 alternating 512 4194304 0.2692 15582607 967.12 891.50 1245.29 11720 1023618
base2 regexp_extract_all shared 4 8192 warm 512 4194304 0.4128 10160469 3215.78 3217.42 3585.92 13268 1447844
base2 regexp_extract_all shared 4 8192 alternating 512 4194304 0.3451 12154822 2538.37 2391.71 3550.75 11720 1023618
base2 regexp_extract_all shared 8 8192 warm 512 4194304 0.6231 6730841 9701.51 9684.79 12895.42 13268 1448481
base2 regexp_extract_all shared 8 8192 alternating 512 4194304 0.5288 7932460 7845.44 7681.96 13371.04 11720 1024256
base2 regexp_extract_all perworker 1 8192 warm 512 4194304 0.3111 13482462 607.37 605.33 652.62 13268 1447844
base2 regexp_extract_all perworker 1 8192 alternating 512 4194304 0.2707 15492558 528.53 576.92 640.54 11720 1023618
base2 regexp_extract_all perworker 2 8192 warm 512 4194304 0.3194 13130644 1246.48 1247.17 1297.00 13268 1447844
base2 regexp_extract_all perworker 2 8192 alternating 512 4194304 0.2687 15609007 968.16 900.88 1243.00 11720 1023618
base2 regexp_extract_all perworker 4 8192 warm 512 4194304 0.4134 10146305 3225.08 3221.96 3557.12 13268 1447844
base2 regexp_extract_all perworker 4 8192 alternating 512 4194304 0.3446 12170329 2534.65 2380.79 3549.21 11720 1024244
base2 regexp_extract_all perworker 8 8192 warm 512 4194304 0.6231 6731720 9701.59 9595.04 13371.88 13268 1447835
base2 regexp_extract_all perworker 8 8192 alternating 512 4194304 0.5292 7925622 7857.29 7650.62 13366.04 11719 1023566
base2 split shared 1 512 warm 8192 4194304 0.7925 5292342 96.62 96.00 108.62 185 81347
base2 split shared 1 512 alternating 8192 4194304 0.6745 6218739 82.17 91.54 105.71 183 73091
base2 split shared 2 512 warm 8192 4194304 0.4115 10192031 100.02 99.17 111.50 185 81347
base2 split shared 2 512 alternating 8192 4194304 0.4052 10351667 84.21 91.17 109.25 183 73093
base2 split shared 4 512 warm 8192 4194304 0.2144 19560157 103.20 100.50 126.46 185 81347
base2 split shared 4 512 alternating 8192 4194304 0.2125 19736643 87.96 94.21 121.96 183 73093
base2 split shared 8 512 warm 8192 4194304 0.1617 25939943 154.41 123.42 292.12 185 81348
base2 split shared 8 512 alternating 8192 4194304 0.1511 27767623 126.96 105.29 279.12 183 73093
base2 split perworker 1 512 warm 8192 4194304 0.7863 5334263 95.85 95.21 108.12 185 81345
base2 split perworker 1 512 alternating 8192 4194304 0.6683 6276301 81.44 90.92 103.88 183 73091
base2 split perworker 2 512 warm 8192 4194304 0.4131 10154003 100.40 99.54 112.71 185 81347
base2 split perworker 2 512 alternating 8192 4194304 0.4050 10357407 84.09 90.92 109.29 183 73091
base2 split perworker 4 512 warm 8192 4194304 0.2151 19500284 103.19 100.54 127.04 185 81347
base2 split perworker 4 512 alternating 8192 4194304 0.2152 19492170 88.98 94.21 123.33 183 73093
base2 split perworker 8 512 warm 8192 4194304 0.1681 24955023 151.11 120.08 282.75 185 81346
base2 split perworker 8 512 alternating 8192 4194304 0.1534 27343598 130.04 112.29 277.46 183 73092
base2 split shared 1 8192 warm 512 4194304 0.7378 5685053 1440.69 1438.21 1503.00 193 1095568
base2 split shared 1 8192 alternating 512 4194304 0.6304 6653236 1231.01 1419.88 1511.38 191 964306
base2 split shared 2 8192 warm 512 4194304 0.3737 11224115 1457.29 1455.88 1491.08 193 1095570
base2 split shared 2 8192 alternating 512 4194304 0.3709 11307827 1234.63 1401.79 1484.04 191 964314
base2 split shared 4 8192 warm 512 4194304 0.1942 21598933 1495.26 1468.54 1735.96 193 1095572
base2 split shared 4 8192 alternating 512 4194304 0.1919 21858464 1273.79 1428.54 1563.42 191 964320
base2 split shared 8 8192 warm 512 4194304 0.1366 30696496 1965.51 1544.75 2969.54 193 1095579
base2 split shared 8 8192 alternating 512 4194304 0.1218 34441036 1621.36 1512.12 2971.12 190 964175
base2 split perworker 1 8192 warm 512 4194304 0.7357 5701249 1436.59 1435.12 1493.04 193 1095483
base2 split perworker 1 8192 alternating 512 4194304 0.6264 6696087 1223.09 1401.58 1492.04 191 964240
base2 split perworker 2 8192 warm 512 4194304 0.3726 11255417 1453.41 1452.42 1495.75 193 1095570
base2 split perworker 2 8192 alternating 512 4194304 0.3722 11269948 1236.48 1415.62 1485.88 191 964310
base2 split perworker 4 8192 warm 512 4194304 0.1956 21446890 1511.94 1485.96 1784.79 193 1095574
base2 split perworker 4 8192 alternating 512 4194304 0.1908 21984814 1262.89 1439.04 1598.75 191 964316
base2 split perworker 8 8192 warm 512 4194304 0.1318 31832717 1972.79 1542.71 3007.04 193 1095562
base2 split perworker 8 8192 alternating 512 4194304 0.1231 34082564 1609.45 1511.46 2954.46 191 964325
label function first_us_mean second_us_mean reps
base2 regexp_extract 397.88 392.59 30
base2 regexp_extract_all 599.41 597.02 30
base2 split 1432.67 1424.06 30
label function batch_rows regime hash
base2 regexp_extract 512 warm 48cf9292e3499221
base2 regexp_extract 512 alternating abc22eb5d1b7ff93
base2 regexp_extract 8192 warm 36022698d568939d
base2 regexp_extract 8192 alternating 2aad4e5fa2692c3b
base2 regexp_extract_all 512 warm b2dfff0b97f0fcb9
base2 regexp_extract_all 512 alternating 87eba83c99f6b34e
base2 regexp_extract_all 8192 warm 42115326f3672337
base2 regexp_extract_all 8192 alternating 46327f4061153e54
base2 split 512 warm 8549f20566101054
base2 split 512 alternating d6f1a26e828fcc75
base2 split 8192 warm 7aa75b4b5320f857
base2 split 8192 alternating 4bd6e6754eab8b91
label function sharing workers batch_rows regime batches rows_total wall_s rows_per_s mean_us p50_us p99_us allocs_per_batch bytes_per_batch
base3 regexp_extract shared 1 512 warm 8192 4194304 0.6004 6986218 73.18 73.04 80.00 988 738598
base3 regexp_extract shared 1 512 alternating 8192 4194304 0.4178 10038573 50.90 67.71 79.75 654 389052
base3 regexp_extract shared 2 512 warm 8192 4194304 0.4083 10272767 99.44 97.75 122.33 988 738598
base3 regexp_extract shared 2 512 alternating 8192 4194304 0.3375 12426038 59.17 67.50 110.92 654 389052
base3 regexp_extract shared 4 512 warm 8192 4194304 0.4182 10028727 203.09 203.62 235.96 988 738637
base3 regexp_extract shared 4 512 alternating 8192 4194304 0.2754 15230733 100.48 93.17 212.88 654 389052
base3 regexp_extract shared 8 512 warm 8192 4194304 0.6929 6052848 675.52 620.46 853.38 988 738637
base3 regexp_extract shared 8 512 alternating 8192 4194304 0.3688 11373338 290.60 215.21 782.00 654 389092
base3 regexp_extract perworker 1 512 warm 8192 4194304 0.5976 7018848 72.86 72.71 79.04 988 738598
base3 regexp_extract perworker 1 512 alternating 8192 4194304 0.4123 10172773 50.22 69.25 77.75 654 389052
base3 regexp_extract perworker 2 512 warm 8192 4194304 0.4055 10343688 98.78 98.17 111.17 988 738598
base3 regexp_extract perworker 2 512 alternating 8192 4194304 0.3377 12420121 59.31 67.54 110.58 654 389052
base3 regexp_extract perworker 4 512 warm 8192 4194304 0.4185 10021742 203.16 204.08 236.58 988 738637
base3 regexp_extract perworker 4 512 alternating 8192 4194304 0.2787 15047132 101.97 94.38 207.12 654 389052
base3 regexp_extract perworker 8 512 warm 8192 4194304 0.6912 6067758 673.35 621.58 859.00 988 738637
base3 regexp_extract perworker 8 512 alternating 8192 4194304 0.3679 11401054 289.61 215.33 781.33 654 389090
base3 regexp_extract shared 1 8192 warm 512 4194304 0.2144 19558545 418.66 418.29 440.92 988 834115
base3 regexp_extract shared 1 8192 alternating 512 4194304 0.1808 23196467 352.97 384.46 430.50 654 484570
base3 regexp_extract shared 2 8192 warm 512 4194304 0.1164 36037435 453.06 452.25 515.96 988 834115
base3 regexp_extract shared 2 8192 alternating 512 4194304 0.1131 37092568 375.63 402.62 491.42 654 484570
base3 regexp_extract shared 4 8192 warm 512 4194304 0.0625 67142076 482.96 480.29 602.62 988 834115
base3 regexp_extract shared 4 8192 alternating 512 4194304 0.0588 71303204 389.43 400.75 529.29 654 484570
base3 regexp_extract shared 8 8192 warm 512 4194304 0.0475 88286810 704.74 626.58 1245.46 988 834747
base3 regexp_extract shared 8 8192 alternating 512 4194304 0.0394 106380503 513.11 466.42 1123.50 654 485198
base3 regexp_extract perworker 1 8192 warm 512 4194304 0.2124 19751658 414.55 414.67 427.83 988 834115
base3 regexp_extract perworker 1 8192 alternating 512 4194304 0.1808 23197659 352.95 379.46 429.83 654 484570
base3 regexp_extract perworker 2 8192 warm 512 4194304 0.1177 35630692 456.44 451.00 523.29 988 834115
base3 regexp_extract perworker 2 8192 alternating 512 4194304 0.1134 36978764 376.12 385.08 491.67 654 484570
base3 regexp_extract perworker 4 8192 warm 512 4194304 0.0662 63329726 493.61 475.17 899.88 988 834115
base3 regexp_extract perworker 4 8192 alternating 512 4194304 0.0603 69522068 393.64 420.79 549.46 654 484570
base3 regexp_extract perworker 8 8192 warm 512 4194304 0.0484 86583964 727.24 656.04 1319.71 988 834754
base3 regexp_extract perworker 8 8192 alternating 512 4194304 0.0398 105285541 525.52 492.17 1073.96 654 485205
base3 regexp_extract_all shared 1 512 warm 8192 4194304 0.7167 5851844 87.34 86.71 97.50 1768 776944
base3 regexp_extract_all shared 1 512 alternating 8192 4194304 0.5155 8136714 62.76 81.83 93.54 1351 423442
base3 regexp_extract_all shared 2 512 warm 8192 4194304 0.6627 6329133 161.34 161.33 180.21 1768 776944
base3 regexp_extract_all shared 2 512 alternating 8192 4194304 0.5020 8355449 98.07 83.17 175.00 1351 423442
base3 regexp_extract_all shared 4 512 warm 8192 4194304 0.8166 5136349 396.81 399.25 418.17 1768 776944
base3 regexp_extract_all shared 4 512 alternating 8192 4194304 0.5843 7178645 244.29 203.88 420.12 1351 423442
base3 regexp_extract_all shared 8 512 warm 8192 4194304 1.2528 3348032 1219.35 1134.08 1505.00 1768 776984
base3 regexp_extract_all shared 8 512 alternating 8192 4194304 0.8521 4922357 734.30 616.00 1455.21 1351 423482
base3 regexp_extract_all perworker 1 512 warm 8192 4194304 0.7063 5938772 86.07 85.83 94.54 1768 776944
base3 regexp_extract_all perworker 1 512 alternating 8192 4194304 0.5194 8074585 63.25 81.33 95.17 1351 423442
base3 regexp_extract_all perworker 2 512 warm 8192 4194304 0.6695 6265011 162.06 161.96 185.38 1768 776944
base3 regexp_extract_all perworker 2 512 alternating 8192 4194304 0.4989 8406619 97.13 82.75 175.12 1351 423442
base3 regexp_extract_all perworker 4 512 warm 8192 4194304 0.8160 5140234 396.57 398.92 418.00 1768 776944
base3 regexp_extract_all perworker 4 512 alternating 8192 4194304 0.5850 7170114 244.33 204.79 418.54 1351 423442
base3 regexp_extract_all perworker 8 512 warm 8192 4194304 1.2508 3353355 1219.04 1135.71 1486.75 1768 776984
base3 regexp_extract_all perworker 8 512 alternating 8192 4194304 0.8528 4918535 734.86 602.33 1491.38 1351 423481
base3 regexp_extract_all shared 1 8192 warm 512 4194304 0.3208 13074288 626.29 624.21 661.88 13268 1447844
base3 regexp_extract_all shared 1 8192 alternating 512 4194304 0.2765 15167801 539.82 597.92 679.25 11720 1023618
base3 regexp_extract_all shared 2 8192 warm 512 4194304 0.3223 13012779 1254.64 1258.58 1301.29 13268 1447844
base3 regexp_extract_all shared 2 8192 alternating 512 4194304 0.2700 15532280 970.33 899.92 1239.33 11720 1023618
base3 regexp_extract_all shared 4 8192 warm 512 4194304 0.4088 10259236 3187.93 3175.00 4482.38 13268 1447844
base3 regexp_extract_all shared 4 8192 alternating 512 4194304 0.3440 12191604 2531.48 2388.58 3183.00 11720 1024244
base3 regexp_extract_all shared 8 8192 warm 512 4194304 0.5942 7058707 9266.44 9212.25 10762.08 13268 1448477
base3 regexp_extract_all shared 8 8192 alternating 512 4194304 0.4993 8399878 7397.26 7426.21 10716.67 11720 1024248
base3 regexp_extract_all perworker 1 8192 warm 512 4194304 0.3204 13089172 625.56 624.92 658.83 13268 1447844
base3 regexp_extract_all perworker 1 8192 alternating 512 4194304 0.2781 15083052 542.87 591.00 654.00 11720 1023618
base3 regexp_extract_all perworker 2 8192 warm 512 4194304 0.3221 13022895 1254.54 1260.88 1305.96 13268 1448469
base3 regexp_extract_all perworker 2 8192 alternating 512 4194304 0.2709 15483874 971.93 898.83 1250.96 11720 1023618
base3 regexp_extract_all perworker 4 8192 warm 512 4194304 0.4058 10336736 3161.54 3172.58 3227.71 13268 1448469
base3 regexp_extract_all perworker 4 8192 alternating 512 4194304 0.3426 12243992 2512.52 2366.25 3203.92 11720 1023618
base3 regexp_extract_all perworker 8 8192 warm 512 4194304 0.5939 7062347 9252.57 9230.75 10694.25 13268 1448477
base3 regexp_extract_all perworker 8 8192 alternating 512 4194304 0.5018 8358179 7439.78 7489.71 10739.92 11720 1024250
base3 split shared 1 512 warm 8192 4194304 0.7886 5318784 96.13 95.67 105.50 185 81347
base3 split shared 1 512 alternating 8192 4194304 0.6681 6277597 81.39 90.92 100.17 183 73092
base3 split shared 2 512 warm 8192 4194304 0.4115 10192426 100.03 99.38 110.29 185 81347
base3 split shared 2 512 alternating 8192 4194304 0.4067 10314271 84.38 91.21 108.83 183 73093
base3 split shared 4 512 warm 8192 4194304 0.2141 19586570 103.22 100.83 124.58 185 81347
base3 split shared 4 512 alternating 8192 4194304 0.2110 19879985 86.82 93.71 117.25 183 73093
base3 split shared 8 512 warm 8192 4194304 0.1618 25916715 153.03 124.67 273.33 185 81343
base3 split shared 8 512 alternating 8192 4194304 0.1520 27600226 126.84 107.62 269.83 183 73093
base3 split perworker 1 512 warm 8192 4194304 0.7889 5316510 96.17 95.71 107.04 185 81347
base3 split perworker 1 512 alternating 8192 4194304 0.6696 6263794 81.57 90.38 101.21 183 73091
base3 split perworker 2 512 warm 8192 4194304 0.4122 10175262 99.99 99.33 111.21 185 81345
base3 split perworker 2 512 alternating 8192 4194304 0.4032 10401954 83.96 89.42 108.58 183 73093
base3 split perworker 4 512 warm 8192 4194304 0.2152 19492313 102.91 100.46 123.67 185 81347
base3 split perworker 4 512 alternating 8192 4194304 0.2114 19840261 87.27 93.50 118.17 183 73093
base3 split perworker 8 512 warm 8192 4194304 0.1628 25759609 155.20 126.54 282.12 185 81347
base3 split perworker 8 512 alternating 8192 4194304 0.1504 27893370 127.45 107.67 258.88 183 73090
base3 split shared 1 8192 warm 512 4194304 0.7342 5712870 1433.67 1434.50 1467.50 193 1095558
base3 split shared 1 8192 alternating 512 4194304 0.6256 6704692 1221.56 1407.21 1468.00 191 964304
base3 split shared 2 8192 warm 512 4194304 0.3728 11251108 1452.08 1450.92 1479.25 193 1095568
base3 split shared 2 8192 alternating 512 4194304 0.3701 11334320 1232.06 1413.67 1469.96 191 964314
base3 split shared 4 8192 warm 512 4194304 0.1914 21917178 1480.95 1464.38 1716.42 193 1095575
base3 split shared 4 8192 alternating 512 4194304 0.1904 22030166 1264.13 1425.58 1554.04 191 964323
base3 split shared 8 8192 warm 512 4194304 0.1305 32151963 1973.53 1545.25 3052.54 193 1095533
base3 split shared 8 8192 alternating 512 4194304 0.1234 33999221 1603.40 1472.25 3029.67 190 964085
base3 split perworker 1 8192 warm 512 4194304 0.7348 5707708 1434.97 1433.79 1468.62 193 1095560
base3 split perworker 1 8192 alternating 512 4194304 0.6254 6706089 1221.31 1412.04 1464.83 191 964314
base3 split perworker 2 8192 warm 512 4194304 0.3719 11276699 1449.89 1448.83 1491.08 193 1095570
base3 split perworker 2 8192 alternating 512 4194304 0.3712 11299227 1232.86 1406.21 1485.38 191 964316
base3 split perworker 4 8192 warm 512 4194304 0.1923 21812265 1475.75 1454.25 1710.04 193 1095574
base3 split perworker 4 8192 alternating 512 4194304 0.1897 22105039 1256.78 1413.25 1554.33 191 964318
base3 split perworker 8 8192 warm 512 4194304 0.1341 31278688 1960.96 1535.38 2995.08 193 1095580
base3 split perworker 8 8192 alternating 512 4194304 0.1204 34832820 1605.07 1469.46 2997.46 191 964333
label function first_us_mean second_us_mean reps
base3 regexp_extract 423.87 420.29 30
base3 regexp_extract_all 628.19 626.94 30
base3 split 1438.48 1425.42 30
label function batch_rows regime hash
base3 regexp_extract 512 warm 48cf9292e3499221
base3 regexp_extract 512 alternating abc22eb5d1b7ff93
base3 regexp_extract 8192 warm 36022698d568939d
base3 regexp_extract 8192 alternating 2aad4e5fa2692c3b
base3 regexp_extract_all 512 warm b2dfff0b97f0fcb9
base3 regexp_extract_all 512 alternating 87eba83c99f6b34e
base3 regexp_extract_all 8192 warm 42115326f3672337
base3 regexp_extract_all 8192 alternating 46327f4061153e54
base3 split 512 warm 8549f20566101054
base3 split 512 alternating d6f1a26e828fcc75
base3 split 8192 warm 7aa75b4b5320f857
base3 split 8192 alternating 4bd6e6754eab8b91
label function sharing workers batch_rows regime batches rows_total wall_s rows_per_s mean_us p50_us p99_us allocs_per_batch bytes_per_batch
base regexp_extract shared 1 512 warm 8192 4194304 0.6038 6946051 73.63 73.17 84.50 988 738598
base regexp_extract shared 1 512 alternating 8192 4194304 0.4296 9764308 52.34 71.29 103.33 654 389052
base regexp_extract shared 2 512 warm 8192 4194304 0.4148 10111674 101.14 100.08 123.04 988 738598
base regexp_extract shared 2 512 alternating 8192 4194304 0.3440 12192416 60.22 70.25 115.67 654 389052
base regexp_extract shared 4 512 warm 8192 4194304 0.4347 9648865 211.06 206.29 422.25 988 738598
base regexp_extract shared 4 512 alternating 8192 4194304 0.2948 14226865 107.87 99.46 362.08 654 389092
base regexp_extract shared 8 512 warm 8192 4194304 0.7186 5836621 694.62 553.92 1342.50 988 738597
base regexp_extract shared 8 512 alternating 8192 4194304 0.3818 10986696 301.19 208.42 1053.00 654 389091
base regexp_extract perworker 1 512 warm 8192 4194304 0.6074 6905342 74.07 72.83 95.42 988 738598
base regexp_extract perworker 1 512 alternating 8192 4194304 0.4157 10088713 50.66 69.54 84.62 654 389052
base regexp_extract perworker 2 512 warm 8192 4194304 0.4097 10238405 99.88 99.08 114.83 988 738598
base regexp_extract perworker 2 512 alternating 8192 4194304 0.3408 12307787 59.65 70.04 112.54 654 389052
base regexp_extract perworker 4 512 warm 8192 4194304 0.4535 9248037 219.44 208.29 445.21 988 738598
base regexp_extract perworker 4 512 alternating 8192 4194304 0.2783 15068483 101.65 94.67 214.58 654 389052
base regexp_extract perworker 8 512 warm 8192 4194304 0.7254 5781836 699.15 557.46 1270.75 988 738637
base regexp_extract perworker 8 512 alternating 8192 4194304 0.3773 11116318 294.31 206.21 1097.04 654 389092
base regexp_extract shared 1 8192 warm 512 4194304 0.2080 20165489 406.03 404.71 459.25 988 834115
base regexp_extract shared 1 8192 alternating 512 4194304 0.1765 23763448 344.51 371.62 438.58 654 484570
base regexp_extract shared 2 8192 warm 512 4194304 0.1166 35957611 454.74 438.17 829.79 988 834115
base regexp_extract shared 2 8192 alternating 512 4194304 0.1089 38503182 361.11 390.00 471.71 654 484570
base regexp_extract shared 4 8192 warm 512 4194304 0.0590 71120486 457.50 448.33 600.33 988 834115
base regexp_extract shared 4 8192 alternating 512 4194304 0.0613 68378423 407.00 425.88 638.50 654 484570
base regexp_extract shared 8 8192 warm 512 4194304 0.0495 84763525 712.59 614.62 1346.25 988 834755
base regexp_extract shared 8 8192 alternating 512 4194304 0.0395 106078497 510.55 471.00 1026.25 653 485139
base regexp_extract perworker 1 8192 warm 512 4194304 0.2075 20212527 405.11 404.54 432.38 988 834115
base regexp_extract perworker 1 8192 alternating 512 4194304 0.1757 23876782 342.90 380.71 426.17 654 484570
base regexp_extract perworker 2 8192 warm 512 4194304 0.1142 36714110 445.57 444.08 526.17 988 834115
base regexp_extract perworker 2 8192 alternating 512 4194304 0.1085 38645544 360.71 391.04 475.00 654 484570
base regexp_extract perworker 4 8192 warm 512 4194304 0.0607 69094644 464.58 455.62 609.21 988 834741
base regexp_extract perworker 4 8192 alternating 512 4194304 0.0669 62658405 429.36 425.50 861.25 654 484570
base regexp_extract perworker 8 8192 warm 512 4194304 0.0529 79304838 740.06 680.50 1352.17 988 834750
base regexp_extract perworker 8 8192 alternating 512 4194304 0.0441 95034766 558.18 533.12 1047.25 654 485209
base regexp_extract_all shared 1 512 warm 8192 4194304 0.7358 5700454 89.66 88.00 108.21 1768 776944
base regexp_extract_all shared 1 512 alternating 8192 4194304 0.5165 8119987 62.89 83.00 96.88 1351 423442
base regexp_extract_all shared 2 512 warm 8192 4194304 0.6719 6242603 163.73 162.00 224.58 1768 776944
base regexp_extract_all shared 2 512 alternating 8192 4194304 0.5268 7961139 103.27 86.12 191.33 1351 423442
base regexp_extract_all shared 4 512 warm 8192 4194304 0.8431 4974573 407.71 400.58 751.67 1768 776944
base regexp_extract_all shared 4 512 alternating 8192 4194304 0.5905 7103422 247.06 205.88 421.08 1351 423442
base regexp_extract_all shared 8 512 warm 8192 4194304 1.3167 3185579 1280.12 990.75 1978.04 1768 776983
base regexp_extract_all shared 8 512 alternating 8192 4194304 0.8951 4685978 771.63 532.04 1945.00 1351 423481
base regexp_extract_all perworker 1 512 warm 8192 4194304 0.7097 5910227 86.49 85.96 98.71 1768 776944
base regexp_extract_all perworker 1 512 alternating 8192 4194304 0.5125 8183507 62.40 81.46 95.29 1351 423442
base regexp_extract_all perworker 2 512 warm 8192 4194304 0.6589 6365996 160.50 160.17 180.58 1768 776944
base regexp_extract_all perworker 2 512 alternating 8192 4194304 0.5022 8351251 97.86 82.96 177.67 1351 423442
base regexp_extract_all perworker 4 512 warm 8192 4194304 0.8244 5087853 401.08 400.83 492.92 1768 776944
base regexp_extract_all perworker 4 512 alternating 8192 4194304 0.5886 7126004 246.22 204.79 423.67 1351 423442
base regexp_extract_all perworker 8 512 warm 8192 4194304 1.3202 3176995 1283.51 995.79 1953.62 1768 776984
base regexp_extract_all perworker 8 512 alternating 8192 4194304 0.8929 4697322 773.31 534.96 1943.08 1351 423481
base regexp_extract_all shared 1 8192 warm 512 4194304 0.3079 13621102 601.16 599.33 641.33 13268 1447844
base regexp_extract_all shared 1 8192 alternating 512 4194304 0.2705 15507252 527.98 567.92 647.83 11720 1023618
base regexp_extract_all shared 2 8192 warm 512 4194304 0.3192 13140642 1242.89 1245.00 1302.17 13268 1447844
base regexp_extract_all shared 2 8192 alternating 512 4194304 0.2682 15636128 962.30 885.00 1244.29 11720 1023615
base regexp_extract_all shared 4 8192 warm 512 4194304 0.4167 10064943 3252.89 3241.46 3733.75 13268 1448476
base regexp_extract_all shared 4 8192 alternating 512 4194304 0.3439 12195885 2530.81 2388.12 3301.04 11720 1024250
base regexp_extract_all shared 8 8192 warm 512 4194304 0.6238 6723916 9715.27 9554.25 14115.46 13268 1447827
base regexp_extract_all shared 8 8192 alternating 512 4194304 0.5274 7952892 7793.10 7597.88 13496.25 11719 1024198
base regexp_extract_all perworker 1 8192 warm 512 4194304 0.3102 13523073 605.52 604.96 640.79 13268 1447844
base regexp_extract_all perworker 1 8192 alternating 512 4194304 0.2723 15402155 531.61 579.25 649.92 11720 1023618
base regexp_extract_all perworker 2 8192 warm 512 4194304 0.3191 13143391 1245.39 1247.12 1294.83 13268 1447844
base regexp_extract_all perworker 2 8192 alternating 512 4194304 0.2692 15580945 967.90 896.04 1244.00 11720 1023618
base regexp_extract_all perworker 4 8192 warm 512 4194304 0.4135 10144387 3222.87 3220.17 3599.21 13268 1447844
base regexp_extract_all perworker 4 8192 alternating 512 4194304 0.3467 12098634 2555.83 2390.33 3955.25 11720 1023618
base regexp_extract_all perworker 8 8192 warm 512 4194304 0.6231 6731713 9665.15 9457.62 13921.33 13268 1448477
base regexp_extract_all perworker 8 8192 alternating 512 4194304 0.5288 7931529 7798.39 7524.29 13208.00 11720 1024254
base split shared 1 512 warm 8192 4194304 0.7897 5311136 96.27 95.50 108.71 185 81345
base split shared 1 512 alternating 8192 4194304 0.6683 6275795 81.44 91.00 104.92 183 73091
base split shared 2 512 warm 8192 4194304 0.4080 10279861 99.27 98.54 110.62 185 81347
base split shared 2 512 alternating 8192 4194304 0.4035 10395456 83.81 90.92 108.42 183 73093
base split shared 4 512 warm 8192 4194304 0.2194 19117965 104.46 102.58 127.12 185 81347
base split shared 4 512 alternating 8192 4194304 0.2140 19597113 89.10 94.42 124.62 183 73093
base split shared 8 512 warm 8192 4194304 0.1647 25473693 154.39 124.50 289.04 185 81347
base split shared 8 512 alternating 8192 4194304 0.1548 27098160 128.49 107.08 280.38 183 73091
base split perworker 1 512 warm 8192 4194304 0.7851 5342199 95.71 95.08 107.83 185 81345
base split perworker 1 512 alternating 8192 4194304 0.6665 6293242 81.21 90.71 103.79 183 73093
base split perworker 2 512 warm 8192 4194304 0.4091 10252838 99.56 98.83 110.50 185 81347
base split perworker 2 512 alternating 8192 4194304 0.4043 10373105 84.02 90.88 108.54 183 73091
base split perworker 4 512 warm 8192 4194304 0.2211 18973800 106.02 103.83 134.62 185 81347
base split perworker 4 512 alternating 8192 4194304 0.2158 19438641 88.99 94.04 123.54 183 73093
base split perworker 8 512 warm 8192 4194304 0.1639 25594031 154.35 122.79 293.12 185 81348
base split perworker 8 512 alternating 8192 4194304 0.1503 27913775 130.45 112.00 271.38 183 73092
base split shared 1 8192 warm 512 4194304 0.7317 5732032 1428.90 1425.08 1481.71 193 1095568
base split shared 1 8192 alternating 512 4194304 0.6236 6725816 1217.73 1401.04 1478.33 191 964230
base split shared 2 8192 warm 512 4194304 0.3701 11332547 1444.16 1441.88 1485.88 193 1095568
base split shared 2 8192 alternating 512 4194304 0.3689 11369899 1227.85 1406.88 1474.83 191 964314
base split shared 4 8192 warm 512 4194304 0.2029 20666780 1523.10 1469.96 2366.88 193 1095574
base split shared 4 8192 alternating 512 4194304 0.1920 21847581 1271.07 1429.88 1619.29 191 964320
base split shared 8 8192 warm 512 4194304 0.1931 21725152 2717.57 1973.50 12089.00 193 1095588
base split shared 8 8192 alternating 512 4194304 0.1206 34773907 1628.19 1524.58 2952.75 190 964099
base split perworker 1 8192 warm 512 4194304 0.7343 5711829 1433.93 1431.25 1477.88 193 1095483
base split perworker 1 8192 alternating 512 4194304 0.6252 6708495 1220.87 1410.58 1473.17 191 964304
base split perworker 2 8192 warm 512 4194304 0.3714 11292686 1445.68 1445.58 1476.33 193 1095571
base split perworker 2 8192 alternating 512 4194304 0.3691 11362474 1227.18 1409.50 1469.38 191 964309
base split perworker 4 8192 warm 512 4194304 0.1972 21268468 1524.49 1521.12 1887.75 193 1095573
base split perworker 4 8192 alternating 512 4194304 0.1895 22135940 1260.48 1428.83 1548.46 191 964318
base split perworker 8 8192 warm 512 4194304 0.1321 31742106 1984.37 1545.33 3018.79 193 1095587
base split perworker 8 8192 alternating 512 4194304 0.1230 34098277 1631.68 1535.12 2966.83 191 964325
label function first_us_mean second_us_mean reps
base regexp_extract 415.25 408.35 30
base regexp_extract_all 612.66 612.83 30
base split 1444.52 1431.87 30
label function batch_rows regime hash
base regexp_extract 512 warm 48cf9292e3499221
base regexp_extract 512 alternating abc22eb5d1b7ff93
base regexp_extract 8192 warm 36022698d568939d
base regexp_extract 8192 alternating 2aad4e5fa2692c3b
base regexp_extract_all 512 warm b2dfff0b97f0fcb9
base regexp_extract_all 512 alternating 87eba83c99f6b34e
base regexp_extract_all 8192 warm 42115326f3672337
base regexp_extract_all 8192 alternating 46327f4061153e54
base split 512 warm 8549f20566101054
base split 512 alternating d6f1a26e828fcc75
base split 8192 warm 7aa75b4b5320f857
base split 8192 alternating 4bd6e6754eab8b91
# Standalone benchmark harness for datafusion-comet PR #5612 (regex pattern cache).
# Path-depends on the repo's spark-expr crate so the same harness builds against
# whichever commit is checked out in the repo.
[package]
name = "bench5612"
version = "0.1.0"
edition = "2021"
[workspace]
[dependencies]
datafusion-comet-spark-expr = { path = "/Users/dustin/code/datafusion-comet/native/spark-expr" }
# Match the repo workspace's pinned versions/features (native/Cargo.toml).
datafusion = { version = "54.1.0", default-features = false, features = [
"unicode_expressions",
"crypto_expressions",
"nested_expressions",
"parquet",
] }
arrow = { version = "58.4.0", features = ["prettyprint", "ffi", "chrono-tz"] }
regex = "1.12.3"
tokio = { version = "1", features = ["rt-multi-thread"] }
[profile.release]
opt-level = 3
debug = false
lto = "thin"
codegen-units = 1
label function sharing workers batch_rows regime batches rows_total wall_s rows_per_s mean_us p50_us p99_us allocs_per_batch bytes_per_batch
head2 regexp_extract shared 1 512 warm 8192 4194304 0.2336 17957932 28.15 28.08 32.71 82 48278
head2 regexp_extract shared 1 512 alternating 8192 4194304 0.4063 10322552 49.49 68.17 78.62 658 391170
head2 regexp_extract shared 2 512 warm 8192 4194304 0.1250 33551222 30.36 30.12 36.46 82 48278
head2 regexp_extract shared 2 512 alternating 8192 4194304 0.3333 12584104 81.08 80.96 90.21 655 389269
head2 regexp_extract shared 4 512 warm 8192 4194304 0.0753 55688430 36.16 35.54 48.08 82 48278
head2 regexp_extract shared 4 512 alternating 8192 4194304 0.3094 13554736 150.77 154.58 168.38 638 379063
head2 regexp_extract shared 8 512 warm 8192 4194304 0.0648 64686525 59.35 51.96 104.50 82 48281
head2 regexp_extract shared 8 512 alternating 8192 4194304 0.2840 14766087 276.60 255.12 335.04 572 337842
head2 regexp_extract perworker 1 512 warm 8192 4194304 0.2235 18766786 27.19 26.79 32.96 82 48278
head2 regexp_extract perworker 1 512 alternating 8192 4194304 0.4050 10355712 49.33 68.21 76.50 658 391170
head2 regexp_extract perworker 2 512 warm 8192 4194304 0.1315 31901397 31.92 31.46 40.71 82 48278
head2 regexp_extract perworker 2 512 alternating 8192 4194304 0.1229 34114027 25.81 25.67 35.83 79 30795
head2 regexp_extract perworker 4 512 warm 8192 4194304 0.0762 55077573 36.29 35.75 47.08 82 48278
head2 regexp_extract perworker 4 512 alternating 8192 4194304 0.0697 60149488 28.86 28.83 42.58 79 30795
head2 regexp_extract perworker 8 512 warm 8192 4194304 0.0651 64439439 60.58 55.42 102.62 82 48274
head2 regexp_extract perworker 8 512 alternating 8192 4194304 0.0604 69454145 49.66 44.75 98.79 80 30799
head2 regexp_extract shared 1 8192 warm 512 4194304 0.1725 24319523 336.64 329.83 375.42 82 144146
head2 regexp_extract shared 1 8192 alternating 512 4194304 0.1710 24521969 333.86 366.75 420.71 658 486688
head2 regexp_extract shared 2 8192 warm 512 4194304 0.0923 45463815 358.72 361.08 414.17 82 144157
head2 regexp_extract shared 2 8192 alternating 512 4194304 0.0989 42428385 339.82 325.00 437.12 502 389941
head2 regexp_extract shared 4 8192 warm 512 4194304 0.0507 82714158 387.88 383.25 488.62 82 144157
head2 regexp_extract shared 4 8192 alternating 512 4194304 0.0515 81365578 350.97 345.04 489.08 362 302657
head2 regexp_extract shared 8 8192 warm 512 4194304 0.0348 120578817 490.77 399.04 766.21 82 144394
head2 regexp_extract shared 8 8192 alternating 512 4194304 0.0331 126860973 466.34 426.92 941.38 396 324012
head2 regexp_extract perworker 1 8192 warm 512 4194304 0.1732 24212313 338.14 330.54 380.71 82 144137
head2 regexp_extract perworker 1 8192 alternating 512 4194304 0.1714 24471970 334.55 366.88 426.54 658 486688
head2 regexp_extract perworker 2 8192 warm 512 4194304 0.0938 44706676 364.43 365.33 414.12 82 144155
head2 regexp_extract perworker 2 8192 alternating 512 4194304 0.0926 45290763 328.19 329.33 407.25 79 126663
head2 regexp_extract perworker 4 8192 warm 512 4194304 0.0528 79484054 405.64 408.04 472.92 82 144157
head2 regexp_extract perworker 4 8192 alternating 512 4194304 0.0509 82398984 349.68 346.29 474.21 79 126402
head2 regexp_extract perworker 8 8192 warm 512 4194304 0.0343 122185844 481.91 388.17 748.21 82 144187
head2 regexp_extract perworker 8 8192 alternating 512 4194304 0.0325 129081821 426.49 380.04 729.75 79 126916
head2 regexp_extract_all shared 1 512 warm 8192 4194304 0.3319 12636332 40.37 40.04 47.54 862 86623
head2 regexp_extract_all shared 1 512 alternating 8192 4194304 0.5166 8118409 62.91 82.75 94.17 1355 425560
head2 regexp_extract_all shared 2 512 warm 8192 4194304 0.4669 8983808 113.65 112.79 130.71 862 86624
head2 regexp_extract_all shared 2 512 alternating 8192 4194304 0.4713 8899631 109.72 128.54 160.25 1211 335933
head2 regexp_extract_all shared 4 512 warm 8192 4194304 0.4879 8595840 237.01 237.04 291.46 862 86625
head2 regexp_extract_all shared 4 512 alternating 8192 4194304 0.5764 7276120 279.61 284.88 396.71 1319 402981
head2 regexp_extract_all shared 8 512 warm 8192 4194304 0.7113 5896338 691.17 634.17 853.83 862 86625
head2 regexp_extract_all shared 8 512 alternating 8192 4194304 0.6286 6671939 612.31 620.08 705.96 1020 216972
head2 regexp_extract_all perworker 1 512 warm 8192 4194304 0.3333 12584364 40.53 40.21 47.21 862 86624
head2 regexp_extract_all perworker 1 512 alternating 8192 4194304 0.5082 8253430 61.88 80.71 93.08 1355 425560
head2 regexp_extract_all perworker 2 512 warm 8192 4194304 0.3353 12510930 81.54 81.29 93.42 862 86624
head2 regexp_extract_all perworker 2 512 alternating 8192 4194304 0.2898 14472416 65.15 61.21 85.71 777 65184
head2 regexp_extract_all perworker 4 512 warm 8192 4194304 0.4310 9731070 209.32 210.46 223.88 862 86625
head2 regexp_extract_all perworker 4 512 alternating 8192 4194304 0.3638 11529520 168.20 161.75 211.96 777 65183
head2 regexp_extract_all perworker 8 512 warm 8192 4194304 0.6171 6797283 600.45 562.88 762.54 862 86625
head2 regexp_extract_all perworker 8 512 alternating 8192 4194304 0.5370 7810920 500.01 520.38 776.62 777 65185
head2 regexp_extract_all shared 1 8192 warm 512 4194304 0.2826 14842499 551.68 547.17 603.79 12362 757997
head2 regexp_extract_all shared 1 8192 alternating 512 4194304 0.2694 15571119 525.85 568.17 638.71 11724 1025736
head2 regexp_extract_all shared 2 8192 warm 512 4194304 0.4489 9343928 1751.45 1753.25 1848.21 12362 758007
head2 regexp_extract_all shared 2 8192 alternating 512 4194304 0.2669 15716205 969.49 911.00 1256.00 11572 931909
head2 regexp_extract_all shared 4 8192 warm 512 4194304 0.4408 9515515 3433.53 3428.75 3922.83 12362 758008
head2 regexp_extract_all shared 4 8192 alternating 512 4194304 0.3701 11331550 2678.63 2493.38 3492.54 11371 806634
head2 regexp_extract_all shared 8 8192 warm 512 4194304 0.6715 6246289 10467.92 10449.42 12375.42 12362 758038
head2 regexp_extract_all shared 8 8192 alternating 512 4194304 0.5067 8277286 7505.49 7730.79 10730.96 11444 851801
head2 regexp_extract_all perworker 1 8192 warm 512 4194304 0.2814 14905489 549.35 545.75 598.25 12362 757971
head2 regexp_extract_all perworker 1 8192 alternating 512 4194304 0.2695 15561644 526.16 568.08 644.79 11724 1025736
head2 regexp_extract_all perworker 2 8192 warm 512 4194304 0.3005 13957348 1169.83 1167.96 1262.25 12362 758007
head2 regexp_extract_all perworker 2 8192 alternating 512 4194304 0.2582 16242672 950.61 909.58 1180.46 11145 665824
head2 regexp_extract_all perworker 4 8192 warm 512 4194304 0.4029 10410159 3144.10 3144.38 3467.75 12362 758016
head2 regexp_extract_all perworker 4 8192 alternating 512 4194304 0.3307 12683454 2460.97 2355.96 2987.33 11145 665874
head2 regexp_extract_all perworker 8 8192 warm 512 4194304 0.5531 7583855 8599.21 8555.92 10071.00 12362 758038
head2 regexp_extract_all perworker 8 8192 alternating 512 4194304 0.4813 8715239 7141.68 7201.96 10372.00 11145 665885
head2 split shared 1 512 warm 8192 4194304 0.7582 5531849 92.41 91.83 104.29 68 68233
head2 split shared 1 512 alternating 8192 4194304 0.6737 6225913 82.08 91.67 105.04 187 75200
head2 split shared 2 512 warm 8192 4194304 0.3901 10750714 94.90 94.38 104.33 68 68233
head2 split shared 2 512 alternating 8192 4194304 0.4001 10481967 83.17 87.58 106.46 149 70439
head2 split shared 4 512 warm 8192 4194304 0.1983 21153208 96.04 94.67 111.71 68 68233
head2 split shared 4 512 alternating 8192 4194304 0.2085 20115525 86.75 89.62 121.58 132 68269
head2 split shared 8 512 warm 8192 4194304 0.1438 29169050 128.59 98.88 214.42 68 68234
head2 split shared 8 512 alternating 8192 4194304 0.1436 29201475 129.20 110.17 276.96 129 67859
head2 split perworker 1 512 warm 8192 4194304 0.7572 5539180 92.29 91.62 104.75 68 68233
head2 split perworker 1 512 alternating 8192 4194304 0.6724 6237531 81.93 91.58 103.12 187 75202
head2 split perworker 2 512 warm 8192 4194304 0.3885 10795794 94.59 94.04 102.92 68 68233
head2 split perworker 2 512 alternating 8192 4194304 0.3835 10937555 78.92 87.21 99.71 67 60032
head2 split perworker 4 512 warm 8192 4194304 0.2006 20906188 95.54 94.04 113.67 68 68233
head2 split perworker 4 512 alternating 8192 4194304 0.1991 21068660 81.09 88.62 106.25 67 60032
head2 split perworker 8 512 warm 8192 4194304 0.1396 30044996 127.53 98.21 206.71 68 68230
head2 split perworker 8 512 alternating 8192 4194304 0.1287 32587306 104.64 95.67 208.29 68 60034
head2 split shared 1 8192 warm 512 4194304 0.7381 5682775 1441.26 1438.75 1495.38 76 1082390
head2 split shared 1 8192 alternating 512 4194304 0.6304 6653159 1231.00 1424.92 1494.21 195 966413
head2 split shared 2 8192 warm 512 4194304 0.3731 11241802 1455.82 1454.33 1495.62 76 1082390
head2 split shared 2 8192 alternating 512 4194304 0.3728 11249595 1236.14 1413.21 1495.12 158 961737
head2 split shared 4 8192 warm 512 4194304 0.1943 21585492 1498.49 1474.83 1733.67 76 1082392
head2 split shared 4 8192 alternating 512 4194304 0.1930 21727112 1282.46 1441.42 1722.42 126 957584
head2 split shared 8 8192 warm 512 4194304 0.1311 31992962 1966.19 1545.50 2942.88 76 1082394
head2 split shared 8 8192 alternating 512 4194304 0.1245 33679879 1618.36 1523.83 2963.67 124 957377
head2 split perworker 1 8192 warm 512 4194304 0.7377 5685676 1440.51 1437.38 1495.29 76 1082390
head2 split perworker 1 8192 alternating 512 4194304 0.6302 6655424 1230.58 1426.38 1494.75 195 966409
head2 split perworker 2 8192 warm 512 4194304 0.3722 11268824 1452.11 1450.79 1499.08 76 1082390
head2 split perworker 2 8192 alternating 512 4194304 0.3711 11302438 1231.43 1417.12 1484.88 75 951190
head2 split perworker 4 8192 warm 512 4194304 0.1952 21490626 1506.33 1477.62 1778.58 76 1082390
head2 split perworker 4 8192 alternating 512 4194304 0.1910 21963449 1265.31 1439.17 1567.42 75 951190
head2 split perworker 8 8192 warm 512 4194304 0.1297 32341663 1967.30 1536.04 2927.75 76 1081884
head2 split perworker 8 8192 alternating 512 4194304 0.1219 34414261 1592.85 1491.54 2926.67 75 951195
label function first_us_mean second_us_mean reps
head2 regexp_extract 394.35 337.53 30
head2 regexp_extract_all 604.21 565.31 30
head2 split 1454.50 1435.75 30
label function batch_rows regime hash
head2 regexp_extract 512 warm 48cf9292e3499221
head2 regexp_extract 512 alternating abc22eb5d1b7ff93
head2 regexp_extract 8192 warm 36022698d568939d
head2 regexp_extract 8192 alternating 2aad4e5fa2692c3b
head2 regexp_extract_all 512 warm b2dfff0b97f0fcb9
head2 regexp_extract_all 512 alternating 87eba83c99f6b34e
head2 regexp_extract_all 8192 warm 42115326f3672337
head2 regexp_extract_all 8192 alternating 46327f4061153e54
head2 split 512 warm 8549f20566101054
head2 split 512 alternating d6f1a26e828fcc75
head2 split 8192 warm 7aa75b4b5320f857
head2 split 8192 alternating 4bd6e6754eab8b91
label function sharing workers batch_rows regime batches rows_total wall_s rows_per_s mean_us p50_us p99_us allocs_per_batch bytes_per_batch
head regexp_extract shared 1 512 warm 8192 4194304 0.2336 17952222 28.43 28.33 33.21 82 48278
head regexp_extract shared 1 512 alternating 8192 4194304 0.4088 10260520 49.79 68.62 77.71 658 391170
head regexp_extract shared 2 512 warm 8192 4194304 0.1341 31283432 32.50 32.12 40.17 82 48278
head regexp_extract shared 2 512 alternating 8192 4194304 0.3763 11146058 91.55 81.75 169.54 654 388566
head regexp_extract shared 4 512 warm 8192 4194304 0.0788 53233401 37.78 37.12 49.67 82 48279
head regexp_extract shared 4 512 alternating 8192 4194304 0.3131 13397973 152.59 154.42 168.79 647 384081
head regexp_extract shared 8 512 warm 8192 4194304 0.0680 61721067 62.59 54.50 109.96 82 48271
head regexp_extract shared 8 512 alternating 8192 4194304 0.2925 14340110 284.45 310.46 408.96 584 344951
head regexp_extract perworker 1 512 warm 8192 4194304 0.2346 17877454 28.55 28.46 33.38 82 48278
head regexp_extract perworker 1 512 alternating 8192 4194304 0.4082 10274432 49.73 68.33 79.00 658 391170
head regexp_extract perworker 2 512 warm 8192 4194304 0.1342 31258670 32.56 32.08 41.38 82 48278
head regexp_extract perworker 2 512 alternating 8192 4194304 0.1220 34381408 25.51 25.33 34.71 79 30795
head regexp_extract perworker 4 512 warm 8192 4194304 0.0769 54512099 36.81 36.25 47.50 82 48278
head regexp_extract perworker 4 512 alternating 8192 4194304 0.0724 57961530 29.91 30.33 44.38 79 30795
head regexp_extract perworker 8 512 warm 8192 4194304 0.0668 62813704 61.82 56.08 106.08 82 48277
head regexp_extract perworker 8 512 alternating 8192 4194304 0.0604 69439724 50.96 45.58 106.21 79 30796
head regexp_extract shared 1 8192 warm 512 4194304 0.1871 22417920 365.23 363.21 392.54 82 144146
head regexp_extract shared 1 8192 alternating 512 4194304 0.1794 23378877 350.19 395.17 441.04 658 486688
head regexp_extract shared 2 8192 warm 512 4194304 0.1031 40682789 397.55 394.42 465.08 82 144158
head regexp_extract shared 2 8192 alternating 512 4194304 0.1032 40653527 347.82 346.25 455.08 482 377262
head regexp_extract shared 4 8192 warm 512 4194304 0.0539 77818386 415.41 415.54 486.04 82 144157
head regexp_extract shared 4 8192 alternating 512 4194304 0.0540 77716912 365.08 363.92 506.12 378 312519
head regexp_extract shared 8 8192 warm 512 4194304 0.0355 118138317 525.79 437.62 800.58 82 144330
head regexp_extract shared 8 8192 alternating 512 4194304 0.0347 120984885 492.85 443.29 951.08 380 314154
head regexp_extract perworker 1 8192 warm 512 4194304 0.1817 23081003 354.72 353.04 383.21 82 144137
head regexp_extract perworker 1 8192 alternating 512 4194304 0.1752 23936846 342.04 378.50 423.04 658 486688
head regexp_extract perworker 2 8192 warm 512 4194304 0.1007 41669100 388.75 382.88 461.29 82 144155
head regexp_extract perworker 2 8192 alternating 512 4194304 0.0937 44765127 323.27 334.08 400.04 79 126663
head regexp_extract perworker 4 8192 warm 512 4194304 0.0544 77081930 413.76 417.29 479.42 82 144157
head regexp_extract perworker 4 8192 alternating 512 4194304 0.0487 86106332 336.51 339.58 430.79 79 126674
head regexp_extract perworker 8 8192 warm 512 4194304 0.0375 111925210 506.51 417.08 770.50 82 144196
head regexp_extract perworker 8 8192 alternating 512 4194304 0.0319 131335183 432.18 386.25 743.42 79 126158
head regexp_extract_all shared 1 512 warm 8192 4194304 0.3315 12653000 40.32 40.04 46.67 862 86624
head regexp_extract_all shared 1 512 alternating 8192 4194304 0.5120 8192637 62.33 81.38 94.71 1355 425560
head regexp_extract_all shared 2 512 warm 8192 4194304 0.4697 8928972 114.28 113.58 129.29 862 86624
head regexp_extract_all shared 2 512 alternating 8192 4194304 0.4639 9041007 107.40 128.08 160.71 1195 325991
head regexp_extract_all shared 4 512 warm 8192 4194304 0.4801 8736943 233.36 233.08 293.33 862 86624
head regexp_extract_all shared 4 512 alternating 8192 4194304 0.5763 7277505 279.54 284.79 396.75 1317 401838
head regexp_extract_all shared 8 512 warm 8192 4194304 0.7257 5779808 703.23 610.71 931.75 862 86628
head regexp_extract_all shared 8 512 alternating 8192 4194304 0.6029 6957104 585.71 579.17 791.25 1243 355644
head regexp_extract_all perworker 1 512 warm 8192 4194304 0.3300 12709545 40.14 39.92 46.00 862 86624
head regexp_extract_all perworker 1 512 alternating 8192 4194304 0.5102 8220232 62.12 81.17 92.67 1355 425560
head regexp_extract_all perworker 2 512 warm 8192 4194304 0.3351 12518000 81.49 81.38 90.83 862 86624
head regexp_extract_all perworker 2 512 alternating 8192 4194304 0.2875 14587334 64.65 60.67 85.33 777 65184
head regexp_extract_all perworker 4 512 warm 8192 4194304 0.4284 9789692 207.90 208.88 234.96 862 86624
head regexp_extract_all perworker 4 512 alternating 8192 4194304 0.3642 11515349 168.67 162.46 213.83 777 65184
head regexp_extract_all perworker 8 512 warm 8192 4194304 0.6501 6452016 631.42 494.58 960.75 862 86625
head regexp_extract_all perworker 8 512 alternating 8192 4194304 0.5638 7439280 528.21 483.21 937.25 777 65187
head regexp_extract_all shared 1 8192 warm 512 4194304 0.2813 14908246 549.26 548.04 591.17 12362 757997
head regexp_extract_all shared 1 8192 alternating 512 4194304 0.2755 15224373 537.83 571.42 682.17 11724 1025736
head regexp_extract_all shared 2 8192 warm 512 4194304 0.4470 9384181 1742.62 1748.00 1839.04 12362 758008
head regexp_extract_all shared 2 8192 alternating 512 4194304 0.2666 15731201 966.18 905.88 1248.96 11570 930551
head regexp_extract_all shared 4 8192 warm 512 4194304 0.4410 9510356 3432.82 3427.88 3887.71 12362 758012
head regexp_extract_all shared 4 8192 alternating 512 4194304 0.3679 11400016 2655.42 2469.12 3422.58 11371 806750
head regexp_extract_all shared 8 8192 warm 512 4194304 0.6687 6272440 10379.40 10369.04 13158.58 12362 758037
head regexp_extract_all shared 8 8192 alternating 512 4194304 0.5342 7851603 7937.80 7707.75 13381.12 11428 841856
head regexp_extract_all perworker 1 8192 warm 512 4194304 0.2816 14894832 549.75 546.29 597.50 12362 757996
head regexp_extract_all perworker 1 8192 alternating 512 4194304 0.2689 15596458 525.01 570.83 633.50 11724 1025736
head regexp_extract_all perworker 2 8192 warm 512 4194304 0.3013 13921241 1172.75 1176.21 1254.33 12362 758008
head regexp_extract_all perworker 2 8192 alternating 512 4194304 0.2571 16313269 943.21 901.17 1163.42 11145 665833
head regexp_extract_all perworker 4 8192 warm 512 4194304 0.3895 10768441 3035.38 3033.88 3441.25 12362 758015
head regexp_extract_all perworker 4 8192 alternating 512 4194304 0.3317 12646302 2463.88 2347.71 3368.92 11145 665844
head regexp_extract_all perworker 8 8192 warm 512 4194304 0.5846 7174984 9086.29 8964.58 12944.50 12362 758049
head regexp_extract_all perworker 8 8192 alternating 512 4194304 0.5074 8265666 7578.78 7470.71 12556.33 11145 665881
head split shared 1 512 warm 8192 4194304 0.7523 5575042 91.72 91.08 103.92 68 68233
head split shared 1 512 alternating 8192 4194304 0.6716 6245390 81.84 91.46 104.54 187 75200
head split shared 2 512 warm 8192 4194304 0.3867 10846714 94.12 93.62 102.00 68 68233
head split shared 2 512 alternating 8192 4194304 0.3967 10573099 82.26 86.67 106.33 149 70380
head split shared 4 512 warm 8192 4194304 0.1973 21261936 94.77 93.79 105.17 68 68230
head split shared 4 512 alternating 8192 4194304 0.2049 20471216 85.35 88.92 120.46 133 68358
head split shared 8 512 warm 8192 4194304 0.1396 30040647 129.34 97.17 217.88 68 68230
head split shared 8 512 alternating 8192 4194304 0.1453 28862637 129.52 109.75 279.38 131 68091
head split perworker 1 512 warm 8192 4194304 0.7508 5586570 91.53 90.92 103.42 68 68233
head split perworker 1 512 alternating 8192 4194304 0.6726 6236308 81.96 91.42 105.00 187 75202
head split perworker 2 512 warm 8192 4194304 0.3843 10914786 93.56 93.04 102.00 68 68233
head split perworker 2 512 alternating 8192 4194304 0.3804 11025932 78.41 86.21 98.54 67 60032
head split perworker 4 512 warm 8192 4194304 0.1959 21407711 94.21 93.21 106.12 68 68233
head split perworker 4 512 alternating 8192 4194304 0.1945 21562822 78.77 87.83 100.58 67 60032
head split perworker 8 512 warm 8192 4194304 0.1389 30193503 126.88 97.21 209.46 68 68230
head split perworker 8 512 alternating 8192 4194304 0.1244 33715400 101.76 91.58 207.83 67 60030
head split shared 1 8192 warm 512 4194304 0.7293 5751000 1424.18 1421.29 1478.17 76 1082390
head split shared 1 8192 alternating 512 4194304 0.6231 6731582 1216.69 1406.00 1471.96 195 966409
head split shared 2 8192 warm 512 4194304 0.3694 11355837 1437.75 1436.83 1472.29 76 1082390
head split shared 2 8192 alternating 512 4194304 0.3680 11397212 1222.40 1399.54 1467.00 159 961797
head split shared 4 8192 warm 512 4194304 0.1896 22127429 1464.69 1448.17 1629.42 76 1082390
head split shared 4 8192 alternating 512 4194304 0.1888 22220904 1252.01 1419.46 1568.29 138 959130
head split shared 8 8192 warm 512 4194304 0.1297 32338546 1962.43 1525.92 2958.04 76 1082394
head split shared 8 8192 alternating 512 4194304 0.1181 35503128 1616.06 1498.00 2993.71 132 958498
head split perworker 1 8192 warm 512 4194304 0.7274 5766299 1420.40 1417.42 1478.79 76 1082390
head split perworker 1 8192 alternating 512 4194304 0.6217 6746160 1214.06 1404.88 1482.50 195 966423
head split perworker 2 8192 warm 512 4194304 0.3677 11406754 1434.13 1432.50 1474.42 76 1082390
head split perworker 2 8192 alternating 512 4194304 0.3660 11461089 1215.13 1398.50 1457.12 75 951190
head split perworker 4 8192 warm 512 4194304 0.1915 21896858 1472.80 1452.00 1703.08 76 1082390
head split perworker 4 8192 alternating 512 4194304 0.1883 22272168 1249.79 1412.00 1532.46 75 951190
head split perworker 8 8192 warm 512 4194304 0.1320 31772683 1940.04 1517.38 2967.88 76 1082393
head split perworker 8 8192 alternating 512 4194304 0.1208 34724629 1594.40 1491.38 2964.42 75 951205
label function first_us_mean second_us_mean reps
head regexp_extract 403.17 361.36 30
head regexp_extract_all 595.32 548.72 30
head split 1442.06 1423.86 30
label function batch_rows regime hash
head regexp_extract 512 warm 48cf9292e3499221
head regexp_extract 512 alternating abc22eb5d1b7ff93
head regexp_extract 8192 warm 36022698d568939d
head regexp_extract 8192 alternating 2aad4e5fa2692c3b
head regexp_extract_all 512 warm b2dfff0b97f0fcb9
head regexp_extract_all 512 alternating 87eba83c99f6b34e
head regexp_extract_all 8192 warm 42115326f3672337
head regexp_extract_all 8192 alternating 46327f4061153e54
head split 512 warm 8549f20566101054
head split 512 alternating d6f1a26e828fcc75
head split 8192 warm 7aa75b4b5320f857
head split 8192 alternating 4bd6e6754eab8b91
label function sharing workers batch_rows regime batches rows_total wall_s rows_per_s mean_us p50_us p99_us allocs_per_batch bytes_per_batch
headfix regexp_extract shared 1 512 warm 8192 4194304 0.2302 18218965 28.02 28.12 31.38 82 48278
headfix regexp_extract shared 1 512 alternating 8192 4194304 0.4168 10062197 50.79 65.67 79.00 658 391170
headfix regexp_extract shared 2 512 warm 8192 4194304 0.1393 30101760 33.77 33.38 42.38 82 48280
headfix regexp_extract shared 2 512 alternating 8192 4194304 0.3368 12452568 81.93 82.38 89.83 655 389622
headfix regexp_extract shared 4 512 warm 8192 4194304 0.0787 53289481 37.79 37.21 49.33 82 48278
headfix regexp_extract shared 4 512 alternating 8192 4194304 0.3155 13293155 153.74 154.08 168.25 654 388478
headfix regexp_extract shared 8 512 warm 8192 4194304 0.0660 63555076 60.85 52.58 106.17 82 48279
headfix regexp_extract shared 8 512 alternating 8192 4194304 0.3188 13155309 310.76 311.50 337.96 653 387865
headfix regexp_extract perworker 1 512 warm 8192 4194304 0.2298 18250295 27.97 28.12 29.88 82 48278
headfix regexp_extract perworker 1 512 alternating 8192 4194304 0.4130 10154721 50.34 66.79 75.79 658 391170
headfix regexp_extract perworker 2 512 warm 8192 4194304 0.1348 31116977 32.65 32.21 40.00 82 48278
headfix regexp_extract perworker 2 512 alternating 8192 4194304 0.1243 33742072 25.77 25.42 34.75 79 30795
headfix regexp_extract perworker 4 512 warm 8192 4194304 0.0763 54951503 36.49 36.08 46.04 82 48278
headfix regexp_extract perworker 4 512 alternating 8192 4194304 0.0722 58110392 30.17 30.38 42.54 79 30795
headfix regexp_extract perworker 8 512 warm 8192 4194304 0.0662 63367439 60.61 54.83 103.17 82 48279
headfix regexp_extract perworker 8 512 alternating 8192 4194304 0.0588 71292398 50.44 45.33 102.92 79 30796
headfix regexp_extract shared 1 8192 warm 512 4194304 0.1839 22807009 359.00 364.38 379.54 82 144146
headfix regexp_extract shared 1 8192 alternating 512 4194304 0.1744 24043572 340.52 360.96 426.58 658 486688
headfix regexp_extract shared 2 8192 warm 512 4194304 0.1018 41197857 395.79 389.46 479.46 82 144157
headfix regexp_extract shared 2 8192 alternating 512 4194304 0.1030 40723770 348.32 344.54 452.38 486 380087
headfix regexp_extract shared 4 8192 warm 512 4194304 0.0576 72845548 440.04 435.92 546.88 82 144157
headfix regexp_extract shared 4 8192 alternating 512 4194304 0.0535 78367200 362.61 371.17 498.29 328 281539
headfix regexp_extract shared 8 8192 warm 512 4194304 0.0498 84254836 708.30 628.46 1323.96 82 144177
headfix regexp_extract shared 8 8192 alternating 512 4194304 0.0350 119938921 498.59 445.67 1039.88 391 320986
headfix regexp_extract perworker 1 8192 warm 512 4194304 0.1829 22927519 357.12 363.67 379.88 82 144146
headfix regexp_extract perworker 1 8192 alternating 512 4194304 0.1735 24176254 338.66 359.46 424.92 658 486688
headfix regexp_extract perworker 2 8192 warm 512 4194304 0.1026 40871790 399.70 390.67 469.88 82 144147
headfix regexp_extract perworker 2 8192 alternating 512 4194304 0.0951 44115141 329.73 332.29 403.38 79 126663
headfix regexp_extract perworker 4 8192 warm 512 4194304 0.0541 77595540 412.77 411.88 466.88 82 144164
headfix regexp_extract perworker 4 8192 alternating 512 4194304 0.0512 81894408 351.16 359.33 444.12 79 126674
headfix regexp_extract perworker 8 8192 warm 512 4194304 0.0442 94932773 573.58 509.96 1113.46 82 144394
headfix regexp_extract perworker 8 8192 alternating 512 4194304 0.0369 113622512 447.98 384.54 981.12 79 126703
headfix regexp_extract_all shared 1 512 warm 8192 4194304 0.3713 11294884 45.18 45.04 50.38 157 90037
headfix regexp_extract_all shared 1 512 alternating 8192 4194304 0.5313 7893887 64.70 82.21 99.21 730 415444
headfix regexp_extract_all shared 2 512 warm 8192 4194304 0.2166 19363029 52.44 51.58 64.83 157 90037
headfix regexp_extract_all shared 2 512 alternating 8192 4194304 0.3161 13269718 65.93 69.12 114.25 449 240377
headfix regexp_extract_all shared 4 512 warm 8192 4194304 0.1279 32799700 60.55 60.17 77.29 157 90038
headfix regexp_extract_all shared 4 512 alternating 8192 4194304 0.2771 15138458 128.28 112.33 193.17 537 295110
headfix regexp_extract_all shared 8 512 warm 8192 4194304 0.1176 35679857 105.55 94.25 188.79 157 90039
headfix regexp_extract_all shared 8 512 alternating 8192 4194304 0.2827 14836433 272.15 280.58 485.33 557 307683
headfix regexp_extract_all perworker 1 512 warm 8192 4194304 0.3741 11210794 45.53 45.50 50.79 157 90037
headfix regexp_extract_all perworker 1 512 alternating 8192 4194304 0.5295 7921609 64.47 82.46 96.71 730 415444
headfix regexp_extract_all perworker 2 512 warm 8192 4194304 0.2091 20058063 50.56 49.75 62.38 157 90037
headfix regexp_extract_all perworker 2 512 alternating 8192 4194304 0.1980 21185128 39.99 40.71 55.83 152 55068
headfix regexp_extract_all perworker 4 512 warm 8192 4194304 0.1228 34153855 58.63 58.42 73.21 157 90038
headfix regexp_extract_all perworker 4 512 alternating 8192 4194304 0.1155 36303058 46.74 47.08 70.33 152 55068
headfix regexp_extract_all perworker 8 512 warm 8192 4194304 0.1154 36339399 108.40 100.12 185.08 157 90034
headfix regexp_extract_all perworker 8 512 alternating 8192 4194304 0.1027 40822861 90.00 84.62 179.21 152 55069
headfix regexp_extract_all shared 1 8192 warm 512 4194304 0.2744 15286792 535.64 537.12 573.21 164 209710
headfix regexp_extract_all shared 1 8192 alternating 512 4194304 0.2513 16691192 490.56 539.50 607.58 737 518244
headfix regexp_extract_all shared 2 8192 warm 512 4194304 0.1511 27752710 581.43 571.38 699.04 164 209721
headfix regexp_extract_all shared 2 8192 alternating 512 4194304 0.1521 27568585 509.26 505.29 693.08 556 406124
headfix regexp_extract_all shared 4 8192 warm 512 4194304 0.0797 52656925 614.93 610.75 766.42 164 209734
headfix regexp_extract_all shared 4 8192 alternating 512 4194304 0.0785 53454202 523.25 534.54 778.83 427 325887
headfix regexp_extract_all shared 8 8192 warm 512 4194304 0.0579 72434281 851.95 624.04 1882.00 163 208958
headfix regexp_extract_all shared 8 8192 alternating 512 4194304 0.0518 80902855 708.62 623.21 1539.67 488 364018
headfix regexp_extract_all perworker 1 8192 warm 512 4194304 0.2733 15346252 533.56 535.62 562.12 164 209710
headfix regexp_extract_all perworker 1 8192 alternating 512 4194304 0.2513 16693230 490.47 542.71 632.96 737 518244
headfix regexp_extract_all perworker 2 8192 warm 512 4194304 0.1529 27423020 592.43 586.29 720.92 164 209721
headfix regexp_extract_all perworker 2 8192 alternating 512 4194304 0.1424 29460882 490.71 505.88 651.92 158 158341
headfix regexp_extract_all perworker 4 8192 warm 512 4194304 0.0787 53306779 597.65 587.50 737.00 164 209734
headfix regexp_extract_all perworker 4 8192 alternating 512 4194304 0.0769 54533450 517.11 516.46 731.38 158 158365
headfix regexp_extract_all perworker 8 8192 warm 512 4194304 0.0565 74203237 861.32 724.50 1680.21 164 209227
headfix regexp_extract_all perworker 8 8192 alternating 512 4194304 0.0524 80017756 698.40 584.17 1459.75 158 158391
headfix split shared 1 512 warm 8192 4194304 0.7575 5537090 92.33 91.92 100.62 68 68233
headfix split shared 1 512 alternating 8192 4194304 0.6710 6250858 81.74 87.96 102.25 187 75202
headfix split shared 2 512 warm 8192 4194304 0.3911 10725012 95.02 94.50 101.54 68 68233
headfix split shared 2 512 alternating 8192 4194304 0.4022 10428647 83.14 86.46 106.67 148 70291
headfix split shared 4 512 warm 8192 4194304 0.1999 20979724 94.88 94.54 103.92 68 68233
headfix split shared 4 512 alternating 8192 4194304 0.2072 20243049 85.71 89.29 117.21 133 68310
headfix split shared 8 512 warm 8192 4194304 0.1392 30129555 127.11 97.17 211.21 68 68230
headfix split shared 8 512 alternating 8192 4194304 0.1442 29082738 129.57 110.79 280.25 129 67892
headfix split perworker 1 512 warm 8192 4194304 0.7591 5525526 92.52 92.08 101.29 68 68233
headfix split perworker 1 512 alternating 8192 4194304 0.6710 6250913 81.70 88.33 101.79 187 75200
headfix split perworker 2 512 warm 8192 4194304 0.3894 10771601 94.79 94.29 101.46 68 68233
headfix split perworker 2 512 alternating 8192 4194304 0.3832 10944495 78.90 84.54 98.67 67 60032
headfix split perworker 4 512 warm 8192 4194304 0.1973 21262515 94.70 94.12 102.42 68 68233
headfix split perworker 4 512 alternating 8192 4194304 0.1973 21260580 79.23 87.79 100.88 67 60032
headfix split perworker 8 512 warm 8192 4194304 0.1375 30501617 130.33 100.21 214.00 68 68230
headfix split perworker 8 512 alternating 8192 4194304 0.1238 33889155 103.65 93.83 206.17 67 60026
headfix split shared 1 8192 warm 512 4194304 0.7370 5691186 1439.14 1439.00 1473.38 76 1082390
headfix split shared 1 8192 alternating 512 4194304 0.6443 6510359 1258.02 1416.12 1549.38 195 966419
headfix split shared 2 8192 warm 512 4194304 0.3744 11202723 1460.52 1458.25 1509.54 76 1082390
headfix split shared 2 8192 alternating 512 4194304 0.3720 11275632 1234.18 1410.21 1482.67 158 961738
headfix split shared 4 8192 warm 512 4194304 0.1893 22152268 1461.77 1461.79 1494.88 76 1082390
headfix split shared 4 8192 alternating 512 4194304 0.1892 22167625 1243.44 1427.75 1504.25 137 958952
headfix split shared 8 8192 warm 512 4194304 0.1288 32575758 1941.60 1480.67 2959.08 76 1082394
headfix split shared 8 8192 alternating 512 4194304 0.1187 35327532 1580.86 1475.88 2968.38 127 957791
headfix split perworker 1 8192 warm 512 4194304 0.7376 5686677 1440.27 1440.21 1477.00 76 1082390
headfix split perworker 1 8192 alternating 512 4194304 0.6286 6672891 1227.35 1420.42 1483.54 195 966423
headfix split perworker 2 8192 warm 512 4194304 0.3744 11203148 1457.35 1457.58 1487.33 76 1082390
headfix split perworker 2 8192 alternating 512 4194304 0.3722 11267732 1230.29 1404.00 1482.58 75 951190
headfix split perworker 4 8192 warm 512 4194304 0.1894 22141768 1460.83 1459.67 1492.42 76 1082390
headfix split perworker 4 8192 alternating 512 4194304 0.1872 22399692 1235.79 1438.21 1481.58 75 951190
headfix split perworker 8 8192 warm 512 4194304 0.1292 32470868 1937.01 1476.33 2969.33 76 1081901
headfix split perworker 8 8192 alternating 512 4194304 0.1193 35167188 1579.78 1463.33 2957.75 75 950685
label function first_us_mean second_us_mean reps
headfix regexp_extract 413.60 360.76 30
headfix regexp_extract_all 606.39 545.87 30
headfix split 1434.32 1423.72 30
label function batch_rows regime hash
headfix regexp_extract 512 warm 48cf9292e3499221
headfix regexp_extract 512 alternating abc22eb5d1b7ff93
headfix regexp_extract 8192 warm 36022698d568939d
headfix regexp_extract 8192 alternating 2aad4e5fa2692c3b
headfix regexp_extract_all 512 warm b2dfff0b97f0fcb9
headfix regexp_extract_all 512 alternating 87eba83c99f6b34e
headfix regexp_extract_all 8192 warm 42115326f3672337
headfix regexp_extract_all 8192 alternating 46327f4061153e54
headfix split 512 warm 8549f20566101054
headfix split 512 alternating d6f1a26e828fcc75
headfix split 8192 warm 7aa75b4b5320f857
headfix split 8192 alternating 4bd6e6754eab8b91
// Multithreaded benchmark for datafusion-comet PR #5612 (per-expression regex PatternCache).
//
// Invokes the native spark-expr scalar UDFs exactly the way the engine does: the UDF is
// built via `create_comet_physical_fun` (the serde entry point, stable across BASE and
// HEAD) and evaluated via `ScalarUDF::invoke_with_args`, with the regex pattern supplied
// as a scalar argument on every invoke (which is why construction-time hoisting is
// impossible and the cache exists). No HEAD-only symbols are used, so the identical
// harness builds against both commits.
//
// Matrix: {regexp_extract, regexp_extract_all, split} x {shared, perworker} x
// {1,2,4,8 workers} x {512, 8192 rows} x {warm, alternating}.
// Plus: cold first-invoke cost per function, and a correctness pass that hashes outputs
// so BASE and HEAD can be asserted identical on identical seeded input.
use std::alloc::{GlobalAlloc, Layout, System};
use std::hint::black_box;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Barrier};
use std::time::Instant;
use arrow::array::builder::StringBuilder;
use arrow::array::{Array, ArrayRef, ListArray, StringArray};
use arrow::datatypes::{DataType, Field};
use datafusion::common::ScalarValue;
use datafusion::config::ConfigOptions;
use datafusion::logical_expr::{ScalarFunctionArgs, ScalarUDF};
use datafusion::physical_plan::ColumnarValue;
use datafusion::prelude::SessionContext;
use datafusion_comet_spark_expr::create_comet_physical_fun;
// ---------------------------------------------------------------------------
// Counting global allocator (allocation count + requested bytes).
// ---------------------------------------------------------------------------
struct CountingAlloc;
static ALLOC_COUNT: AtomicU64 = AtomicU64::new(0);
static ALLOC_BYTES: AtomicU64 = AtomicU64::new(0);
unsafe impl GlobalAlloc for CountingAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
ALLOC_COUNT.fetch_add(1, Ordering::Relaxed);
ALLOC_BYTES.fetch_add(layout.size() as u64, Ordering::Relaxed);
System.alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout)
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
ALLOC_COUNT.fetch_add(1, Ordering::Relaxed);
ALLOC_BYTES.fetch_add(layout.size() as u64, Ordering::Relaxed);
System.alloc_zeroed(layout)
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
ALLOC_COUNT.fetch_add(1, Ordering::Relaxed);
ALLOC_BYTES.fetch_add(new_size as u64, Ordering::Relaxed);
System.realloc(ptr, layout, new_size)
}
}
#[global_allocator]
static GLOBAL: CountingAlloc = CountingAlloc;
fn alloc_snapshot() -> (u64, u64) {
(
ALLOC_COUNT.load(Ordering::Relaxed),
ALLOC_BYTES.load(Ordering::Relaxed),
)
}
// ---------------------------------------------------------------------------
// Deterministic seeded data generation (identical for BASE and HEAD).
// ---------------------------------------------------------------------------
struct Lcg(u64);
impl Lcg {
fn new(seed: u64) -> Self {
Lcg(seed.wrapping_mul(0x9E3779B97F4A7C15).wrapping_add(1))
}
fn next(&mut self) -> u64 {
self.0 = self
.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
self.0 >> 33
}
}
const LETTERS: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
fn rand_word(rng: &mut Lcg, min: usize, max: usize) -> String {
let len = min + (rng.next() as usize) % (max - min + 1);
(0..len)
.map(|_| LETTERS[(rng.next() as usize) % 26] as char)
.collect()
}
/// Input for regexp_extract / regexp_extract_all: ~10% nulls, ~30% alpha-underscore
/// strings ("foo_bar", matched by the alternating pattern), rest digit-dash strings
/// ("123-4567", matched by the warm pattern).
fn make_extract_batch(rows: usize, seed: u64) -> ArrayRef {
let mut rng = Lcg::new(seed);
let mut b = StringBuilder::new();
for _ in 0..rows {
let r = rng.next() % 10;
if r == 0 {
b.append_null();
} else if r <= 3 {
b.append_value(format!(
"{}_{}",
rand_word(&mut rng, 3, 8),
rand_word(&mut rng, 3, 8)
));
} else {
b.append_value(format!("{:03}-{:04}", rng.next() % 1000, rng.next() % 10000));
}
}
Arc::new(b.finish())
}
/// Input for split: ~10% nulls, rest 3..8 random words joined by mixed delimiters
/// out of ",", ";", "|" (all matched by "[,;|]+"; only ";" and "|" by the alt pattern).
fn make_split_batch(rows: usize, seed: u64) -> ArrayRef {
let mut rng = Lcg::new(seed);
let mut b = StringBuilder::new();
let delims = [",", ";", "|", ";;", ",,"];
for _ in 0..rows {
if rng.next() % 10 == 0 {
b.append_null();
} else {
let n = 3 + (rng.next() as usize) % 6;
let mut s = String::new();
for i in 0..n {
if i > 0 {
s.push_str(delims[(rng.next() as usize) % delims.len()]);
}
s.push_str(&rand_word(&mut rng, 2, 9));
}
b.append_value(s);
}
}
Arc::new(b.finish())
}
// ---------------------------------------------------------------------------
// Function descriptors.
// ---------------------------------------------------------------------------
#[derive(Clone, Copy, PartialEq)]
enum InputKind {
Extract,
Split,
}
struct FuncSpec {
name: &'static str,
input: InputKind,
warm_pattern: &'static str,
alt_pattern: &'static str,
/// Third scalar argument (group index or split limit).
extra: ScalarValue,
return_type: DataType,
}
fn func_specs() -> Vec<FuncSpec> {
let list_ty = DataType::List(Arc::new(Field::new("item", DataType::Utf8, true)));
vec![
FuncSpec {
name: "regexp_extract",
input: InputKind::Extract,
warm_pattern: r"(\d+)-(\d+)",
alt_pattern: r"([a-z]+)_([a-z]+)",
extra: ScalarValue::Int32(Some(1)),
return_type: DataType::Utf8,
},
FuncSpec {
name: "regexp_extract_all",
input: InputKind::Extract,
warm_pattern: r"(\d+)-(\d+)",
alt_pattern: r"([a-z]+)_([a-z]+)",
extra: ScalarValue::Int32(Some(1)),
return_type: list_ty.clone(),
},
FuncSpec {
name: "split",
input: InputKind::Split,
warm_pattern: r"[,;|]+",
alt_pattern: r"[;|]+",
extra: ScalarValue::Int32(Some(-1)),
return_type: list_ty,
},
]
}
fn make_udf(spec: &FuncSpec, ctx: &SessionContext) -> Arc<ScalarUDF> {
create_comet_physical_fun(spec.name, spec.return_type.clone(), &ctx.state(), None)
.expect("create_comet_physical_fun failed")
}
fn invoke(
udf: &ScalarUDF,
batch: &ArrayRef,
pattern: &ScalarValue,
extra: &ScalarValue,
return_field: &Arc<Field>,
config: &Arc<ConfigOptions>,
) -> ColumnarValue {
let args = ScalarFunctionArgs {
args: vec![
ColumnarValue::Array(Arc::clone(batch)),
ColumnarValue::Scalar(pattern.clone()),
ColumnarValue::Scalar(extra.clone()),
],
arg_fields: vec![],
number_rows: batch.len(),
return_field: Arc::clone(return_field),
config_options: Arc::clone(config),
};
udf.invoke_with_args(args).expect("invoke failed")
}
// ---------------------------------------------------------------------------
// Output hashing for the BASE-vs-HEAD equality check (FNV-1a 64).
// ---------------------------------------------------------------------------
struct Fnv(u64);
impl Fnv {
fn new() -> Self {
Fnv(0xcbf29ce484222325)
}
fn bytes(&mut self, b: &[u8]) {
for &x in b {
self.0 ^= x as u64;
self.0 = self.0.wrapping_mul(0x100000001b3);
}
}
fn marker(&mut self, m: u8) {
self.bytes(&[0xFE, m]);
}
}
fn hash_string_array(h: &mut Fnv, a: &StringArray) {
for i in 0..a.len() {
if a.is_null(i) {
h.marker(0);
} else {
h.marker(1);
h.bytes(a.value(i).as_bytes());
}
}
}
fn hash_output(h: &mut Fnv, v: &ColumnarValue) {
match v {
ColumnarValue::Array(arr) => match arr.data_type() {
DataType::Utf8 => {
hash_string_array(h, arr.as_any().downcast_ref::<StringArray>().unwrap())
}
DataType::List(_) => {
let la = arr.as_any().downcast_ref::<ListArray>().unwrap();
for i in 0..la.len() {
if la.is_null(i) {
h.marker(2);
} else {
h.marker(3);
let inner = la.value(i);
hash_string_array(
h,
inner.as_any().downcast_ref::<StringArray>().unwrap(),
);
}
}
}
other => panic!("unexpected output type {other:?}"),
},
ColumnarValue::Scalar(s) => h.bytes(format!("{s:?}").as_bytes()),
}
}
// ---------------------------------------------------------------------------
// Benchmark cells.
// ---------------------------------------------------------------------------
const POOL_SIZE: usize = 16;
const ROWS_TARGET: usize = 4 * 1024 * 1024;
fn batch_pool(kind: InputKind, rows: usize) -> Arc<Vec<ArrayRef>> {
let base_seed = match kind {
InputKind::Extract => 0xE0000 + rows as u64,
InputKind::Split => 0x50000 + rows as u64,
};
Arc::new(
(0..POOL_SIZE)
.map(|i| match kind {
InputKind::Extract => make_extract_batch(rows, base_seed + 31 * i as u64),
InputKind::Split => make_split_batch(rows, base_seed + 31 * i as u64),
})
.collect(),
)
}
struct CellResult {
batches: usize,
rows_total: usize,
wall_s: f64,
mean_us: f64,
p50_us: f64,
p99_us: f64,
allocs_per_batch: f64,
bytes_per_batch: f64,
}
#[allow(clippy::too_many_arguments)]
fn run_cell(
spec: &FuncSpec,
ctx: &SessionContext,
pool: &Arc<Vec<ArrayRef>>,
shared: bool,
workers: usize,
batch_rows: usize,
alternating: bool,
) -> CellResult {
let nbatches = (ROWS_TARGET / batch_rows).max(64);
let return_field = Arc::new(Field::new("out", spec.return_type.clone(), true));
let config = Arc::new(ConfigOptions::default());
let p0 = ScalarValue::Utf8(Some(spec.warm_pattern.to_string()));
let p1 = ScalarValue::Utf8(Some(spec.alt_pattern.to_string()));
// One UDF for all workers (contended cache) or one per worker (planner-typical).
let shared_udf = if shared { Some(make_udf(spec, ctx)) } else { None };
let barrier = Arc::new(Barrier::new(workers + 1));
let mut handles = Vec::with_capacity(workers);
for w in 0..workers {
let udf = shared_udf
.as_ref()
.map(Arc::clone)
.unwrap_or_else(|| make_udf(spec, ctx));
let pool = Arc::clone(pool);
let barrier = Arc::clone(&barrier);
let return_field = Arc::clone(&return_field);
let config = Arc::clone(&config);
let (p0, p1) = (p0.clone(), p1.clone());
let extra = spec.extra.clone();
// Global batch indices w, w+workers, ... so alternating interleaves across
// workers on the shared instance.
let mine: Vec<usize> = (w..nbatches).step_by(workers).collect();
handles.push(std::thread::spawn(move || {
// Warmup (also puts warm-regime caches into steady state).
for gi in mine.iter().take(16) {
let pat = if alternating && gi % 2 == 1 { &p1 } else { &p0 };
let out = invoke(
&udf,
&pool[gi % POOL_SIZE],
pat,
&extra,
&return_field,
&config,
);
black_box(&out);
}
let mut lat = Vec::with_capacity(mine.len());
barrier.wait();
for gi in &mine {
let pat = if alternating && gi % 2 == 1 { &p1 } else { &p0 };
let t0 = Instant::now();
let out = invoke(
&udf,
&pool[gi % POOL_SIZE],
pat,
&extra,
&return_field,
&config,
);
lat.push(t0.elapsed().as_nanos() as u64);
black_box(&out);
}
lat
}));
}
barrier.wait();
let (a0, b0) = alloc_snapshot();
let t0 = Instant::now();
let mut lats: Vec<u64> = Vec::with_capacity(nbatches);
for h in handles {
lats.extend(h.join().expect("worker panicked"));
}
let wall_s = t0.elapsed().as_secs_f64();
let (a1, b1) = alloc_snapshot();
lats.sort_unstable();
let n = lats.len();
let mean_us = lats.iter().sum::<u64>() as f64 / n as f64 / 1000.0;
let p50_us = lats[n / 2] as f64 / 1000.0;
let p99_us = lats[(n * 99 / 100).min(n - 1)] as f64 / 1000.0;
CellResult {
batches: n,
rows_total: n * batch_rows,
wall_s,
mean_us,
p50_us,
p99_us,
allocs_per_batch: (a1 - a0) as f64 / n as f64,
bytes_per_batch: (b1 - b0) as f64 / n as f64,
}
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
fn main() {
let mut args = std::env::args().skip(1);
let label = args.next().expect("usage: bench5612 <label> <out_dir>");
let out_dir = args.next().expect("usage: bench5612 <label> <out_dir>");
std::fs::create_dir_all(&out_dir).unwrap();
let ctx = SessionContext::new();
let specs = func_specs();
// ---------------- correctness hashes (untimed) ----------------
let mut verify_csv = String::from("label,function,batch_rows,regime,hash\n");
for spec in &specs {
for &rows in &[512usize, 8192] {
let pool = batch_pool(spec.input, rows);
for &alternating in &[false, true] {
let udf = make_udf(spec, &ctx);
let return_field = Arc::new(Field::new("out", spec.return_type.clone(), true));
let config = Arc::new(ConfigOptions::default());
let p0 = ScalarValue::Utf8(Some(spec.warm_pattern.to_string()));
let p1 = ScalarValue::Utf8(Some(spec.alt_pattern.to_string()));
let mut h = Fnv::new();
for (gi, batch) in pool.iter().enumerate() {
let pat = if alternating && gi % 2 == 1 { &p1 } else { &p0 };
let out = invoke(&udf, batch, pat, &spec.extra, &return_field, &config);
hash_output(&mut h, &out);
}
verify_csv.push_str(&format!(
"{},{},{},{},{:016x}\n",
label,
spec.name,
rows,
if alternating { "alternating" } else { "warm" },
h.0
));
}
}
}
std::fs::write(format!("{out_dir}/{label}_verify.csv"), &verify_csv).unwrap();
eprintln!("[{label}] verify hashes written");
// ---------------- cold first-invoke cost ----------------
let mut cold_csv = String::from("label,function,first_us_mean,second_us_mean,reps\n");
for spec in &specs {
let pool = batch_pool(spec.input, 8192);
let return_field = Arc::new(Field::new("out", spec.return_type.clone(), true));
let config = Arc::new(ConfigOptions::default());
let p0 = ScalarValue::Utf8(Some(spec.warm_pattern.to_string()));
let reps = 30usize;
let (mut first_ns, mut second_ns) = (0u64, 0u64);
for r in 0..reps {
let udf = make_udf(spec, &ctx); // fresh planned expression => cold cache
let batch = &pool[r % POOL_SIZE];
let t0 = Instant::now();
black_box(&invoke(&udf, batch, &p0, &spec.extra, &return_field, &config));
first_ns += t0.elapsed().as_nanos() as u64;
let t1 = Instant::now();
black_box(&invoke(&udf, batch, &p0, &spec.extra, &return_field, &config));
second_ns += t1.elapsed().as_nanos() as u64;
}
cold_csv.push_str(&format!(
"{},{},{:.2},{:.2},{}\n",
label,
spec.name,
first_ns as f64 / reps as f64 / 1000.0,
second_ns as f64 / reps as f64 / 1000.0,
reps
));
}
std::fs::write(format!("{out_dir}/{label}_cold.csv"), &cold_csv).unwrap();
eprintln!("[{label}] cold costs written");
// ---------------- main matrix ----------------
let mut csv = String::from(
"label,function,sharing,workers,batch_rows,regime,batches,rows_total,wall_s,\
rows_per_s,mean_us,p50_us,p99_us,allocs_per_batch,bytes_per_batch\n",
);
let overall = Instant::now();
for spec in &specs {
for &rows in &[512usize, 8192] {
let pool = batch_pool(spec.input, rows);
for &shared in &[true, false] {
for &workers in &[1usize, 2, 4, 8] {
for &alternating in &[false, true] {
let r = run_cell(spec, &ctx, &pool, shared, workers, rows, alternating);
let sharing = if shared { "shared" } else { "perworker" };
let regime = if alternating { "alternating" } else { "warm" };
csv.push_str(&format!(
"{},{},{},{},{},{},{},{},{:.4},{:.0},{:.2},{:.2},{:.2},{:.0},{:.0}\n",
label,
spec.name,
sharing,
workers,
rows,
regime,
r.batches,
r.rows_total,
r.wall_s,
r.rows_total as f64 / r.wall_s,
r.mean_us,
r.p50_us,
r.p99_us,
r.allocs_per_batch,
r.bytes_per_batch
));
eprintln!(
"[{label}] {} {} w={} b={} {}: {:.0} rows/s (wall {:.2}s)",
spec.name,
sharing,
workers,
rows,
regime,
r.rows_total as f64 / r.wall_s,
r.wall_s
);
}
}
}
}
}
std::fs::write(format!("{out_dir}/{label}_cells.csv"), &csv).unwrap();
eprintln!(
"[{label}] done in {:.1}s total measured wall",
overall.elapsed().as_secs_f64()
);
}
// Task B: isolate the mechanism of the HEAD shared-instance slowdown seen on
// regexp_extract_all warm/8192 (HEAD-shared 6.27 vs HEAD-perworker 7.17 Mrows/s at 8
// workers; 9.38 vs 13.92 at 2 workers; BASE shared == perworker at ~6.7/13.1).
//
// Fact (regex-automata 0.4.16, src/meta/regex.rs:1916-1926): `impl Clone for Regex`
// does Arc::clone on the compiled program but Pool::new for the caches, so every
// clone has a PRIVATE fresh scratch pool. Sharing the compiled program does not share
// scratch state. So the candidate deltas in shared mode are only: the single Mutex
// cache line, the shared Arc refcount cache line touched by every clone/drop, and
// reading the one shared program's memory from all threads.
//
// Modes (each thread does the same number of "invokes"; one invoke = optionally
// obtain a Regex, then run captures_iter over one 8192-row batch, like extract_all):
// compile_per_invoke Regex::new every invoke (= BASE)
// shared_mutex lock shared Mutex slot, compare, clone, unlock (= HEAD shared)
// private_mutex same but per-thread Mutex slot (= HEAD perworker)
// shared_clone clone from one shared Arc<Regex>, no mutex
// private_clone clone from a per-thread Regex, no mutex
// private_hoisted per-thread Regex used directly, no per-invoke clone
// shared_hoisted one shared Regex used directly by all threads (shared pool)
//
// Usage: shared_clone [invokes_per_thread]
use regex::Regex;
use std::hint::black_box;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Barrier, Mutex};
use std::time::Instant;
struct Lcg(u64);
impl Lcg {
fn new(seed: u64) -> Self {
Lcg(seed.wrapping_mul(0x9E3779B97F4A7C15).wrapping_add(1))
}
fn next(&mut self) -> u64 {
self.0 = self
.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
self.0 >> 33
}
}
const LETTERS: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
fn rand_word(rng: &mut Lcg, min: usize, max: usize) -> String {
let len = min + (rng.next() as usize) % (max - min + 1);
(0..len)
.map(|_| LETTERS[(rng.next() as usize) % 26] as char)
.collect()
}
/// Same distribution as the main harness extract input (nulls become skipped rows).
fn make_rows(rows: usize, seed: u64) -> Vec<Option<String>> {
let mut rng = Lcg::new(seed);
(0..rows)
.map(|_| {
let r = rng.next() % 10;
if r == 0 {
None
} else if r <= 3 {
Some(format!(
"{}_{}",
rand_word(&mut rng, 3, 8),
rand_word(&mut rng, 3, 8)
))
} else {
Some(format!("{:03}-{:04}", rng.next() % 1000, rng.next() % 10000))
}
})
.collect()
}
const PATTERN: &str = r"(\d+)-(\d+)";
const ROWS: usize = 8192;
/// One batch worth of extract_all-like work.
fn scan(re: &Regex, rows: &[Option<String>]) -> u64 {
let mut acc = 0u64;
for r in rows.iter().flatten() {
for caps in re.captures_iter(r) {
if let Some(m) = caps.get(1) {
acc = acc.wrapping_add(m.len() as u64);
}
}
}
acc
}
#[derive(Clone, Copy, PartialEq)]
enum Mode {
CompilePerInvoke,
SharedMutex,
PrivateMutex,
SharedClone,
PrivateClone,
PrivateHoisted,
SharedHoisted,
/// One true Regex::clone from the shared source per THREAD (hoisted out of the
/// loop): private scratch pool, but the compiled program is shared. Discriminates
/// per-search program-sharing contention from per-invoke clone contention.
SharedCloneHoisted,
/// Just Regex::clone from the shared source in a loop, no scanning: prices the
/// clone itself under cross-thread sharing.
CloneOnlyShared,
/// Same clone loop from a per-thread source.
CloneOnlyPrivate,
}
impl Mode {
fn name(self) -> &'static str {
match self {
Mode::CompilePerInvoke => "compile_per_invoke",
Mode::SharedMutex => "shared_mutex",
Mode::PrivateMutex => "private_mutex",
Mode::SharedClone => "shared_clone",
Mode::PrivateClone => "private_clone",
Mode::PrivateHoisted => "private_hoisted",
Mode::SharedHoisted => "shared_hoisted",
Mode::SharedCloneHoisted => "shared_clone_hoisted",
Mode::CloneOnlyShared => "clone_only_shared",
Mode::CloneOnlyPrivate => "clone_only_private",
}
}
}
type Slot = Arc<Mutex<Option<(String, Regex)>>>;
fn get_from_slot(slot: &Slot) -> Regex {
// Same procedure as PatternCache::get_or_compile on the hit path.
let guard = slot.lock().unwrap();
if let Some((p, re)) = guard.as_ref() {
if p == PATTERN {
return re.clone();
}
}
unreachable!("slot is pre-filled");
}
fn run(mode: Mode, threads: usize, invokes: usize, rows: &Arc<Vec<Option<String>>>) -> (f64, f64) {
let shared_src = Arc::new(Regex::new(PATTERN).unwrap());
let shared_slot: Slot = Arc::new(Mutex::new(Some((
PATTERN.to_string(),
Regex::new(PATTERN).unwrap(),
))));
let barrier = Arc::new(Barrier::new(threads + 1));
let sink = Arc::new(AtomicU64::new(0));
let mut handles = Vec::new();
for _ in 0..threads {
let rows = Arc::clone(rows);
let barrier = Arc::clone(&barrier);
let shared_src = Arc::clone(&shared_src);
let shared_slot = Arc::clone(&shared_slot);
let sink = Arc::clone(&sink);
handles.push(std::thread::spawn(move || {
// Per-thread sources, compiled on this thread.
let private_src = Regex::new(PATTERN).unwrap();
let private_slot: Slot = Arc::new(Mutex::new(Some((
PATTERN.to_string(),
Regex::new(PATTERN).unwrap(),
))));
let hoisted = Regex::new(PATTERN).unwrap();
// Warmup.
for _ in 0..4 {
black_box(scan(&private_src.clone(), &rows));
}
let shared_clone_hoisted = Regex::clone(shared_src.as_ref());
barrier.wait();
let mut acc = 0u64;
for _ in 0..invokes {
acc = acc.wrapping_add(match mode {
Mode::CompilePerInvoke => scan(&Regex::new(PATTERN).unwrap(), &rows),
Mode::SharedMutex => scan(&get_from_slot(&shared_slot), &rows),
Mode::PrivateMutex => scan(&get_from_slot(&private_slot), &rows),
// Explicit Regex::clone (an Arc<Regex>.clone() would only bump the Arc).
Mode::SharedClone => scan(&Regex::clone(shared_src.as_ref()), &rows),
Mode::PrivateClone => scan(&Regex::clone(&private_src), &rows),
Mode::PrivateHoisted => scan(&hoisted, &rows),
Mode::SharedHoisted => scan(&shared_src, &rows),
Mode::SharedCloneHoisted => scan(&shared_clone_hoisted, &rows),
Mode::CloneOnlyShared => {
let re = Regex::clone(shared_src.as_ref());
black_box(&re);
0
}
Mode::CloneOnlyPrivate => {
let re = Regex::clone(&private_src);
black_box(&re);
0
}
});
}
sink.fetch_add(acc, Ordering::Relaxed);
}));
}
barrier.wait();
let t0 = Instant::now();
for h in handles {
h.join().unwrap();
}
let wall = t0.elapsed().as_secs_f64();
black_box(sink.load(Ordering::Relaxed));
let total_invokes = threads * invokes;
let nonnull_rows = rows.iter().filter(|r| r.is_some()).count();
let mrows_s = (total_invokes * ROWS) as f64 / wall / 1e6;
let mean_us = wall * 1e6 / (total_invokes as f64 / threads as f64);
let _ = nonnull_rows;
(mrows_s, mean_us)
}
fn main() {
let invokes: usize = std::env::args()
.nth(1)
.map(|s| s.parse().unwrap())
.unwrap_or(512);
let rows = Arc::new(make_rows(ROWS, 0xE0000 + 8192));
println!("mode,threads,invokes_per_thread,mrows_per_s,mean_invoke_us");
for &mode in &[
Mode::CompilePerInvoke,
Mode::SharedMutex,
Mode::PrivateMutex,
Mode::SharedClone,
Mode::PrivateClone,
Mode::PrivateHoisted,
Mode::SharedHoisted,
Mode::SharedCloneHoisted,
Mode::CloneOnlyShared,
Mode::CloneOnlyPrivate,
] {
for &threads in &[1usize, 2, 4, 8] {
let (mrows, mean_us) = run(mode, threads, invokes, &rows);
println!(
"{},{},{},{:.2},{:.1}",
mode.name(),
threads,
invokes,
mrows,
mean_us
);
eprintln!(
"{} t={} -> {:.2} Mrows/s ({:.0} us/invoke)",
mode.name(),
threads,
mrows,
mean_us
);
}
}
}
mode threads invokes_per_thread mrows_per_s mean_invoke_us
compile_per_invoke 1 512 13.54 605.0
compile_per_invoke 2 512 28.45 575.8
compile_per_invoke 4 512 55.59 589.4
compile_per_invoke 8 512 68.54 956.1
shared_mutex 1 512 15.84 517.2
shared_mutex 2 512 14.68 1116.4
shared_mutex 4 512 12.97 2526.9
shared_mutex 8 512 5.78 11336.0
private_mutex 1 512 15.64 523.7
private_mutex 2 512 30.22 542.2
private_mutex 4 512 59.44 551.2
private_mutex 8 512 60.37 1085.6
shared_clone 1 512 15.75 520.2
shared_clone 2 512 14.25 1149.9
shared_clone 4 512 12.77 2566.7
shared_clone 8 512 7.17 9138.0
private_clone 1 512 15.40 531.9
private_clone 2 512 30.26 541.4
private_clone 4 512 59.69 549.0
private_clone 8 512 80.60 813.1
private_hoisted 1 512 15.58 525.6
private_hoisted 2 512 30.70 533.7
private_hoisted 4 512 61.05 536.7
private_hoisted 8 512 59.02 1110.4
shared_hoisted 1 512 14.68 558.2
shared_hoisted 2 512 9.44 1736.3
shared_hoisted 4 512 10.31 3177.8
shared_hoisted 8 512 6.76 9696.9
shared_clone_hoisted 1 512 16.04 510.6
shared_clone_hoisted 2 512 14.19 1155.0
shared_clone_hoisted 4 512 12.63 2594.3
shared_clone_hoisted 8 512 7.17 9145.7
clone_only_shared 1 512 80082.18 0.1
clone_only_shared 2 512 22935.36 0.7
clone_only_shared 4 512 40333.92 0.8
clone_only_shared 8 512 47600.61 1.4
clone_only_private 1 512 81905.60 0.1
clone_only_private 2 512 56711.59 0.3
clone_only_private 4 512 51182.66 0.6
clone_only_private 8 512 103496.30 0.6
label function workers iter wall_s rows invokes max_in_flight
base regexp_extract_all 1 0 0.1739 1048576 256 2
base regexp_extract_all 1 1 0.1721 1048576 256 2
base regexp_extract_all 1 2 0.1717 1048576 256 2
base regexp_extract_all 1 3 0.1724 1048576 256 2
base regexp_extract_all 1 4 0.1717 1048576 256 2
base regexp_extract_all 1 5 0.1715 1048576 256 2
label function workers iter wall_s rows invokes max_in_flight
base regexp_extract_all 8 0 0.1292 1048576 256 9
base regexp_extract_all 8 1 0.1288 1048576 256 9
base regexp_extract_all 8 2 0.1281 1048576 256 9
base regexp_extract_all 8 3 0.1268 1048576 256 9
base regexp_extract_all 8 4 0.1289 1048576 256 9
base regexp_extract_all 8 5 0.1309 1048576 256 9
label function workers iter wall_s rows invokes max_in_flight
base regexp_extract 1 0 0.1330 1048576 256 2
base regexp_extract 1 1 0.1327 1048576 256 2
base regexp_extract 1 2 0.1337 1048576 256 2
base regexp_extract 1 3 0.1370 1048576 256 2
base regexp_extract 1 4 0.1331 1048576 256 2
base regexp_extract 1 5 0.1330 1048576 256 2
label function workers iter wall_s rows invokes max_in_flight
base regexp_extract 8 0 0.1123 1048576 256 9
base regexp_extract 8 1 0.1081 1048576 256 9
base regexp_extract 8 2 0.1114 1048576 256 9
base regexp_extract 8 3 0.1122 1048576 256 9
base regexp_extract 8 4 0.1106 1048576 256 9
base regexp_extract 8 5 0.1114 1048576 256 9
label workers iter wall_s rows invokes max_in_flight
base 1 0 0.1736 1048576 256 2
base 1 1 0.1721 1048576 256 2
base 1 2 0.1720 1048576 256 2
base 1 3 0.1720 1048576 256 2
base 1 4 0.1713 1048576 256 2
base 1 5 0.1698 1048576 256 2
label workers iter wall_s rows invokes max_in_flight
base 8 0 0.1327 1048576 256 9
base 8 1 0.1276 1048576 256 9
base 8 2 0.1282 1048576 256 9
base 8 3 0.1306 1048576 256 9
base 8 4 0.1285 1048576 256 9
base 8 5 0.1309 1048576 256 9
base2 regexp_extract 1 0 0.1309 1048576 256 2
base2 regexp_extract 1 1 0.1303 1048576 256 2
base2 regexp_extract 1 2 0.1282 1048576 256 2
base2 regexp_extract 1 3 0.1300 1048576 256 2
head2 regexp_extract 1 0 0.1310 1048576 256 2
head2 regexp_extract 1 1 0.1293 1048576 256 2
head2 regexp_extract 1 2 0.1294 1048576 256 2
head2 regexp_extract 1 3 0.1307 1048576 256 2
base2 regexp_extract 1 0 0.1317 1048576 256 2
base2 regexp_extract 1 1 0.1308 1048576 256 2
base2 regexp_extract 1 2 0.1301 1048576 256 2
base2 regexp_extract 1 3 0.1304 1048576 256 2
head2 regexp_extract 1 0 0.1256 1048576 256 2
head2 regexp_extract 1 1 0.1253 1048576 256 2
head2 regexp_extract 1 2 0.1241 1048576 256 2
head2 regexp_extract 1 3 0.1264 1048576 256 2
base2 regexp_extract_all 8 0 0.1337 1048576 256 9
base2 regexp_extract_all 8 1 0.1302 1048576 256 9
base2 regexp_extract_all 8 2 0.1321 1048576 256 9
base2 regexp_extract_all 8 3 0.1304 1048576 256 9
head2 regexp_extract_all 8 0 0.3185 1048576 256 9
head2 regexp_extract_all 8 1 0.2806 1048576 256 9
head2 regexp_extract_all 8 2 0.3161 1048576 256 9
head2 regexp_extract_all 8 3 0.2787 1048576 256 9
base2 regexp_extract_all 8 0 0.1369 1048576 256 9
base2 regexp_extract_all 8 1 0.1310 1048576 256 9
base2 regexp_extract_all 8 2 0.1297 1048576 256 9
base2 regexp_extract_all 8 3 0.1305 1048576 256 9
head2 regexp_extract_all 8 0 0.2758 1048576 256 9
head2 regexp_extract_all 8 1 0.3057 1048576 256 9
head2 regexp_extract_all 8 2 0.2795 1048576 256 9
head2 regexp_extract_all 8 3 0.2793 1048576 256 9
base3 regexp_extract_all 1 0 0.1828 1048576 256 2
base3 regexp_extract_all 1 1 0.1714 1048576 256 2
base3 regexp_extract_all 1 2 0.1717 1048576 256 2
headfix regexp_extract_all 1 0 0.1719 1048576 256 2
headfix regexp_extract_all 1 1 0.1657 1048576 256 2
headfix regexp_extract_all 1 2 0.1662 1048576 256 2
base3 regexp_extract_all 1 0 0.1728 1048576 256 2
base3 regexp_extract_all 1 1 0.1708 1048576 256 2
base3 regexp_extract_all 1 2 0.1707 1048576 256 2
headfix regexp_extract_all 1 0 0.1656 1048576 256 2
headfix regexp_extract_all 1 1 0.1691 1048576 256 2
headfix regexp_extract_all 1 2 0.1658 1048576 256 2
base3 regexp_extract_all 8 0 0.1309 1048576 256 9
base3 regexp_extract_all 8 1 0.1304 1048576 256 9
base3 regexp_extract_all 8 2 0.1281 1048576 256 9
headfix regexp_extract_all 8 0 0.1213 1048576 256 9
headfix regexp_extract_all 8 1 0.1188 1048576 256 9
headfix regexp_extract_all 8 2 0.1139 1048576 256 9
base3 regexp_extract_all 8 0 0.1331 1048576 256 9
base3 regexp_extract_all 8 1 0.1280 1048576 256 9
base3 regexp_extract_all 8 2 0.1303 1048576 256 9
headfix regexp_extract_all 8 0 0.1256 1048576 256 9
headfix regexp_extract_all 8 1 0.1198 1048576 256 9
headfix regexp_extract_all 8 2 0.1187 1048576 256 9
label function workers iter wall_s rows invokes max_in_flight
head regexp_extract_all 1 0 0.2546 1048576 256 2
head regexp_extract_all 1 1 0.2505 1048576 256 2
head regexp_extract_all 1 2 0.2516 1048576 256 2
head regexp_extract_all 1 3 0.2512 1048576 256 2
head regexp_extract_all 1 4 0.2492 1048576 256 2
head regexp_extract_all 1 5 0.2497 1048576 256 2
label function workers iter wall_s rows invokes max_in_flight
head regexp_extract_all 8 0 0.2870 1048576 256 9
head regexp_extract_all 8 1 0.2874 1048576 256 9
head regexp_extract_all 8 2 0.2802 1048576 256 9
head regexp_extract_all 8 3 0.2876 1048576 256 9
head regexp_extract_all 8 4 0.3120 1048576 256 9
head regexp_extract_all 8 5 0.2753 1048576 256 9
label function workers iter wall_s rows invokes max_in_flight
head regexp_extract 1 0 0.1391 1048576 256 2
head regexp_extract 1 1 0.1533 1048576 256 2
head regexp_extract 1 2 0.1557 1048576 256 2
head regexp_extract 1 3 0.1513 1048576 256 2
head regexp_extract 1 4 0.1525 1048576 256 2
head regexp_extract 1 5 0.1499 1048576 256 2
label function workers iter wall_s rows invokes max_in_flight
head regexp_extract 8 0 0.1068 1048576 256 9
head regexp_extract 8 1 0.1042 1048576 256 9
head regexp_extract 8 2 0.1049 1048576 256 9
head regexp_extract 8 3 0.1046 1048576 256 9
head regexp_extract 8 4 0.1044 1048576 256 9
head regexp_extract 8 5 0.1037 1048576 256 9
label workers iter wall_s rows invokes max_in_flight
head 1 0 0.2414 1048576 256 2
head 1 1 0.2434 1048576 256 2
head 1 2 0.2395 1048576 256 2
head 1 3 0.2412 1048576 256 2
head 1 4 0.2403 1048576 256 2
head 1 5 0.2427 1048576 256 2
label workers iter wall_s rows invokes max_in_flight
head 8 0 0.2790 1048576 256 9
head 8 1 0.3046 1048576 256 9
head 8 2 0.2737 1048576 256 9
head 8 3 0.2672 1048576 256 9
head 8 4 0.2744 1048576 256 9
head 8 5 0.2671 1048576 256 9
// Task A: sunchao's sort-key sharing scenario for PR #5612.
//
// Comet passes sort-key expressions directly into DataFusion's SortExec
// (native/core/src/execution/planner.rs: create_sort_expr builds PhysicalSortExpr
// with an arbitrary physical expression; SortExec::new(LexOrdering::new(exprs)...)).
// In DataFusion 54.1.0, ExternalSorter::in_mem_sort_stream (sorts/sort.rs:586) takes
// the spawn_buffered branch (line 633) once the memory reservation reaches
// execution.sort_in_place_threshold_bytes (default 1 MiB): every buffered batch is
// sorted in its own tokio task on the multithread runtime, and all tasks share the
// SAME cloned LexOrdering -> the SAME ScalarUDF instance evaluated concurrently.
//
// This repro builds exactly that plan shape: a single-partition memory source of
// unsorted 8192-row string batches, SortExec (no fetch) whose sort key is the comet
// regexp_extract_all UDF wrapped in a tracking shim that counts concurrent in-flight
// invocations on ONE inner UDF instance. Built via create_comet_physical_fun, so the
// identical source compiles against BASE and HEAD.
//
// Usage: sort_repro <label> <workers> <iters>
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::time::Instant;
use arrow::array::builder::StringBuilder;
use arrow::array::RecordBatch;
use arrow::compute::SortOptions;
use arrow::datatypes::{DataType, Field, Schema};
use datafusion::common::{Result as DFResult, ScalarValue};
use datafusion::config::ConfigOptions;
use datafusion::datasource::memory::MemorySourceConfig;
use datafusion::execution::TaskContext;
use datafusion::logical_expr::{
ColumnarValue, ScalarFunctionArgs, ScalarUDF, ScalarUDFImpl, Signature, Volatility,
};
use datafusion::physical_expr::expressions::{col, lit};
use datafusion::physical_expr::{LexOrdering, PhysicalSortExpr, ScalarFunctionExpr};
use datafusion::physical_plan::sorts::sort::SortExec;
use datafusion::physical_plan::{collect, displayable, ExecutionPlan};
use datafusion::prelude::SessionContext;
use datafusion_comet_spark_expr::create_comet_physical_fun;
// ---------------- deterministic data (same generator as the main harness) ----------
struct Lcg(u64);
impl Lcg {
fn new(seed: u64) -> Self {
Lcg(seed.wrapping_mul(0x9E3779B97F4A7C15).wrapping_add(1))
}
fn next(&mut self) -> u64 {
self.0 = self
.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
self.0 >> 33
}
}
const LETTERS: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
fn rand_word(rng: &mut Lcg, min: usize, max: usize) -> String {
let len = min + (rng.next() as usize) % (max - min + 1);
(0..len)
.map(|_| LETTERS[(rng.next() as usize) % 26] as char)
.collect()
}
fn make_batch(schema: &Arc<Schema>, rows: usize, seed: u64) -> RecordBatch {
let mut rng = Lcg::new(seed);
let mut b = StringBuilder::new();
for _ in 0..rows {
let r = rng.next() % 10;
if r == 0 {
b.append_null();
} else if r <= 3 {
b.append_value(format!(
"{}_{}",
rand_word(&mut rng, 3, 8),
rand_word(&mut rng, 3, 8)
));
} else {
b.append_value(format!("{:03}-{:04}", rng.next() % 1000, rng.next() % 10000));
}
}
RecordBatch::try_new(Arc::clone(schema), vec![Arc::new(b.finish())]).unwrap()
}
// ---------------- tracking shim around ONE comet UDF instance ----------------------
#[derive(Default)]
struct Counters {
in_flight: AtomicU64,
max_in_flight: AtomicU64,
invokes: AtomicU64,
}
struct TrackingUdf {
inner: Arc<ScalarUDF>,
signature: Signature,
return_type: DataType,
counters: Arc<Counters>,
}
impl std::fmt::Debug for TrackingUdf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TrackingUdf").finish()
}
}
impl PartialEq for TrackingUdf {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
impl Eq for TrackingUdf {}
impl std::hash::Hash for TrackingUdf {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
"tracked_regexp_extract_all".hash(state);
}
}
impl ScalarUDFImpl for TrackingUdf {
fn name(&self) -> &str {
"tracked_regexp_extract_all"
}
fn signature(&self) -> &Signature {
&self.signature
}
fn return_type(&self, _: &[DataType]) -> DFResult<DataType> {
Ok(self.return_type.clone())
}
fn invoke_with_args(&self, args: ScalarFunctionArgs) -> DFResult<ColumnarValue> {
let cur = self.counters.in_flight.fetch_add(1, Ordering::SeqCst) + 1;
self.counters.max_in_flight.fetch_max(cur, Ordering::SeqCst);
self.counters.invokes.fetch_add(1, Ordering::SeqCst);
let r = self.inner.invoke_with_args(args);
self.counters.in_flight.fetch_sub(1, Ordering::SeqCst);
r
}
}
// ---------------- main --------------------------------------------------------------
const BATCH_ROWS: usize = 8192;
const NUM_BATCHES: usize = 128;
fn build_plan(
func: &str,
batches: &[RecordBatch],
schema: &Arc<Schema>,
ctx: &SessionContext,
) -> (Arc<dyn ExecutionPlan>, Arc<Counters>) {
let key_ty = if func == "regexp_extract" {
DataType::Utf8
} else {
DataType::List(Arc::new(Field::new("item", DataType::Utf8, true)))
};
// ONE comet UDF instance (one PatternCache in HEAD), shared by every evaluation.
let inner = create_comet_physical_fun(func, key_ty.clone(), &ctx.state(), None)
.expect("create_comet_physical_fun");
let counters = Arc::new(Counters::default());
let tracked = ScalarUDF::new_from_impl(TrackingUdf {
inner,
signature: Signature::variadic_any(Volatility::Immutable),
return_type: key_ty.clone(),
counters: Arc::clone(&counters),
});
let sort_key = Arc::new(ScalarFunctionExpr::new(
"tracked_regexp_extract_all",
Arc::new(tracked),
vec![
col("s", schema).unwrap(),
lit(ScalarValue::Utf8(Some(r"(\d+)-(\d+)".to_string()))),
lit(ScalarValue::Int32(Some(1))),
],
Arc::new(Field::new("key", key_ty, true)),
Arc::new(ConfigOptions::default()),
));
// Same construction as Comet's planner: PhysicalSortExpr with the expression as
// the sort key, SortExec with no fetch, single partition.
let source =
MemorySourceConfig::try_new_exec(&[batches.to_vec()], Arc::clone(schema), None).unwrap();
let sort_expr = PhysicalSortExpr {
expr: sort_key,
options: SortOptions::default(),
};
let sort: Arc<dyn ExecutionPlan> = Arc::new(SortExec::new(
LexOrdering::new(vec![sort_expr]).unwrap(),
source,
));
(sort, counters)
}
fn main() {
let mut args = std::env::args().skip(1);
let label = args.next().expect("usage: sort_repro <label> <function> <workers> <iters>");
let func = args.next().expect("function");
let workers: usize = args.next().expect("workers").parse().unwrap();
let iters: usize = args.next().expect("iters").parse().unwrap();
let schema = Arc::new(Schema::new(vec![Field::new("s", DataType::Utf8, true)]));
let batches: Vec<RecordBatch> = (0..NUM_BATCHES)
.map(|i| make_batch(&schema, BATCH_ROWS, 0xA0000 + 31 * i as u64))
.collect();
let input_bytes: usize = batches
.iter()
.map(|b| b.get_array_memory_size())
.sum();
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(workers)
.enable_all()
.build()
.unwrap();
let ctx = SessionContext::new();
// Print the physical plan once (workers=1 invocation is enough, but cheap to always do).
let (plan, _c) = build_plan(&func, &batches, &schema, &ctx);
eprintln!(
"input: {} batches x {} rows, {} bytes buffered (sort_in_place_threshold_bytes default = {}; spawn_buffered branch requires reservation >= threshold and >1 batch)",
NUM_BATCHES,
BATCH_ROWS,
input_bytes,
ConfigOptions::default().execution.sort_in_place_threshold_bytes
);
eprintln!("physical plan:\n{}", displayable(plan.as_ref()).indent(true));
println!("label,function,workers,iter,wall_s,rows,invokes,max_in_flight");
for iter in 0..iters {
let (plan, counters) = build_plan(&func, &batches, &schema, &ctx);
let task_ctx = Arc::new(TaskContext::default());
let t0 = Instant::now();
let out = rt
.block_on(async { collect(plan, task_ctx).await })
.expect("sort failed");
let wall = t0.elapsed().as_secs_f64();
let rows: usize = out.iter().map(|b| b.num_rows()).sum();
assert_eq!(rows, NUM_BATCHES * BATCH_ROWS);
println!(
"{},{},{},{},{:.4},{},{},{}",
label,
func,
workers,
iter,
wall,
rows,
counters.invokes.load(Ordering::SeqCst),
counters.max_in_flight.load(Ordering::SeqCst)
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment