Skip to content

perf: eliminate typing lag — async JS GC + rAF-coalesced autoResize #81

Description

@jdmanring

Problem

Two distinct performance problems make typing feel laggy with occasional 1-second freezes:

1-second lockups: Memory.forciblyPurgeJavaScriptMemory via CDP calls V8::LowMemoryNotification() synchronously in the renderer, blocking the JS event loop for 100 ms–1 s+ depending on heap size. This fires:

  • From the PSI monitor every 5 s when avg10 > 5 % — no cooldown, so it can fire repeatedly during a long typing session
  • From changeEvent(WindowDeactivate) on every focus shift (notifications, alt-tab, clicking into a dialog) on KDE/Wayland

General jankiness: autoResize (ui.js) reads getComputedStyle, textarea.offsetWidth, and clone.scrollHeight on every keystroke — 2 forced layout reflows per character. At 8 chars/sec this is 16 forced layout reflows/sec in QtWebEngine, which is slower than a desktop browser.

Fix

static/js/ui.js — autoResize: Replace clone-based synchronous measurement with requestAnimationFrame-coalesced height: 'auto' + scrollHeight. N keystrokes per 16 ms frame → 1 layout reflow (vs. 2 per keystroke before). Drop clone entirely.

qt_wrapper.py — async GC overhaul:

  • Remove _cdp_purge_memory (the synchronous stop-the-world variant)
  • Replace all three trigger sites with gc({ type: 'major', execution: 'async' }) via page.runJavaScript() — already enabled by --expose-gc; non-blocking, incremental
  • Add 500 ms debounce on focus-loss (skips transient focus shifts)
  • Add 30 s cooldown on PSI purge (prevents repeated GC bursts under sustained pressure)
  • Node-threshold trigger calls runJavaScript directly (already on Qt main thread)
  • PSI monitor (background thread) uses a 250 ms drain timer on the main thread — correct cross-thread dispatch without needing QTimer.singleShot from a non-Qt thread

Files

  • static/js/ui.jsautoResize rewrite
  • qt_wrapper.py — remove _cdp_purge_memory, add _request_async_gc, update PSI/focus-loss/node-threshold

Tests

  • New tests/test_ui_auto_resize_js.py (5 tests)
  • Updated tests/test_qt_cdp_listener_audit.py (−7 old, +9 new = 43 total)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions