Performance
All numbers on this page come from real Criterion
benchmarks running on ubuntu-22.04 in CI, updated automatically on every push to main.
The key claim: rs-grid's cost scales with what you show, not with what you store.
Frame pipeline — O(visible cells)
The complete per-frame cost — ScrollBy → viewport recalc → SceneBuilder::build() — measured
across wildly different dataset sizes. The renderer sees only the ~275 cells visible in the
viewport at any moment, regardless of how many rows exist in the datasource.
All configs render in 65–89 µs — less than 0.6% of the 16.6 ms frame budget at 60fps. Row count has zero impact on frame time.
Hit-test — O(log n_cols)
Every click, hover, and drag starts with a hit-test: converting a viewport coordinate to a
(row, col) address. Column resolution uses precomputed offsets and a binary search — O(log n_cols).
Row resolution is O(1) thanks to uniform row height. Total row count has zero effect.
Varying row count (1 000 cols fixed)
| Configuration | Hit-test time |
|---|---|
| 1 000 rows, 1 000 cols | 25.6 ns |
| 1 billion rows, 1 000 cols | 25.5 ns |
| 1 quadrillion rows, 1 000 cols | 25.6 ns |
Varying column count (O(log n) in action)
| Columns | Hit-test time |
|---|---|
| 10 cols | 10.3 ns |
| 100 cols | 13.9 ns |
| 1 000 cols | 18.0 ns |
The 1.7× increase from 10 → 1 000 columns (10 → 18 ns) reflects the binary search over column offsets. Going from 1 000 rows to 1 quadrillion rows costs nothing.
Initialization — O(n_cols)
GridState::new precomputes column offsets and flex widths. With FnDataSource (virtual rows),
row count is just a u64 stored in a closure — it carries no allocation cost.
Varying row count — FnDataSource (20 cols fixed)
| Rows | Init time |
|---|---|
| 1 000 | 5.0 µs |
| 100 000 | 4.9 µs |
| 1 000 000 | 4.9 µs |
| 100 000 000 | 5.0 µs |
| 1 000 000 000 | 5.2 µs |
| 1 000 000 000 000 000 | 5.0 µs |
Flat regardless of row count — O(n_cols), not O(n_rows).
Varying column count (1M rows fixed)
Initializing a grid with 1 quadrillion virtual rows takes the same ~5 µs as a grid with
1 000 rows. If you need all data in memory (VecDataSource), initialization is still dominated
by column setup, not row count.
Sort — 100 000 rows
Sort uses a two-phase algorithm: numeric columns use an 8-pass LSD radix sort (O(8n)); string
columns fall back to sort_unstable_by. A key cache avoids re-extracting values when toggling
sort direction on the same column.
Radix sort, first call — key extraction + sort
Radix sort, direction toggle — keys reused from cache
Lexicographic comparison sort
Client-side sort is limited to 1 000 000 rows (MAX_CLIENT_SORT_ROWS). Beyond that,
rs-grid emits a SortWarning and delegates to your backend.
Memory per row
The datasource determines per-row memory cost. With FnDataSource, rows have no allocation —
data is generated on demand. With VecDataSource, each RowRecord allocates a HashMap.
| Data source | Description | Memory / row |
|---|---|---|
| FnDataSource | Virtual / server-side — data generated on demand | 0 bytes |
| VecDataSource (empty rows) | In-memory, no cell values | 56 B |
| VecDataSource (10 cols, ~8 chars) | In-memory, realistic cell data | 1.1 KB |
For large datasets (> 100k rows), prefer FnDataSource with server-side pagination.
See FnDataSource and PageCache for implementation details.
Measured with Criterion (sample-size=100) on ubuntu-22.04 · commit seed · June 4, 2026. Updated automatically on every push to main via CI.

