Files
buzz/crates/buzz-acp/src
Sanket Kanjalkar a314975ce7 perf(acp): reduce observer CPU and lock overhead (#9381)
## Summary

The Agent Client Protocol (ACP) observer records agent activity for the
owner's viewer. Sizing currently allocates JSON strings and rescans
whole frames for each trimmed field.

Count JSON bytes directly, reuse fitted sizes, and count changed fields
when cheaper. Skip raw-message copies for disabled observers and copy
replay payloads outside the mutex. Keep sequence numbers, timestamps,
replay insertion, and publication under one lock to preserve replay/live
ordering.

This adds one `Arc` allocation per replay entry, and snapshots pin
entries until copying completes. Replay retains the existing 1,000-event
limit.

### Related issue

Related to #7987. This PR targets `main` independently of #9366, which
owns the byte-budget fix. The changes overlap in observer plumbing, so
the second PR to land will need a rebase.

### Testing

The new ignored probe exercises the production publish queue. Timing
includes template clones and queue ingestion, before encryption. Five
alternating fresh release processes per arm/case compared `75d38b6c2`
with this commit on a busy host. Every pair retained identical queue
bytes and event counts.

```sh
cargo test --release -p buzz-acp --lib observer::perf_tests::publish_queue_probe -- --exact --ignored --nocapture
```

Set `BUZZ_OBSERVER_PROBE_EVENTS`, `BUZZ_OBSERVER_PROBE_BYTES`, and
`BUZZ_OBSERVER_PROBE_FIELDS` for each row. Medians:

| Events / string bytes per event / fields | Before | After |
| --- | ---: | ---: |
| 200 / 1 MiB / 64 | 5605 ms | 228 ms |
| 1,000 / 1 MiB / 1 | 714 ms | 352 ms |
| 10,000 / 1 KiB / 1 | 13.01 ms | 6.90 ms |

A separate 20-trial integration run used the same production Chromium
app build (`block/buzz-app@128cd4c2`) and four real ACP subprocesses.
Three peers emitted 225 synthetic messages over five seconds, each with
1 MiB across 64 strings. A fourth emitted 50 markers, 100 ms apart. Each
arm/mode used five fresh processes and browser contexts, with
alternating arm order.

The real observer, publisher, encryption/signing, browser broker, and
Activity UI ran through a modeled relay transport. Marker timing used
one controller clock, from release through transcript DOM commit plus
two animation frames. Observer timestamps were not the timing origin.
Observation-enabled medians:

| Metric | Before | After |
| --- | ---: | ---: |
| Rust CPU time | 4.49 s | 0.45 s |
| Marker-to-render p95 | 4471 ms | 1018 ms |
| Sampled peak RSS | 342.3 MiB | 296.9 MiB |

All 500 enabled-mode markers rendered exactly once; all 2,250 large
events were delivered with the existing payload elision.
Observation-disabled CPU stayed roughly flat (0.13 s to 0.12 s). The
controlled publication-order regression also fails original `main` and
passes this commit.

These are stress-workload measurements, not a general app speedup.
Native hosting and deployed-relay performance remain unmeasured. The
publish cadence remains one frame per second. No CI timing threshold was
added.

Generated with Codex

Signed-off-by: Sanket Kanjalkar <sanketk@squareup.com>
Co-authored-by: Codex <codex@openai.com>
2026-11-12 14:37:33 -08:00
..