inst/benchmarks/ENGINE-ROADMAP.md

basetable C++ engine roadmap

State of the native engine as of 1.0.0, and the work left to do. Public R function names stay stable; R keeps base-style argument handling and NSE capture, the C++ kernels do the computation.

Implemented

Next engine tracks

Ordered by measured impact (see make-readme-figures.R, 1e6 rows).

1. Sort pipeline -- the one clear loss

orderrows() is ~2-3x of data.table on a string key. The radix byte passes are threaded but memory-bandwidth bound, and an MSD attempt (August 2026) was slower because the copy-back doubled memory traffic. What would close it: a cache-blocked parallel forward radix that partitions once by the high byte, then sorts partitions independently in cache, so each pass is local and the threads do not contend on one shared output buffer. This is a full rewrite of radix_pairs, not an incremental change.

2. Equi-join -- consistent win

merge() now probes in parallel (per-thread match buffers, concatenated in thread order) and gathers the atomic output columns on worker threads, which took it from ~1.7x of data.table to about parity (~37 ms vs ~38 ms at 1e6 rows, inner and left). Full/right outer still uses the serial path because it needs shared y_matched bookkeeping.

For a consistent edge rather than parity: a single-key fast path that skips the KeyBuf machinery -- map the one key column to an int (int/factor directly, string by interned CHARSXP address, double by real_slot), build one int -> y-row-list table, probe in parallel. match_mask_int_single is the template; it needs extending to string/double keys and to emit match lists rather than a mask.

3. Parallelise the remaining grouping kernels

group_single dictionary pass, the dense integer-key aggregate path, bt_group_id_, and bt_count_ are still serial. count and sd already beat data.table here; parallelising widens the margin and helps the mid-cardinality range where the fused reducer does not engage.

4. Expression kernel for transform()

Only subset() predicates compile today. transform() needs integer-result preservation (int op int stays int, not promoted to double) and a grouped variant so transform(by =) stops looping groups in R.

5. Interval-overlap joins

overlapmerge() and two-y-column non-equi conditions still linear-scan the bucket after the equi-key match. Add an interval-tree or sweepline path for the case where two conditions bound a [start, end] range.



Try the basetable package in your browser

Any scripts or data that you put into this service are public.

basetable documentation built on Sept. 18, 2026, 9:13 a.m.