# filter — benchmark

Workload: 10_000 rows; predicate `val > 50`. Tick rewrites one row's
`val` (potentially flipping its membership). Batch streams 1000 such
ticks. Generated by [comparisons/bench/operators/filter.bench.ts](../../comparisons/bench/operators/filter.bench.ts).

| Library | Setup (ms) | Single (ms) | vs data | Batch 1000 (ms) | vs data |
|---|---:|---:|---:|---:|---:|
| **data** | 3.33 | **0.008** | — | **2.52** | — |
| svelte-store | 0.938 | 0.290 | 36.3× | 300.02 | 119× |
| rxjs | 1.00 | 0.314 | 39.3× | 404.47 | 161× |
| react | 1.40 | 0.440 | 55.0× | 362.29 | 144× |
| preact-signals | 2.71 | 0.625 | 78.1× | 63.95 | 25.4× |
| solid | 7.11 | 2.96 | 370× | 217.29 | 86.2× |
| crossfilter | 10.04 | 3.23 | 404× | 557.29 | 221× |
| mobx | 108.92 | 3.88 | 485× | 337.53 | 134× |
| vue-reactivity | 17.05 | 13.37 | 1671× | 882.92 | 350× |



data is fastest on both single (39x over svelte-store) and batch (17x
over preact-signals).

## How

`filter` is a `RowOperator` — touching one row routes its BU2 only to the
row's slot in the filter's sparse output. Peers that wrap the array as a
single signal walk all N rows on every emit; data's per-row reactive
machinery means each tick is O(1) regardless of N.

Run `BENCH_OPS=filter npm run bench:ops` to refresh — update this file
when the numbers change materially.
