Summary
in compares every element of its left side against every element of its
right side. There is no hash set, and no specialisation for a single-element
right side, so a membership test against a column is O(n·m) and is beaten by
hand-written equality predicates by two to three orders of magnitude.
Measurement
A 351,393-element symbol column (766 distinct values), right side varied:
| right side |
(in col right) |
| 1 symbol |
6,500 us |
| 10 symbols |
11,950 us |
| 100 symbols |
76,800 us |
| 766 symbols |
298,200 us |
For comparison, on the same column, (== col 'SYM) — the same question for
one element — is about 16 us. in is ~400x slower than the equality it could
lower to, before any set is involved.
Why it matters to us
We maintain an order book table (levels, ~351k rows, keyed conceptually by
(venue, instrument)) and frequently need "the rows belonging to these few
books". The three shapes available today:
(left-join [venue instrument] levels keys) — correct, but sized by
levels: ~13 ms whether the right side names one book or ten.
(in instrument syms) — sized by n·m as above, and also wrong on its own
for a compound key: (and (in venue vs) (in instrument is)) matches
cross-venue pairs that are in neither input.
- OR-ed per-pair predicates,
(or (and (== v ...) (== i ...)) ...) — what we
ended up writing. 50 us for one book, 600 us for ten, linear in the number
of books, and it crosses the join's flat cost at ~216 books.
The third is fastest for small key sets and is what we shipped, but it is a
loop building a boolean expression by hand, and it is linear where a hash set
would be flat.
What we are asking for
- Hash the right side of
in when it is a vector above some small
length, so membership is O(n + m) rather than O(n·m). The machinery
already exists — hash_row_keys is what the join builds.
- Specialise a one-element right side to the equality scan.
(in col [x]) should not cost 400x (== col x).
Optionally, and more useful still for our case:
- A compound membership — a way to ask "is this row's
(a, b) in this
two-column table" without materialising a composite key column and without
a full join. This is the primitive our workload actually wants; in per
column cannot express it correctly.
Prior art
q's in hashes its right side, and ?/in over a compound key is expressed
with a keyed table lookup that probes a hash rather than scanning.
Measured on rayforce 2.6.2 (the build carrying bc350ddc, the while form).
Summary
incompares every element of its left side against every element of itsright side. There is no hash set, and no specialisation for a single-element
right side, so a membership test against a column is O(n·m) and is beaten by
hand-written equality predicates by two to three orders of magnitude.
Measurement
A 351,393-element symbol column (766 distinct values), right side varied:
(in col right)For comparison, on the same column,
(== col 'SYM)— the same question forone element — is about 16 us.
inis ~400x slower than the equality it couldlower to, before any set is involved.
Why it matters to us
We maintain an order book table (
levels, ~351k rows, keyed conceptually by(venue, instrument)) and frequently need "the rows belonging to these fewbooks". The three shapes available today:
(left-join [venue instrument] levels keys)— correct, but sized bylevels: ~13 ms whether the right side names one book or ten.(in instrument syms)— sized by n·m as above, and also wrong on its ownfor a compound key:
(and (in venue vs) (in instrument is))matchescross-venue pairs that are in neither input.
(or (and (== v ...) (== i ...)) ...)— what weended up writing. 50 us for one book, 600 us for ten, linear in the number
of books, and it crosses the join's flat cost at ~216 books.
The third is fastest for small key sets and is what we shipped, but it is a
loop building a boolean expression by hand, and it is linear where a hash set
would be flat.
What we are asking for
inwhen it is a vector above some smalllength, so membership is O(n + m) rather than O(n·m). The machinery
already exists —
hash_row_keysis what the join builds.(in col [x])should not cost 400x(== col x).Optionally, and more useful still for our case:
(a, b)in thistwo-column table" without materialising a composite key column and without
a full join. This is the primitive our workload actually wants;
inpercolumn cannot express it correctly.
Prior art
q's
inhashes its right side, and?/inover a compound key is expressedwith a keyed table lookup that probes a hash rather than scanning.
Measured on
rayforce2.6.2 (the build carryingbc350ddc, thewhileform).