Commit 2f6b0b7a by PLN (Algolia)

docs(perf): 2026-07-18 Pulsar lag investigation + TidalCycles editor landscape research

Records of the editor-lag deep-dive: local root-cause analysis (renderer
main-thread saturation, console DOM growth, three marker leaks, .line
transition churn, Electron 12 ceiling, thermal state) and the companion
web-research report on the 2026 TidalCycles editor ecosystem (uzu/Codeberg
migration, upstream issue #229, vim/VSCode/browser alternatives, no prior
art for Pulsar forks). Fixes landed in the pulsar-tidalcycles fork, branch
perf/event-highlighter, commit 6e9f374.
parent 1bb7c4fa
# Pulsar editor lag — investigation log (2026-07-18)
**Symptoms** (reported during rehearsal): multi-second typing/cursor latency in Pulsar ("typing
blind"), a GHC error renders a big red stack over several seconds, evals stay slow afterwards —
while the taskbar CPU indicator shows the machine "barely used" and window-manager shortcuts
stay instant. Fans maxed and 73–82°C despite recently cleaned vents.
## Verdict
The machine is healthy. The lag is **Pulsar's renderer main thread saturating one core**
(~65–75% sustained through the whole session), which reads as ~4% on a 16-thread CPU meter.
Every keystroke, cursor move and eval queues behind that single busy thread; KWin shortcuts
bypass it entirely, which is why the desktop stayed fast.
## Evidence
- Renderer process (PID 14530): **37:51 CPU over a 58:05 session** (65%), still ~74%/core over
the following 4 minutes of active use; ~1–5% when truly idle. All on the **main thread**.
- System-wide: no memory pressure (22 Gi available, swap 0B), no IO/CPU PSI pressure.
- GPU process holds `/dev/dri` fds → **hardware acceleration active** (not SwiftShader).
- Pulsar 1.118.0 = **Electron 12.2.3 / Chrome 89 (April 2021)**, running as an X11 client via
**Xwayland** on Plasma 6 (Xwayland itself at ~18% CPU relaying Pulsar's pixels).
- Thermals: package 82°C, both fans pegged at 3300 RPM, non-zero `thermal_throttle` counters,
load avg 2.87 — real sustained load (Pulsar renderer + Ardour ~20% + Xwayland ~18%), not a
cooling defect. Mixed power state: `perf.sh --cool` leftovers (max_perf_pct=85, powersave
governor, EPP balance_performance, Pulsar main+GPU at nice 10 / renderer at 0) while
power-profiles-daemon reports "performance".
## Root causes (in `~/Work/Tools/pulsar-tidalcycles`, branch `perf/event-highlighter`)
1. **`lib/console-view.js` — the "big red stack" stall.** Every log line appends a `<pre>` to a
DOM that is never pruned, then forces a synchronous full reflow via
`scrollTop = scrollHeight`. `showErrorNotifications` (default **on**) additionally renders
the *entire* GHC stack through `atom.notifications.addError()`. Reflow cost grows with the
session-long console DOM → evals after an error stay slow.
2. **`lib/editors.js` `decorateCodeBlocks()` — progressive typing lag.** Runs on every
line-count change (every Enter / deleted line), destroys line-number decorations (including
other packages') but **leaks the markers** (`invalidate: 'never'`, never destroyed). Atom
updates its marker index on every keystroke → cost grows all session.
3. **`lib/event-highlighter.js` `#createPositionMarkers()` — secondary marker leak.** Re-evaling
a line overwrites the `rowMarkers` map entry without destroying the previous marker.
Live stats after a 63-min session: **2170 live markers** (and leaked-overwritten ones on top).
4. **`styles/tidal.less`**`backdrop-filter: blur(10px)` on the sound-browser waveform and
search panels: one of the most paint-expensive CSS properties on Chrome 89; costs whenever
the panel is visible.
5. Config/state mismatch: `eventHighlighting: {}` on disk (= default `enable: false`) while the
running instance has an active highlighter; BootTidal.hs streams `/editor/highlights` OSC to
port 6013 regardless of the editor-side toggle.
## Highlighter profile (user-run `tidalcycles:highlight-stats`, 63-min window)
```
[Highlighter profile] 3799.5s window | RAF 4/s, work 3/s (idle 0%) | frame avg 0.502ms / max 89.195ms |
DOM writes 17/s | markers 2170 live, creates/destroys 1/s, toggles 34/s | active 0 | errors 0
[Highlighter profile] jank: 7 frames >16ms, 2 >50ms | worst frame 89.195ms did 16 adds / 1 removes
```
Read: the perf branch works — the highlighter is now cheap on average (0.5 ms/frame, idle-aware
RAF at 3–4/s). The remaining concerns are the 2170-marker accumulation and rare 50–90 ms spike
frames. The session-long ~65% burn comes from the console/marker/styles causes above, not from
the highlighter loop itself.
## Remediation plan
1. Config (immediate): `tidalcycles.showErrorNotifications: false`;
`autocomplete-plus.autoActivationDelay` ≥ 300 ms (documented community fix for typing lag).
2. Patch fork: cap console DOM (max messages), truncate long messages, lighter error
notifications (first line + short detail); fix `decorateCodeBlocks` marker leak + debounce;
fix `#createPositionMarkers` overwrite leak; optionally drop `backdrop-filter`.
3. Verify with `tidalcycles:highlight-stats` + renderer CPU sampling before/after.
4. Re-audition `editor-background` only after the renderer is no longer saturated.
5. Editor endgame: don't fork Pulsar (Electron 12 ceiling, no prior art); if Pulsar still
disappoints, thin-plugin path (tidal.nvim / VSCode extension) carries the identity cheaper.
Upstream issue #229 (Aug 2025) plans a web-worker offload for highlighting.
See companion: `2026-07-18-tidalcycles-2026-landscape-research.md`.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment