# aggregate — benchmark (sum)

Workload: 10_000 rows; `sum('val')`. Tick rewrites one row's `val`.
Batch streams 1000 such ticks. Generated by
[comparisons/bench/operators/aggregate.bench.ts](../../comparisons/bench/operators/aggregate.bench.ts).

| Library | Setup (ms) | Single (ms) | vs data | Batch 1000 (ms) | vs data |
|---|---:|---:|---:|---:|---:|
| **data** | 6.00 | **0.059** | — | **2.66** | — |
| svelte-store | 0.954 | 0.260 | 4.4× | 206.49 | 77.6× |
| rxjs | 1.14 | 0.280 | 4.7× | 295.34 | 111× |
| react | 1.82 | 0.393 | 6.7× | 256.59 | 96.5× |
| preact-signals | 1.76 | 0.477 | 8.1× | 31.48 | 11.8× |
| crossfilter | 2.30 | 0.833 | 14.1× | 112.56 | 42.3× |
| solid | 2.51 | 1.93 | 32.7× | 152.63 | 57.4× |
| mobx | 127.74 | 8.56 | 145× | 631.41 | 237× |
| vue-reactivity | 23.34 | 23.87 | 405× | 1924.02 | 723× |



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

## How

`sum` maintains a running total — each BU2 swaps `(old, new)` on the
accumulator in O(1). Peers re-walk N rows per emit; crossfilter's
`groupAll.reduceSum` is incremental but pays a higher constant.
`avg` is the same shape (running sum + count). `max`/`min` recompute
O(N) on publish, which would narrow the gap; that's a follow-up if it
matters.

Run `BENCH_OPS=aggregate npm run bench:ops` to refresh.
