Attributes · Performance Benchmarks · Practice Question
At What Point Does the Unique Attribute Start Providing a Significant Benefit?
Question
For a column with high cardinality (many distinct values), at approximately what data size does the unique attribute start providing a meaningful performance benefit over a linear scan?
Solution
/ Benchmark: single-value lookup at different table sizes
/ (from KX columnar database white paper)
/ Table: keyed on sym, filtering for one value
/ 100K rows: ~0ms both with and without `u#
/ 1M rows: ~0ms with `u#, ~2ms without
/ 10M rows: ~0ms with `u#, ~23ms without
/ 100M rows: ~0ms with `u#, ~233ms without
/ At ~1 million rows, the linear scan becomes measurably slower
/ At ~100 million rows, the gap is 230ms vs 0ms
/ Practical verification — neg[n]?`8 generates n distinct random symbols:
t: ([] sym:neg[10000000]?`8; val:10000000?100.0) / 10M rows, unique syms, no attribute
s: t[`sym] 5000000 / a symbol that is in the table
\ts select from t where sym=s / measurably slow
t: update `u#sym from t
\ts select from t where sym=s / almost instant with `u#Explanation
The unique attribute provides hash-based lookup, whose cost does not grow with the table. Without it, where sym=x compares every row with the target. For small tables, O(n) is fast enough that the hash table overhead of u# provides no advantage. As the table grows, the linear scan time grows linearly while the hash lookup stays constant.
The crossover point is approximately 1 million rows.
Below 1M rows, a linear scan typically completes in under a millisecond — fast enough that u# provides no perceptible improvement. Above 1M rows, the gap opens. At 100M rows, KX's white paper measured 233ms vs 0ms — a penalty that compounds if the query runs repeatedly or concurrently.
This rule of thumb helps with practical decisions:
- Small reference tables (instruments, currencies, exchanges) with fewer than 1M rows:
u#is beneficial but not urgent - Growing tables (instrument universe expanding over time): apply
u#before the table reaches 1M rows - High-frequency lookup columns in large tables: apply
u#immediately
Note on workspace: the unique attribute also dramatically reduces workspace for lookups. In the white paper, a single-value select on a 100M-row table used 134MB of workspace without u#, and about 1KB with it.
Keyed tables and direct indexing:
For keyed tables (keyed on sym), direct indexing is even faster than a select statement, regardless of whether u# is applied:
kt: `sym xkey ([] sym:`AAPL`GOOG`MSFT; price:150 2800 310)
kt[`AAPL] / direct key lookup — faster than select from kt where sym=`AAPLFor very large keyed tables, applying u# to the key column provides the same O(1) benefit as for regular columns.