# Benchmark summary

PG 19devel, aarch64-darwin, debug build (-O0, cassert).
All runs: max_parallel_workers_per_gather = 0.

## Tom's reproducer (50K groups × 4 elems, 200K rows)

| Test | Unpatched | Patched | Change |
|------|:---------:|:-------:|:------:|
| custom array_agg, 200MB (run 1) | 4,563 ms | 155 ms | 29x faster |
| custom array_agg, 200MB (run 2) | 5,025 ms | 152 ms | 33x faster |
| custom array_agg, 200MB (run 3) | 6,451 ms | 152 ms | 42x faster |
| custom array_agg, 4MB (run 1) | 388 ms | 283 ms | 1.4x faster |
| built-in array_agg, 200MB | 102 ms | 103 ms | unchanged |

Plan structure, batch count, and memory usage: identical.

## Synthetic workloads (5M total elements)

| Workload | work_mem | Unpatched | Patched | Change |
|----------|:--------:|:---------:|:-------:|:------:|
| 100K grp × 50 elem | 256MB | 35.0 s | 2.9 s | 12x |
| 100K grp × 50 elem | 4MB | 6.1 s | 5.9 s | — |
| 10K grp × 500 elem | 256MB | 2.7 s | 2.5 s | — |
| 10K grp × 500 elem | 4MB | 4.5 s | 4.5 s | — |
| 1K grp × 5K elem | 256MB | 2.4 s | 2.5 s | — |
| 500K grp × 2 elem | 256MB | 672 s | 1.7 s | 395x |
| 500K grp × 2 elem | 4MB | 5.4 s | 1.8 s | 3x |

## Non-array aggregates (unchanged)

| Aggregate | Unpatched | Patched |
|-----------|:---------:|:-------:|
| count(*) 100K groups | 1.9 s | 1.9 s |
| sum() 100K groups | 1.9 s | 1.9 s |
| string_agg 10K groups | 0.6 s | 0.6 s |
| built-in array_agg 256MB | 2.9 s | 2.9 s |

## Spill Behavior (work_mem = 4MB, 100K groups)

| Metric | Unpatched | Patched |
|--------|:---------:|:-------:|
| Batches | 61 | 61 |
| Memory Usage | 8,547 kB | 8,753 kB |
| Disk Usage | 143,872 kB | 143,440 kB |
